如何减少比较来自两个不同数据帧的两个句子的函数的处理时间?

How to reduce the processing time in a function that compares two sentences from two different dataframes?

我正在使用一个函数来比较两个数据帧的句子并提取具有最高相似度的值和句子:

df1的每个句子与df2的400个句子进行比较,函数return是一个包含最高值的句子和值的元组。如果值 returned 小于 96,它 return 是一个包含两个 None 值的元组。

我尝试了不同的算法来比较句子(spacynltk),但我发现使用 fuzzywuzzy.process-instance 及其处理时间方面的最佳结果process.extractOne(text1, text2)-方法和dask,通过划分df1(分成4个分区)。在 extractOne() 中,我使用选项 score_cutoff = 96,仅 return 值 >= 96,想法是,一旦找到值 >= 96,该函数就会执行不必遍历整个 df2,但它似乎不能那样工作。

我也试过在函数内部划分 df2,但是处理时间并不比在 df2 中使用列表理解迭代更好(这在代码中有注释)。

这是我的代码:

def similitud( text1 ):
    a = process.extractOne(   text1,
                            [ df2['TITULO_PROYECTO'][i]
                                                 for i in range( len( df2 ) )
                              ],
                              score_cutoff = 96
                            )
    """
    a = process.extractOne(   text1,
                              ddf2.map_partitions(   lambda df2:
                                                            df2.apply( lambda row:
                                                                             #row['TITULO_PROYECTO'],
                                                                       axis = 1
                                                                       ),
                                                     meta = 'str'
                                                   ).compute( sheduler = 'processses' ),
                              score_cutoff #= 96
                            )
    """

    return ( None, None ) if a == None else a
    
tupla_values = ddf1.map_partitions( lambda df1:
                                           df1.progress_apply( ( lambda row:
                                                             similitud( row['TITULO_PROYECTO'] )
                                                                 ),
                                                                 axis = 1
                                                               ),
                                    meta = 'str'
                                    ).compute( scheduler = 'processes' )

如何找到减少处理时间的解决方案?

您可以消除函数内的 for 循环并修改您的函数以接受两个参数,并在 apply 函数中处理参数。 也许:

tupla_values = ddf1.map_partitions(lambda df1: df1.progress_apply((lambda row: similitud(row['TITULO_PROYECTO'],row['TITULO_PROYECTO'])), axis = 1), meta = 'str').compute(scheduler = 'processes')

这是通用的解决方案,您的代码可能需要进一步考虑。

FuzzyWuzzy 并不关心 score_cutoff 参数。它会简单地遍历所有元素,然后搜索得分最高的元素并 return 它以防它高于您的 score_cutoff 阈值。

你应该尝试使用 RapidFuzz(我是作者) 来做同样的事情,它提供了一个非常相似的界面。作为区别,process.extractOne()-方法 return 是一个包含 choicescore 的元组,在 FuzzyWuzzy 中,
而它 return 是一个包含choicescore 和 RapidFuzz 中的 index

RapidFuzz 使用 Levenshtein 距离的一些快速近似值,保证 return 分数 >= 归一化 Levenshtein 距离的实际分数。当分数保证低于 score_cutoff 时,这些快速近似用于提前退出而不执行昂贵的 Levenshtein 计算。然而,它不可能在 process.extractOne() 中找到得分高于 score_cutoff 的第一个元素后退出 - 处理,因为它搜索极端,寻找 [= 以上的最佳匹配12=] 阈值(但它 将提前退出 以防它找到得分为 100 的第一个匹配项)。

此外,您不需要在 similitud() 中创建所有选项的列表,因为 FuzzyWuzzy 和 RapidFuzz 也都接受 DataSeries。所以你可以使用类似的东西:

from rapidfuzz import process

def similitud( text1 ):
    a = process.extractOne( text1,
                            df2['TITULO_PROYECTO'],
                            score_cutoff = 96
                            )
    return ( None, None ) if a is None else ( a[0], a[1] )

process.extractOne() 将始终通过将字符串小写化、用空格替换标点符号等非字母数字字符并从字符串的开头和结尾删除空格来预处理您的字符串。

您可以预先对 df2 中的数据进行预处理,这样您就不必对 df1 中比较的每个元素都执行此操作。之后你只能在每次迭代中预处理 df1 的元素:

from rapidfuzz import process, utils

def similitud( text1 ):
    a = process.extractOne( utils.default_process( text1 ),
                            df2['PROCESSED_TITULO_PROYECTO'],
                            processor    = None,
                            score_cutoff = 96
                            )
    return ( None, None ) if a is None else ( a[0], a[1] )

我最近创建了一个基准测试来比较 FuzzyWuzzy-模块(与 Python-Levenshtein)和 RapidFuzz-模块(可以找到 here).


基准:

reproducible-science数据的来源:

  • titledata.csv 来自 FuzzyWuzzy(包含 2764 个标题的数据集,最大选择长度:2500)

以上图形基准测试的硬件 运行(规格):

  • CPU: i7-8550U的单核
  • 内存:8GB
  • OS:软呢帽 32