使用多个条件将一列df中的字符串值检查到另一列df

2024-06-16 11:20:08 发布

您现在位置:Python中文网/ 问答频道 /正文

我有两个数据帧:

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列中的最后一个字符串相匹配

如果存在匹配项,那么我想将AddressAdd 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


Tags: 数据nameadddfnewaddressmatchnan
2条回答

您可以利用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,“Owner”),它将“Owner_match_ratio”列添加到second_df中
  • 最后,根据所需的最小匹配率(例如70%)过滤第二个df,如下所示:new_df = second_df.loc[second_df["Owner_match_ratio"] > 0.7, :]

受此启发,您可以采用类似的解决方案

TL;博士

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

相关问题 更多 >