在 2 列上合并 pandas 数据帧,但以任意顺序
Posted
技术标签:
【中文标题】在 2 列上合并 pandas 数据帧,但以任意顺序【英文标题】:Merging pandas dataframes on 2 columns but in either order 【发布时间】:2018-11-01 17:34:36 【问题描述】:问题:
我遇到了 2 个数据框的情况:
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
OR 其中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
当前解决方案:
我的解决方案有效,但看起来很混乱,我想看看我是否忽略了一些更聪明的做事方式。它涉及将 test2
与其自身连接,但将感兴趣的 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))
问题:
有没有人知道一个更简洁的方法来做到这一点?我觉得我可能忽略了一些令人惊奇的、讨人喜欢的做事方式。
感谢您的帮助!
【问题讨论】:
【参考方案1】:你可以试试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
【讨论】:
这很聪明!我想我正在尝试做的事情没有神奇的pandas
功能:(
@sacul 我不认为熊猫可以做到这一点,因为它需要重建数据框(列侧)以进行合并。【参考方案2】:
你可以做两个内连接,然后连接和去重复,比如:
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')
【讨论】:
是的,两次合并方法是我的第一次尝试,它也很有效。感谢您的选择!【参考方案3】:与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 列上合并 pandas 数据帧,但以任意顺序的主要内容,如果未能解决你的问题,请参考以下文章