这篇文章旨在向读者介绍 SQL 风格的 Pandas 合并、如何使用它以及何时不使用它。
特别是,本文将介绍以下内容:
这篇文章(以及我在该线程上的其他帖子)不会经历的内容:
- 与性能相关的讨论和时间安排(目前)。在适当的情况下,主要提到了更好的替代方案。
- 处理后缀、删除额外的列、重命名输出和其他特定用例。还有其他(阅读:更好)的帖子可以解决这个问题,所以弄清楚吧!
Note除非另有说明,大多数示例在演示各种功能时默认使用 INNER JOIN 操作。
此外,这里的所有 DataFrame 都可以复制和复制,因此
你可以和他们一起玩。另请参阅这
邮政 https://stackoverflow.com/questions/31610889/how-to-copy-paste-dataframe-from-stackoverflow-into-python关于如何从剪贴板读取数据帧。
最后,JOIN 操作的所有视觉表示都是使用 Google Drawings 手绘的。灵感来自here https://stackoverflow.com/a/55858991/4909087.
说得够多了——只是告诉我如何使用merge
!
设置和基础知识
np.random.seed(0)
left = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'value': np.random.randn(4)})
right = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'value': np.random.randn(4)})
left
key value
0 A 1.764052
1 B 0.400157
2 C 0.978738
3 D 2.240893
right
key value
0 B 1.867558
1 D -0.977278
2 E 0.950088
3 F -0.151357
为了简单起见,键列具有相同的名称(目前)。
An 内部联接表示为
Note这个以及即将出现的数字都遵循这个约定:
-
blue表示合并结果中存在的行
-
red表示从结果中排除(即删除)的行
-
green表示缺失值被替换为
NaN
结果中的 s
要执行 INNER JOIN,请调用merge https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.merge.html在左侧 DataFrame 上,指定右侧 DataFrame 和连接键(至少)作为参数。
left.merge(right, on='key')
# Or, if you want to be explicit
# left.merge(right, on='key', how='inner')
key value_x value_y
0 B 0.400157 1.867558
1 D 2.240893 -0.977278
这仅返回来自left
and right
它们共享一个公共密钥(在本例中为“B”和“D”)。
A 左外连接,或 LEFT JOIN 表示为
这可以通过指定来执行how='left'
.
left.merge(right, on='key', how='left')
key value_x value_y
0 A 1.764052 NaN
1 B 0.400157 1.867558
2 C 0.978738 NaN
3 D 2.240893 -0.977278
请仔细注意此处 NaN 的位置。如果您指定how='left'
,那么只有来自left
已使用,并且缺少数据right
被 NaN 取代。
同样,对于一个右外连接,或 RIGHT JOIN 这是...
...指定how='right'
:
left.merge(right, on='key', how='right')
key value_x value_y
0 B 0.400157 1.867558
1 D 2.240893 -0.977278
2 E NaN 0.950088
3 F NaN -0.151357
在这里,钥匙来自right
已使用,并且缺少数据left
被 NaN 取代。
最后,对于全外连接,由下式给出
specify how='outer'
.
left.merge(right, on='key', how='outer')
key value_x value_y
0 A 1.764052 NaN
1 B 0.400157 1.867558
2 C 0.978738 NaN
3 D 2.240893 -0.977278
4 E NaN 0.950088
5 F NaN -0.151357
这使用了两个帧中的键,并且为两个帧中缺失的行插入了 NaN。
该文档很好地总结了这些不同的合并:
其他 JOIN - LEFT 排除、RIGHT 排除和 FULL 排除/ANTI JOIN
如果你需要LEFT-排除 JOIN and RIGHT-排除 JOIN分两步进行。
对于 LEFT-Exclusion JOIN,表示为
首先执行 LEFT OUTER JOIN,然后过滤来自left
仅(不包括右侧的所有内容),
(left.merge(right, on='key', how='left', indicator=True)
.query('_merge == "left_only"')
.drop('_merge', 1))
key value_x value_y
0 A 1.764052 NaN
2 C 0.978738 NaN
Where,
left.merge(right, on='key', how='left', indicator=True)
key value_x value_y _merge
0 A 1.764052 NaN left_only
1 B 0.400157 1.867558 both
2 C 0.978738 NaN left_only
3 D 2.240893 -0.977278 both
类似地,对于 RIGHT-Exclusion JOIN,
(left.merge(right, on='key', how='right', indicator=True)
.query('_merge == "right_only"')
.drop('_merge', 1))
key value_x value_y
2 E NaN 0.950088
3 F NaN -0.151357
最后,如果您需要进行仅保留左侧或右侧键的合并,但不保留两者(IOW,执行反连接),
你可以用类似的方式做到这一点——
(left.merge(right, on='key', how='outer', indicator=True)
.query('_merge != "both"')
.drop('_merge', 1))
key value_x value_y
0 A 1.764052 NaN
2 C 0.978738 NaN
4 E NaN 0.950088
5 F NaN -0.151357
关键列的不同名称
如果键列的名称不同 - 例如,left
has keyLeft
, and right
has keyRight
代替key
——那么你必须指定left_on
and right_on
作为参数而不是on
:
left2 = left.rename({'key':'keyLeft'}, axis=1)
right2 = right.rename({'key':'keyRight'}, axis=1)
left2
keyLeft value
0 A 1.764052
1 B 0.400157
2 C 0.978738
3 D 2.240893
right2
keyRight value
0 B 1.867558
1 D -0.977278
2 E 0.950088
3 F -0.151357
left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')
keyLeft value_x keyRight value_y
0 B 0.400157 B 1.867558
1 D 2.240893 D -0.977278
避免输出中出现重复的键列
当合并时keyLeft
from left
and keyRight
from right
,如果您只想要其中一个keyLeft
or keyRight
(但不是两者)在输出中,您可以通过设置索引作为初步步骤开始。
left3 = left2.set_index('keyLeft')
left3.merge(right2, left_index=True, right_on='keyRight')
value_x keyRight value_y
0 0.400157 B 1.867558
1 2.240893 D -0.977278
将此与之前命令的输出进行对比(即,left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')
),你会注意到keyLeft
不见了。您可以根据将哪个帧的索引设置为键来确定要保留哪一列。例如,当执行某些 OUTER JOIN 操作时,这可能很重要。
仅合并其中一列中的一列DataFrames
例如,考虑
right3 = right.assign(newcol=np.arange(len(right)))
right3
key value newcol
0 B 1.867558 0
1 D -0.977278 1
2 E 0.950088 2
3 F -0.151357 3
如果您需要仅合并“newcol”(没有任何其他列),通常可以在合并之前对列进行子集化:
left.merge(right3[['key', 'newcol']], on='key')
key value newcol
0 B 0.400157 0
1 D 2.240893 1
如果您正在进行 LEFT OUTER JOIN,则性能更高的解决方案将涉及map
:
# left['newcol'] = left['key'].map(right3.set_index('key')['newcol']))
left.assign(newcol=left['key'].map(right3.set_index('key')['newcol']))
key value newcol
0 A 1.764052 NaN
1 B 0.400157 0.0
2 C 0.978738 NaN
3 D 2.240893 1.0
如前所述,这类似于但比
left.merge(right3[['key', 'newcol']], on='key', how='left')
key value newcol
0 A 1.764052 NaN
1 B 0.400157 0.0
2 C 0.978738 NaN
3 D 2.240893 1.0
合并多列
要加入多个列,请指定一个列表on
(or left_on
and right_on
, 作为适当的)。
left.merge(right, on=['key1', 'key2'] ...)
或者,如果名称不同,
left.merge(right, left_on=['lkey1', 'lkey2'], right_on=['rkey1', 'rkey2'])
其他有用的merge*
操作和功能
-
将 DataFrame 与索引上的 Series 合并:请参阅这个答案 https://stackoverflow.com/a/40762674/4909087.
-
Besides merge
, DataFrame.update https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.update.html and DataFrame.combine_first https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.combine_first.html在某些情况下也用于用另一个 DataFrame 更新一个 DataFrame。
-
pd.merge_ordered http://pandas.pydata.org/pandas-docs/version/0.19.0/generated/pandas.merge_ordered.html对于有序 JOIN 来说是一个有用的函数。
-
pd.merge_asof https://pandas.pydata.org/pandas-docs/stable/generated/pandas.merge_asof.html(阅读:merge_asOf)对于近似 joins.
本节仅涵盖非常基础的内容,旨在激发您的兴趣。更多示例和案例请参见文档关于merge, join, and concat https://pandas.pydata.org/pandas-docs/stable/merging.html以及功能规格的链接。
继续阅读
跳转到 Pandas Merging 101 中的其他主题继续学习:
-
Merging basics - basic types of joins https://stackoverflow.com/a/53645883/4909087 *
-
基于索引的连接 https://stackoverflow.com/a/65167356/4909087
-
泛化到多个 DataFrame https://stackoverflow.com/a/65167327/4909087
-
交叉连接 https://stackoverflow.com/a/53699013/4909087
*You are here.