代码之家  ›  专栏  ›  技术社区  ›  Anna

在python中,合并两个数据帧,其中一个数据帧的合并键包含在另一数据帧的键中

  •  2
  • Anna  · 技术社区  · 3 年前

    我想合并两个数据帧df1和df2,以便比较两个值info1和info2。合并它们的键隐藏在名称列中。Df1是“clean”,因为它有一个名字列和一个姓氏列。然而,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”那样在st上合并?或者我正在尝试解析df2.Name,我发现nameparser导入HumanName,但我认为它不能处理“and”和“&”。

    如果有什么不清楚的地方,我深表歉意。非常感谢您提前提供的帮助!

    4 回复  |  直到 3 年前
        1
  •  4
  •   mozway    3 年前

    可以使用双子字符串 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
    
        2
  •  2
  •   ak_slick    3 年前

    我认为您需要制作一个能够匹配名称的列。那它会很好用的。

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

    此外,您的示例数据中有一个拼写错误,但我在下面修复了它。(泰斯曼就是泰斯曼)

    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
    
        3
  •  2
  •   Timeless    3 年前

    另一种可能的解决方案:

    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
    
        4
  •  2
  •   Michael Cao    3 年前

    首先,我们替换所有的“&”使用“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"]
    )
    
        5
  •  2
  •   PaulS    3 年前

    另一种可能的解决方案,基于替换的想法 and & 按每行的姓氏 df2 :

    (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