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

合并2列上的熊猫数据帧,但顺序不限

  •  8
  • sacuL  · 技术社区  · 6 年前

    问题是:

    我有两个数据帧的情况:

    test1 = pd.DataFrame({'id_A':['Ben', 'Julie', 'Jack', 'Jack'],
                      'id_B':['Julie', 'Ben', 'Nina', 'Julie']})
    
    test2 = pd.DataFrame({'id_a':['Ben', 'Ben', 'Ben', 'Julie', 'Julie', 'Nina'],
                          'id_b':['Julie', 'Nina', 'Jack', 'Nina', 'Jack', 'Jack'],
                          'value':[1,1,0,0,1,0]})
    
    >>> test1
        id_A   id_B
    0    Ben  Julie
    1  Julie    Ben
    2   Jack   Nina
    3   Jack  Julie
    
    >>> test2
        id_a   id_b  value
    0    Ben  Julie      1
    1    Ben   Nina      1
    2    Ben   Jack      0
    3  Julie   Nina      0
    4  Julie   Jack      1
    5   Nina   Jack      0
    

    我想做的是合并 test2 具有 test1 哪里 id_A == id_a id_B == id_b 哪里 id_A == id_b id_B == id_a ,导致以下数据帧:

    >>> final_df
        id_A   id_B  value
    0    Ben  Julie      1
    1  Julie    Ben      1
    2   Jack   Nina      0
    3   Jack  Julie      1
    

    当前解决方案:

    我的解决方案是可行的,但看起来很混乱,我想看看我是否忽略了一些更聪明的方法。它包括连接 测试2 有了它自己,但颠倒了2列感兴趣的内容( id_a 变成 id_b 反之亦然),然后从那里合并。

    test3 = pd.concat([test2, test2.rename(columns = {'id_a':'id_b', 'id_b':'id_a'})])
    
    final_df = (test1.merge(test3, left_on = ['id_A', 'id_B'],
                            right_on = ['id_a', 'id_b'])
                .drop(['id_a', 'id_b'], axis=1))
    

    问题:

    有人知道更整洁的方法吗?我觉得我可能忽略了一些令人惊奇的、随和的做事方式。

    谢谢你的帮助!

    3 回复  |  直到 6 年前
        1
  •  2
  •   piRSquared    6 年前

    frozenset

    test1.assign(
        value=test1.apply(frozenset, 1).map({frozenset(a): b for *a, b in test2.values}))
    
        id_A   id_B  value
    0    Ben  Julie      1
    1  Julie    Ben      1
    2   Jack   Nina      0
    3   Jack  Julie      1
    

    不那么可爱,更健壮。之后把你需要的东西拿出来。

    t1 = test1.assign(ref=list(map(frozenset, zip(test1.id_A, test1.id_B))))
    t2 = test2.assign(ref=list(map(frozenset, zip(test2.id_a, test2.id_b))))
    
    t1.merge(t2, on='ref')
    
        id_A   id_B            ref   id_a   id_b  value
    0    Ben  Julie   (Julie, Ben)    Ben  Julie      1
    1  Julie    Ben   (Julie, Ben)    Ben  Julie      1
    2   Jack   Nina   (Jack, Nina)   Nina   Jack      0
    3   Jack  Julie  (Jack, Julie)  Julie   Jack      1
    
        2
  •  2
  •   BENY    6 年前

    你可以试试 np.sort

    test1.assign(key=pd.DataFrame(np.sort(test1.values,axis=1)).sum(1)).merge(test2.assign(key=pd.DataFrame(np.sort(test2[['id_a','id_b']].values,axis=1)).sum(1))).drop('key',1)
    Out[188]: 
        id_A   id_B   id_a   id_b  value
    0    Ben  Julie    Ben  Julie      1
    1  Julie    Ben    Ben  Julie      1
    2   Jack   Nina   Nina   Jack      0
    3   Jack  Julie  Julie   Jack      1
    
        3
  •  1
  •   scnerd    6 年前

    您可以执行两个内部联接,然后连接和消除重复,如下所示:

    merge_1 = test1.merge(test2, left_on = ['id_A', 'id_B'], right_on= ['id_a', 'id_b'])
    merge_2 = test1.merge(test2, left_on = ['id_A', 'id_B'], right_on= ['id_b', 'id_a'])
    final_df = pd.concat([merge_1, merge_2]).drop_duplicates()
    

    或者您可以执行外部联接并手动计算条件:

    final_df = test1.merge(test2, how='outer')
    final_df = final_df[((final_df.id_A == final_df.id_a) &
                         (final_df.id_B == final_df.id_b)) |
                        ((final_df.id_A == final_df.id_b) &
                         (final_df.id_B == final_df.id_a))]
    

    或者可以创建一个始终按已知顺序排列的联接键:

    test1['join_key'] = test1.apply(lambda row: tuple(sorted(row[['id_A', 'id_B']])), axis=1)
    test2['join_key'] = test2.apply(lambda row: tuple(sorted(row[['id_a', 'id_b']])), axis=1)
    final_df = test1.merge(test2, on='join_key').drop('join_key')