在Python中,使用一个数据框的合并键包含在另一个数据框的键中,来合并两个数据框。

4

我想合并两个数据框df1和df2,以便比较两个值info 1和info 2。将它们合并的关键在于名称列中隐藏着。Df1是“干净的”,因为它有一个名字列和一个姓氏列。然而,df2很棘手。只有一个名称列,名称可以以不同的方式给出。标准情况是名字和姓氏,但如下图所示,它可能包含由“and”或“&”分隔的两个名称,甚至可能完全不同,比如学校。

enter image description here

以下是代码中的虚拟数据:

data1 = [['Anna','Tessmann',10], ['Ben','Fachmann',20], ['John','Smith',10]]
df1 = pd.DataFrame(data1, columns=['FirstName','LastName','Info1'])


data2 = [['Ben Fachmann',30], ['School AAA',40], ['John and Melissa Smith',50], ['Bob & Anna Tessmann',20]]
df2= pd.DataFrame(data2, columns=['Name','Info2'])

有人知道一种有效的方法来合并这两个吗?是否有可能在类似于“df2.Name包含df1.Lastname”的情况下进行合并?或者我正在尝试解析df2.Name,我发现可以导入HumanName但我认为它无法处理“and”和“&”。

如果有什么不清楚的地方,请谅解。非常感谢您提前的任何帮助!


我认为我理解了,但您想要的代码是如果df2.name有两个名称,则值/ 2,否则值,然后将其附加到db1 - ajgrinds
为什么df_analysisInfo2中有“Anna Tessmann”的10?另外,在data2中您有一个拼写错误,“Testmann”应该是“Tessmann”。您能否再次核对一下? - Timeless
1
我认为这是一个打字错误,鉴于图片,我已经修复了它。 - mozway
5个回答

4

你可以使用双字符串 merge

import re

pattern1 = '|'.join(map(re.escape, df1['FirstName']))
pattern2 = '|'.join(map(re.escape, df1['LastName']))

match1 = df2['Name'].str.extractall(f'(?P<FirstName>{pattern1})').droplevel(1)
match2 = df2['Name'].str.extractall(f'(?P<LastName>{pattern2})').droplevel(1)

out = df1.merge(df2.join(match1).join(match2),
                on=['FirstName', 'LastName'])

输出:

  FirstName  LastName  Info1                    Name  Info2
0      Anna  Tessmann     10     Bob & Anna Tessmann     20
1       Ben  Fachmann     20            Ben Fachmann     30
2      John     Smith     10  John and Melissa Smith     50

1
非常感谢!它在我的实际数据集上也非常有效! - Anna

2

我认为你需要创建一个可以匹配名称的列,然后它就能正常工作了。

这里有一些可行的方法。根据数据中名称的唯一性,它可能并不总是有效。

另外,在你的示例数据中有一个拼写错误,但我已经在下面进行了修正。(tessmann应该是testmann)

import pandas as pd

data1 = [['Anna','Tessmann',10], ['Ben','Fachmann',20], ['John','Smith',10]]
df1 = pd.DataFrame(data1, columns=['FirstName','LastName','Info1'])


data2 = [['Ben Fachmann',30], ['School AAA',40], ['John and Melissa Smith',50], ['Bob & Anna Tessmann',20]]
df2= pd.DataFrame(data2, columns=['Name','Info2'])

# make a column to identify which indices in df1 match to df2
df2['merge_index'] = None
for _ind, _row in enumerate(df1.to_dict(orient='records')):
    df2.loc[df2.Name.str.contains(_row['FirstName']) & df2.Name.str.contains(_row['LastName']), 'merge_index'] = _ind

# merge df1 index to df2.merge_index column and select columns to keep
merged = pd.merge(left=df1, right=df2, how='left', left_index=True, right_on='merge_index')[['FirstName', 'LastName', 'Info1', 'Info2']]

输出: 已合并

      FirstName  LastName  Info1  Info2
3      Anna      Tessmann     10     20
0       Ben      Fachmann     20     30
2      John      Smith        10     50

2

另一个可能的解决方案:

L1 = df1[["FirstName", "LastName"]].agg(set, axis=1).tolist()
L2 = list(zip([s.split() for s in df2["Name"]], df2["Info2"]))
​
df_analysis = (
  df1.assign(Infos2=[next((v for (lst, v) in L2 if s.issubset(lst)), None) for s in L1])
)

输出:

print(df_analysis)

  FirstName  LastName  Info1  Infos2
0      Anna  Tessmann     10      20
1       Ben  Fachmann     20      30
2      John     Smith     10      50

2
首先,为了保持一致性,我们将所有的“&”替换为“and”,然后沿着“and”进行分割,并将其扩展为具有相同索引的多行。
然后,我们将其拆分并扩展为列。仅有名字的人可以从他们配偶的姓氏中获取回填。我们将这个扩展名称表与 df2 合并,只需在索引上匹配名称和 info2。
然后,与 df1 进行简单的合并即可。
names = df2["Name"].str.replace("&", "and")
names = names.str.split("and").explode()
names = names.str.strip()

names = (
    names.str.split(" ", expand=True)
    .rename(columns={0: "FirstName", 1: "LastName"})
    .fillna(method="bfill")
)

df1.merge(
    names.merge(df2, left_index=True, right_index=True), on=["FirstName", "LastName"]
)

2

另一个可能的解决方案是基于以下思路:将df2每行中的最后一个名称替换为and&

(df1.assign(Name = df1['FirstName'] + ' ' + df1['LastName'])
 .merge(df2.assign(Name = df2['Name'].str.replace(
     r'and\s|&\s', lambda x: x.string.split()[-1] + ',', regex=True)
                   .str.split(','))
 .explode('Name'), on='Name', how='left')
 .drop('Name', axis=1))

输出:

  FirstName  LastName  Info1  Info2
0      Anna  Tessmann     10     20
1       Ben  Fachmann     20     30
2      John     Smith     10     50

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接