我有以下pandas数据框,包含50,000个唯一行和20列(包含相关列的片段):
DF1 :
PRODUCT_ID PRODUCT_DESCRIPTION
0 165985858958 "Fish Burger with Lettuce"
1 185965653252 "Chicken Salad with Dressing"
2 165958565556 "Pork and Honey Rissoles"
3 655262522233 "Cheese, Ham and Tomato Sandwich"
4 857485966653 "Coleslaw with Yoghurt Dressing"
5 524156285551 "Lemon and Raspberry Cheesecake"
我还有以下数据框(我也以字典形式保存),它有2列和20,000个唯一行:
df2(也保存为dict_2)
PROD_ID PROD_DESCRIPTION
0 548576 "Fish Burger"
1 156956 "Chckn Salad w/Ranch Dressing"
2 257848 "Rissoles - Lamb & Rosemary"
3 298770 "Lemn C-cake"
4 651452 "Potato Salad with Bacon"
5 100256 "Cheese Cake - Lemon Raspberry Coulis"
我想要做的是比较" PRODUCT_DESCRIPTION" df1中的字段到" PROD_DESCRIPTION"在df2中的字段并找到最接近的匹配/匹配以帮助重型提升部件。然后我需要手动检查匹配但是会更快更好理想的结果看起来像这样,例如注意到一个或多个部分匹配:
PRODUCT_ID PRODUCT_DESCRIPTION PROD_ID PROD_DESCRIPTION
0 165985858958 "Fish Burger with Lettuce" 548576 "Fish Burger"
1 185965653252 "Chicken Salad with Dressing" 156956 "Chckn Salad w/Ranch Dressing"
2 165958565556 "Pork and Honey Rissoles" 257848 "Rissoles - Lamb & Rosemary"
3 655262522233 "Cheese, Ham and Tomato Sandwich" NaN NaN
4 857485966653 "Coleslaw with Yoghurt Dressing" NaN NaN
5 524156285551 "Lemon and Raspberry Cheesecake" 298770 "Lemn C-cake"
6 524156285551 "Lemon and Raspberry Cheesecake" 100256 "Cheese Cake - Lemon Raspberry Coulis"
我已经完成了确定完全匹配的联接。保留索引并不重要,因为每个df中的产品ID都是唯一的。结果也可以保存到新的数据框中,然后将其应用于具有大约1400万行的第三个数据框。
我使用了以下问题和答案(其中包括):
Is it possible to do fuzzy match merge with python pandas
Fuzzy merge match with duplicates包括在其中一个答案中建议的尝试水母模块
Python fuzzy matching fuzzywuzzy keep only the best match
Fuzzy match items in a column of an array
以及各种循环/功能/映射等,但没有成功,要么得到第一个"模糊匹配"分数较低或未检测到匹配。
我喜欢根据here生成匹配/距离分数列的想法,因为它可以让我加快手动检查过程。
我使用的是Python 2.7,pandas并安装了fuzzywuzzy。
答案 0 :(得分:5)
使用fuzz.ratio
作为我的距离指标,像这样计算我的距离矩阵
df3 = pd.DataFrame(index=df.index, columns=df2.index)
for i in df3.index:
for j in df3.columns:
vi = df.get_value(i, 'PRODUCT_DESCRIPTION')
vj = df2.get_value(j, 'PROD_DESCRIPTION')
df3.set_value(
i, j, fuzz.ratio(vi, vj))
print(df3)
0 1 2 3 4 5
0 63 15 24 23 34 27
1 26 84 19 21 52 32
2 18 31 33 12 35 34
3 10 31 35 10 41 42
4 29 52 32 10 42 12
5 15 28 21 49 8 55
设置可接受距离的阈值。我设置了50
找到每行具有最大值的索引值(对于df2
)。
threshold = df3.max(1) > 50
idxmax = df3.idxmax(1)
进行作业
df['PROD_ID'] = np.where(threshold, df2.loc[idxmax, 'PROD_ID'].values, np.nan)
df['PROD_DESCRIPTION'] = np.where(threshold, df2.loc[idxmax, 'PROD_DESCRIPTION'].values, np.nan)
df
答案 1 :(得分:1)
我没有足够的声誉来评论 @piRSquared 的回答。因此有了这个答案。
AttributeError: 'DataFrame' object has no attribute 'get_value'
)。当我插入“下划线”时它起作用了。例如。 vi = df._get_value(i, 'PRODUCT_DESCRIPTION')
set_value
”存在类似问题,同样的解决方案也适用。例如。 df3._set_value(i, j, fuzz.ratio(vi, vj))
idxmax
会导致另一个错误 (TypeError: reduction operation 'argmax' not allowed for this dtype
),这是因为 df3(模糊比率)的内容属于“对象”类型。我在定义 threshold
之前将它们全部转换为数字并且它起作用了。例如。 df3 = df3.apply(pd.to_numeric)
一百万感谢 @piRSquared 提供的解决方案。对于像我这样的 Python 新手来说,它很有魅力。我发布此答案是为了方便像我这样的其他新手。
答案 2 :(得分:0)
您应该能够遍历两个数据帧并使用所需信息填充第3个数据帧的字典:
d = {
'df1_id': [],
'df1_prod_desc': [],
'df2_id': [],
'df2_prod_desc': [],
'fuzzywuzzy_sim': []
}
for _, df1_row in df1.iterrows():
for _, df2_row in df2.iterrows():
d['df1_id'] = df1_row['PRODUCT_ID']
...
df3 = pd.DataFrame.from_dict(d)