Python 如何根据最接近的匹配有效地替换大数据帧(100k+;行)中的值?

Python 如何根据最接近的匹配有效地替换大数据帧(100k+;行)中的值?,python,python-3.x,pandas,Python,Python 3.x,Pandas,因此,我使用levenshire distance查找最接近的匹配项,并替换大型数据帧中的许多值,作为基础: import operator def levenshteinDistance(s1, s2): if len(s1) > len(s2): s1, s2 = s2, s1 distances = range(len(s1) + 1) for i2, c2 in enumerate(s2): distances_ = [i

因此,我使用levenshire distance查找最接近的匹配项,并替换大型数据帧中的许多值,作为基础:

import operator

def levenshteinDistance(s1, s2):
    if len(s1) > len(s2):
        s1, s2 = s2, s1

    distances = range(len(s1) + 1)
    for i2, c2 in enumerate(s2):
        distances_ = [i2+1]
        for i1, c1 in enumerate(s1):
            if c1 == c2:
                distances_.append(distances[i1])
            else:
                distances_.append(1 + min((distances[i1], distances[i1 + 1], distances_[-1])))
        distances = distances_
    return distances[-1]

def closest_match(string, matchings):
    scores = {}
    for m in matchings:
        scores[m] = 1 - levenshteinDistance(string,m)
    
    return max(scores.items(), key=operator.itemgetter(1))[0]
因此,当从另一个类似大小的数据帧(100k+行)中替换一个中等大小的数据帧中的许多值时,如下所示将永远运行:(从最后半小时运行ha!)

那么有没有更有效的方法?为了同样的目的,我添加了if-else条件,这样,如果存在直接匹配,就不会涉及任何也会产生相同结果的计算

样本数据
结果

   products
0, pizza
1, ketchup
2, salami
3, anchovy
4, pepperoni
5, marinara
6, olive
7, sausage
8, cheese
9, bbq sauce
10, stuffed crust
results2

   products
0, salaaaami
1, kechap
2, lives
3, ppprn
4, pizzas
5, marinara
6, sauce de bbq
7, marinara sauce
8, chease
9, sausages
10, crust should be stuffed
我希望
results2
中的值被
results
中最接近的匹配项替换为使用编译过的Python

使用Cython/CPython

使用PyPy-aka-Stackless-Python

使用Numba执行以下两种功能:

from numba import jit
@jit
def levenshteinDistance(s1, s2):
...
因此,我采取了一些措施来提高速度,并实现了近4800x的加速。 在此处发布,以帮助处理pandas上CPU密集型任务性能缓慢的任何人:

  • 我没有像问题中那样一次全部替换,而是制作了一个替换字典,在每个数据帧中使用唯一的值进行替换,这使得它从永远(我在2小时后停止)变为2分钟,因为有许多重复的值。这是60倍的加速:

    replacements = {string: closest_match(string, results2.products.unique())
                  if string not in results2.products.unique() else string 
                    for string in results.products.unique()}
    results.replace({'products':replacements}, inplace = True)
    
  • 我使用了一个基于c的实现,利用:library计算levenshtein距离。在研究中,我发现许多这样的任务都有基于C语言的实现,比如矩阵乘法和搜索算法等。此外,您始终可以用C编写模块,并在python中使用它。
    editdistance.eval(“香蕉”、“巴哈马”)
    与我定义的函数
    levenshteinDistance(“香蕉”、“巴哈马”)
    相比,每个循环只需
    1.71µs±289 ns(平均±标准偏差7次,每个循环100000次)
    的时间
    34.4µs±4.2µs(平均±标准偏差7次,每个循环10000次)
    这是20倍的加速

  • 然后我通过并行性一次使用了所有的内核。为此,我经历了多种选择,例如多处理和线程,但没有一种比的速度快。它的最小更改(只需一行import
    modin.pands as pd
    代替
    import pandas as pd
    )即可优雅地工作。它使以前的跑步速度提高了4倍左右


  • 因此,总共有4800x的加速,这是巨大的,整个过程在眨眼之间就开始了。

    你能粘贴一些样本数据和预期的输出吗?@VivekKalyanarangan补充道!psyco从2012年就死了。即使如此,它也只适用于32位python,显然在2020年,我将运行64位版本:@jit支持(parallel=True,no_python=True)实现,以加快执行速度,但这两个标志对于我使用的数据类型都不可用。没有这些标志,我让它运行,但警告显示它会退回到我正在使用的字符串/其他数据类型,并且没有明显的速度优势。即使这样,我还是让它运行了,但两个小时后它什么也没得到。它还在运行,我停了下来。很好的答案,肯定是书上的标记
    replacements = {string: closest_match(string, results2.products.unique())
                  if string not in results2.products.unique() else string 
                    for string in results.products.unique()}
    results.replace({'products':replacements}, inplace = True)