我有两个数据帧:
import pandas as pd
first_df = pd.DataFrame({'Full Name': ['Mulligan Nick & Mary', 'Tsang S C', 'Hattie J A C '],
'Address': ['270 Claude Road', '13 Sunnyridge Place', '18A Empire Road']})
second_df = pd.DataFrame({'Owner' : ['David James Mulligan', 'Brenda Joy Mulligan ', 'Helen Kwok Hattie'],
'Add Match': ['19 Dexter Avenue', 'Claude Road ', 'Building NO 512']})
是否只将Full Name
列中的第一个字符串与Owner
列中的最后一个字符串匹配。
如果有匹配,我想将Address
与Add match
进行比较,看看是否有类似的值。如果第一个条件通过,但第二个条件失败,则不会将其添加到新的数据帧中。
使用左联接会导致:
new_df = first_df.merge(second_df, how='left', left_on = ['Full Name', 'Address'], right_on = ['Owner', 'Add Match'])
print(new_df.head())
Full Name Address Owner Add Match
0 Mulligan Nick & Mary 270 Claude Road NaN NaN
1 Tsang S C 13 Sunnyridge Place NaN NaN
2 Hattie J A C 18A Empire Road NaN NaN
然而,想要的输出看起来更像这样:
new_df
Name Address
---- --------
Brenda Joy Mulligan Claude Road
您可以利用Python标准库中的difflib
模块来查找不同列之间的相似性。例如,您可以定义以下函数:
from difflib import SequenceMatcher
def compare_df(left, right, col: str):
left[f"{col}_match_ratio"] = 0
for value in left[col]:
best_ratio = 0
for other in right[col]:
result = SequenceMatcher(None, str(value), str(other)).ratio()
if result > best_ratio:
best_ratio = result
left.loc[left[col] == value, f"{col}_match_ratio"] = round(best_ratio, 2)
然后:
- 您只需要确保要比较的列在两个dfs中具有相同的名称
- 您调用df_compare(first_df,second_df,"所有者"(,它将添加"所有者";所有者匹配比率"列到second_df
- 最后,根据所需的最小匹配率(例如70%(过滤第二个df,如下所示:
new_df = second_df.loc[second_df["Owner_match_ratio"] > 0.7, :]
受此答案的启发,您可以使用类似的解决方案。
TL;DR
first_df[['last_name', 'start_name']] = first_df['Full Name'].str.split(' ', 1, expand=True)
second_df['last_name'] = second_df['Owner'].str.split(' ').str[-1]
df_final = first_df.merge(second_df, how='inner', left_on=['last_name'], right_on=['last_name'])
address_matches = df_final.apply(lambda x: True if difflib.get_close_matches(x['Address'], [x['Add Match']], n=1, cutoff=0.8) else False, axis=1)
df_final = df_final[address_matches].drop(columns=['last_name', 'start_name', 'Full Name', 'Address']).rename(columns={'Owner':'Name', 'Add Match': 'Address'})
循序渐进
最初,提取所需的姓氏关键字。
first_df[['last_name', 'start_name']] = first_df['Full Name'].str.split(' ', 1, expand=True)
second_df['last_name'] = second_df['Owner'].str.split(' ').str[-1]
PS:根据您的指示,我们使用pandas/numpy组合中的内置字符串方法。但如果它更适合你,你也可以对地址部分应用下面显示的相似性方法(例如,difflib.get_close_matches
(。
接下来,执行这些数据帧的内部联接,以匹配last_name
密钥。
df_temp = first_df.merge(second_df, how='inner', left_on=['last_name'], right_on=['last_name'])
然后应用具有所需相似性的difflib.get_close_matches
(我使用cutoff=0.8
,因为在该值以上没有返回值(方法来标记哪些行包含匹配项,然后只获得所需的行。
matches_mask = df_final.apply(lambda x: True if difflib.get_close_matches(x['Address'], [x['Add Match']], n=1, cutoff=0.8) else False, axis=1)
df_final = df_final[matches_mask].drop(columns=['last_name', 'start_name'])
Full Name Address Owner Add Match
Mulligan Nick & Mary 270 Claude Road Brenda Joy Mulligan Claude Road
最后,为了与问题末尾发布的结果的格式相匹配,您可以删除或重命名一些列。
df_final.drop(columns=['Full Name', 'Address']).rename(columns={'Owner':'Name', 'Add Match': 'Address'})
Owner Add Match
Brenda Joy Mulligan Claude Road