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

时间:2021-03-30 11:15:18

标签: python pandas dataframe

我有两个数据框:

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

2 个答案:

答案 0 :(得分: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, :]

答案 1 :(得分:1)

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

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
相关问题