【问题标题】:Matching 2 large csv files by Fuzzy string matching in Python通过 Python 中的模糊字符串匹配匹配 2 个大型 csv 文件
【发布时间】:2016-07-19 16:56:26
【问题描述】:

我正在尝试将大约 600,000 个个人姓名(全名)与另一个拥有超过 8700 万个观察结果的数据库(全名)进行匹配!

我第一次尝试 fuzzywuzzy 库太慢了,所以我决定使用速度更快的模块 fuzzyset。假设我有一台功能强大的计算机可以将所有数据集加载到内存中,我将使用包含 964 个观测值的测试文件与 50,000 个观测值进行匹配来执行以下操作:

import time
from cfuzzyset import cFuzzySet as FuzzySet

df1=pd.read_csv(file1,delimiter='|') # test file with 964 observations
df2=pd.read_csv(file2,delimiter='|') # test file with 50,000 observations to be matched against

a=FuzzySet() # allocate the FuzzySet object
for row in file2['name']:
   a.add(str(row)) # Fill the FuzzySet object with all names from file2

start_time = time.time() # Start recording the time

dicto={'index':[],'name':[]} # Dictionary where I store the output

for names in file1['f_ofulln']:
    dicto['index'].append(a.get(names)[0][0])
    dicto['name'].append(a.get(names)[0][1])

print("--- %s seconds ---" % (time.time() - start_time))   

>>> --- 39.68284249305725 seconds ---

使用更小的数据集(964 个观测值与 50,000 个观测值匹配),时间为 39 秒

但是,如果我想在整个数据集上执行此方法,这太慢了。

有人知道如何提高运行时间吗?我认为 Cython 是不可能的,因为我已经在导入 Cython 版本的 fuzzyset 模块

非常感谢,

阿德里安

【问题讨论】:

  • 这个问题比看起来要困难得多。在执行匹配之前,您可能必须先阻止(或聚类)87M 数据。我不建议查找 600k 名称与数据库中每个名称之间的所有距离。 Python 库dedupe 有一些阻塞技术的实现。但是,我不确定它是否会扩展到您拥有的数据集。另一种可能性是在执行模糊匹配之前在两个集合中删除重复名称。 (对不起我含糊的回答......)
  • 感谢titpat,我一定会尽可能减少要合并的数据集的大小。我还修改了这个问题,因为我发现fuzzyset 模块要快得多。最后,我找到了将整个数据帧存储在内存中的资源。但即便如此,最终运行时间仍以周计算!

标签: python performance string-matching fuzzywuzzy


【解决方案1】:

所以我要回答我自己的问题,因为我找到了一种非常快的方法。

我使用 panda.HDFStore 和 panda.to_hdf 方法以 HDF5 格式保存了两个数据库。 我将姓氏的每个第一个字母保存到一个数据框中。 然后,我创建了一个基于 python-Levenshtein 模块查找最接近匹配的函数(非常快,因为它是用 C 编程的)。

最后,我一次发送了 26 个批处理作业,每个姓氏的字母一个。这意味着我只匹配姓氏首字母相同的人。

请注意,我还编写了函数来查找与出生年份最接近的匹配,相差不超过 1 年。

编辑:由于有人要求,我在下面提供了我的功能摘要。合并两个dataframe的main函数太长了,不好意思贴在这里。

# Needed imports:
from Levenshtein import *
import pandas as pd

# Function that get the closest match of a word in a big list:

def get_closest_match(x, list_strings,fun):
    # fun: the matching method : ratio, wrinkler, ... (cf python-Levenshtein module)
    best_match = None
    highest_ratio = 0
    for current_string in list_strings.values.tolist():
        if highest_ratio!=1:
            current_score = fun(x, current_string)
            if(current_score > highest_ratio):
                highest_ratio = current_score
                best_match = current_string
    return (best_match,highest_ratio)

# the function that matches 2 dataframes (only the idea behind, since too long to write everything
dicto={'Index':[],'score':[], ...} 
def LevRatioMerge(df1,df2,fun,colname,required=[],approx=[],eps=[]):
    # Basically loop over df1 with:
    for name in df1.itertuples():
        result=get_closest_match(name[YourColnumber],df2[YourColname],fun)
        dicto['score'].append(result[1])
        dicto['Index'].append(name[0])
        ...

这就是想法。希望它对你的工作有足够的启发。

【讨论】:

  • 很久以前,但我有点建立在相同的逻辑上。我可以知道你是如何编写你的 python-Levenshtein 函数的吗?我正在使用 FuzzyWuzzy 的流程,但对于我的需求来说似乎太慢了。
  • 我编辑了我的答案,不幸的是我的函数很长,而且真的是为我的问题量身定制的。希望这个小总结足以让你用python-Levenshtein模块解决自己的问题。
  • 嗨。我正在尝试为地址匹配实现相同的逻辑,将“city_name”保留为我的阻塞或索引键。是否可以看到您编写的功能?这对我会有帮助。
猜你喜欢
  • 2017-08-08
  • 1970-01-01
  • 2015-02-07
  • 2012-02-14
  • 2014-11-02
  • 2020-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多