Python 模糊匹配 (FuzzyWuzzy) - 只保留最佳匹配
Python Fuzzy Matching (FuzzyWuzzy) - Keep only Best Match
我正在尝试模糊匹配两个 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 列中的重复名称。
它应该相当简单,但我似乎无法弄清楚。
任何帮助将不胜感激。
fuzzywuzzy 的 process.extract()
returns 列表倒序排列,最匹配的排在最前面。
所以要找到最好的匹配,你可以将限制参数设置为 1
,这样它只会 returns 最好的匹配,如果大于 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)
使用 FuzzyWuzzy 的 process.extractOne()
可以大大简化您的几段代码。它不仅 return 最匹配,您还可以在函数调用中为其设置分数阈值,而不需要执行单独的逻辑步骤,例如:
process.extractOne(row, data, score_cutoff = 60)
如果找到满足条件的匹配,此函数将 return 最高匹配加上伴随分数的元组。否则它将 return None
。
我只是为自己写了同样的东西但是在 pandas.....
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', ' ']
希望这对您有所帮助。
我正在尝试模糊匹配两个 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 列中的重复名称。 它应该相当简单,但我似乎无法弄清楚。 任何帮助将不胜感激。
fuzzywuzzy 的 process.extract()
returns 列表倒序排列,最匹配的排在最前面。
所以要找到最好的匹配,你可以将限制参数设置为 1
,这样它只会 returns 最好的匹配,如果大于 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)
使用 FuzzyWuzzy 的 process.extractOne()
可以大大简化您的几段代码。它不仅 return 最匹配,您还可以在函数调用中为其设置分数阈值,而不需要执行单独的逻辑步骤,例如:
process.extractOne(row, data, score_cutoff = 60)
如果找到满足条件的匹配,此函数将 return 最高匹配加上伴随分数的元组。否则它将 return None
。
我只是为自己写了同样的东西但是在 pandas.....
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', ' ']
希望这对您有所帮助。