10

我正在尝试模糊匹配两个 csv 文件,每个文件都包含一列名称,它们相似但不相同。

到目前为止,我的代码如下:

import pandas as pd
from pandas import DataFrame
from fuzzywuzzy import process
import csv

save_file = open('fuzzy_match_results.csv', 'w')
writer = csv.writer(save_file, lineterminator = '\n')

def parse_csv(path):

with open(path,'r') as f:
    reader = csv.reader(f, delimiter=',')
    for row in reader:
        yield row


if __name__ == "__main__":
## Create lookup dictionary by parsing the products csv
data = {}
for row in parse_csv('names_1.csv'):
    data[row[0]] = row[0]

## For each row in the lookup compute the partial ratio
for row in parse_csv("names_2.csv"):
    #print(process.extract(row,data, limit = 100))
    for found, score, matchrow in process.extract(row, data, limit=100):
        if score >= 60:
            print('%d%% partial match: "%s" with "%s" ' % (score, row, found))
            Digi_Results = [row, score, found]
            writer.writerow(Digi_Results)


save_file.close()

输出如下:

Name11 , 90 , Name25 
Name11 , 85 , Name24 
Name11 , 65 , Name29

该脚本工作正常。输出符合预期。但我正在寻找的只是最好的匹配。

Name11 , 90 , Name25
Name12 , 95 , Name21
Name13 , 98 , Name22

因此,我需要根据第 2 列中的最大值以某种方式删除第 1 列中的重复名称。这应该相当简单,但我似乎无法弄清楚。任何帮助,将不胜感激。

4

3 回答 3

10

Fuzzywuzzyprocess.extract()以反向排序的顺序返回列表,最好的匹配首先出现。

所以要找到最佳匹配,您可以将 limit 参数设置为1,以便它只返回最佳匹配,如果大于 60 ,您可以将其写入 csv ,就像您现在所做的那样。

例子 -

from fuzzywuzzy import process
## For each row in the lookup compute the partial ratio
for row in parse_csv("names_2.csv"):

    for found, score, matchrow in process.extract(row, data, limit=1):
        if score >= 60:
            print('%d%% partial match: "%s" with "%s" ' % (score, row, found))
            Digi_Results = [row, score, found]
            writer.writerow(Digi_Results)
于 2015-08-17T17:19:40.293 回答
5

process.extractOne()使用FuzzyWuzzy可以大大简化您的几段代码。它不仅返回最高匹配项,您还可以在函数调用中为其设置分数阈值,而无需执行单独的逻辑步骤,例如:

process.extractOne(row, data, score_cutoff = 60)

如果找到满足条件的匹配,此函数将返回最高匹配的元组加上随附的分数。否则它将返回None

于 2016-10-03T03:35:22.927 回答
5

我只是为自己写了同样的东西,但在熊猫中......

import pandas as pd
import numpy as np
from fuzzywuzzy import fuzz
from fuzzywuzzy import process

d1={1:'Tim','2':'Ted',3:'Sally',4:'Dick',5:'Ethel'}
d2={1:'Tam','2':'Tid',3:'Sally',4:'Dicky',5:'Aardvark'}

df1=pd.DataFrame.from_dict(d1,orient='index')
df2=pd.DataFrame.from_dict(d2,orient='index')

df1.columns=['Name']
df2.columns=['Name']

def match(Col1,Col2):
    overall=[]
    for n in Col1:
        result=[(fuzz.partial_ratio(n, n2),n2) 
                for n2 in Col2 if fuzz.partial_ratio(n, n2)>50
               ]
        if len(result):
            result.sort()    
            print('result {}'.format(result))
            print("Best M={}".format(result[-1][1]))
            overall.append(result[-1][1])
        else:
            overall.append(" ")
    return overall

print(match(df1.Name,df2.Name))

我在此使用了 50 的阈值 - 但它是可配置的。

Dataframe1 看起来像

    Name
1   Tim
2   Ted
3   Sally
4   Dick
5   Ethel

Dataframe2 看起来像

Name
1   Tam
2   Tid
3   Sally
4   Dicky
5   Aardvark

所以运行它会产生匹配

['Tid', 'Tid', 'Sally', 'Dicky', ' ']

希望这可以帮助。

于 2017-09-03T05:55:27.217 回答