熊猫合并101
Posted
技术标签:
【中文标题】熊猫合并101【英文标题】:Pandas Merging 101 【发布时间】:2021-09-22 04:05:42 【问题描述】: 如何使用 pandas 执行 (INNER
| (LEFT
|RIGHT
|FULL
) OUTER
) JOIN
?
如何在合并后为缺失的行添加 NaN?
合并后如何去除 NaN?
我可以合并索引吗?
如何合并多个 DataFrame?
与 pandas 交叉联接
merge
? join
? concat
? update
?谁?什么?为什么?!
...等等。我已经看到这些反复出现的问题询问熊猫合并功能的各个方面。今天,关于合并及其各种用例的大部分信息都分散在数十个措辞不当、无法搜索的帖子中。这里的目的是为后代整理一些更重要的观点。
本问答旨在成为有关 Pandas 常用习语的一系列有用用户指南的下一部分(请参阅 this post on pivoting 和 this post on concatenation,稍后我会谈到)。
请注意,这篇文章不是要替代the documentation,所以也请阅读!部分示例取自那里。
目录
为了方便访问。
Merging basics - basic types of joins(先阅读)
Index-based joins
Generalizing to multiple DataFrames
Cross join
【问题讨论】:
【参考方案1】:这篇文章旨在为读者提供 SQL 风格与 Pandas 合并的入门知识,如何使用它以及何时不使用它。
特别是,本文将介绍以下内容:
基础 - 连接类型(左、右、外、内)
与不同的列名合并 与多列合并 避免输出中出现重复的合并键列这篇文章(以及我在这个线程上的其他帖子)不会经过:
与性能相关的讨论和时间安排(目前)。在适当的情况下,主要提到了更好的替代方案。 处理后缀、删除多余的列、重命名输出和其他特定用例。还有其他(阅读:更好的)帖子可以解决这个问题,所以弄清楚吧!注意 大多数示例在演示各种功能时默认使用 INNER JOIN 操作,除非另有说明。
此外,这里的所有 DataFrame 都可以复制和复制,所以 你可以和他们一起玩。另见this post 关于如何从剪贴板中读取 DataFrame。
最后,JOIN 操作的所有可视化表示都是使用 Google 绘图手绘的。灵感来自here。
说够了 - 告诉我如何使用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
为简单起见,键列具有相同的名称(目前)。
INNER JOIN由
表示注意 这和即将到来的数字都遵循这个约定:
蓝色表示合并结果中存在的行 红色表示从结果中排除(即删除)的行 绿色表示在结果中被NaN
s 替换的缺失值
要执行 INNER JOIN,请在左侧 DataFrame 上调用 merge
,指定右侧 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
和right
的行,它们共享一个公共键(在本例中为“B”和“D”)。
LEFT OUTER JOIN,或 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 OUTER JOIN,或 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 替换。
最后,对于 FULL OUTER JOIN,由
给出指定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-Excluding、RIGHT-Excluding 和 FULL-Excluding/ANTI JOIN
如果您需要分两步LEFT-Excluding JOIN和RIGHT-Excluding JOIN。
对于LEFT-Excluding 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
在哪里,
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-Excluding 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,执行 ANTI-JOIN),
你可以用类似的方式来做这件事——
(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
具有 keyLeft
,right
具有 keyRight
而不是 key
——那么您必须将 left_on
和 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
避免在输出中重复键列
当从left
合并keyLeft
和从right
合并keyRight
时,如果您只想在输出中使用keyLeft
或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
如果您只需要合并“new_val”(没有任何其他列),您通常可以在合并之前只对列进行子集:
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
(或left_on
和right_on
,视情况而定)的列表。
left.merge(right, on=['key1', 'key2'] ...)
或者,如果名称不同,
left.merge(right, left_on=['lkey1', 'lkey2'], right_on=['rkey1', 'rkey2'])
其他有用的merge*
操作和函数
将 DataFrame 与索引上的 Series 合并:请参阅 this answer。
除了merge
,DataFrame.update
和DataFrame.combine_first
在某些情况下也用于更新一个DataFrame。
pd.merge_ordered
是用于有序 JOIN 的有用函数。
pd.merge_asof
(阅读:merge_asOf)对于近似连接很有用。
本部分仅涵盖最基本的内容,旨在激发您的兴趣。有关更多示例和案例,请参阅documentation on merge
, join
, and concat
以及功能规范的链接。
继续阅读
跳到 Pandas Merging 101 中的其他主题以继续学习:
Merging basics - basic types of joins *
Index-based joins
Generalizing to multiple DataFrames
Cross join
*你在这里。
【讨论】:
如果有人对每篇文章末尾的目录感到困惑,我将这个庞大的答案分成 4 个单独的答案,3 个在这个问题上,1 个在另一个问题上。以前的设置方式使得向人们推荐特定主题变得更加困难。这使您现在可以轻松地为单独的主题添加书签! 这是一个很棒的资源!我唯一的问题是为什么叫它合并而不是加入,加入而不是合并?【参考方案2】:pd.concat([df0, df1], kwargs)
的补充视觉视图。
请注意,kwarg axis=0
或 axis=1
的含义不如 df.mean()
或 df.apply(func)
直观
【讨论】:
这是一个很好的图表。请问你们是怎么制作的? google doc 的内置“插入 ==> 绘图... ==> 新”(截至 2019 年 5 月)。但是,要明确一点:我为这张图片使用 google doc 的唯一原因是因为我的笔记存储在 google doc 中,我想要一张可以在 google doc 本身中快速修改的图片。其实现在你提到了,google doc 的绘图工具很漂亮。 哇,这太棒了。来自 SQL 世界,“垂直”连接在我的脑海中并不是一个连接,因为表的结构总是固定的。现在甚至认为 pandas 应该合并concat
和 merge
,方向参数为 horizontal
或 vertical
。
@Ufos 这不正是axis=1
和axis=0
的样子吗?
是的,现在有 merge
和 concat
和轴等等。但是,正如@eliu 所示,这都是 merge 与“左”和“右”以及“水平”或“垂直”的相同概念。就个人而言,每次我必须记住哪个“轴”是 0
和哪个是 1
时,我都必须查看文档。【参考方案3】:
加入 101
这些动画可能更能直观地向您解释。 致谢:Garrick Aden-Buie tidyexplain repo
内连接
外部联接或完全联接
右连接
左连接
【讨论】:
这些太棒了! 我很感激为实现这一目标付出的努力。做得很好。【参考方案4】:在这个答案中,我将考虑实际的例子。
第一个,是pandas.concat
。
第二个,从一个索引和另一个列合并数据帧。
1。 pandas.concat
考虑以下具有相同列名的DataFrames
:
Preco2018,尺寸为 (8784, 5)
Preco 2019 尺寸为 (8760, 5)
具有相同的列名。
您可以使用pandas.concat
将它们组合起来,只需
import pandas as pd
frames = [Preco2018, Preco2019]
df_merged = pd.concat(frames)
这会导致 DataFrame 具有以下大小 (17544, 5)
如果你想可视化,它最终会像这样工作
(Source)
2。按列和索引合并
在这一部分,我会考虑一个具体的案例:如果想合并一个数据框的索引和另一个数据框的列。
假设一个数据框 Geo
有 54 列,是日期 Data
的列之一,其类型为 datetime64[ns]
。
数据框Price
有一列价格,索引对应日期
在这种特定情况下,要合并它们,可以使用pd.merge
merged = pd.merge(Price, Geo, left_index=True, right_on='Data')
这会导致以下数据框
【讨论】:
【参考方案5】:这篇文章将涉及以下主题:
不同条件下与索引合并 基于索引的连接选项:merge
、join
、concat
合并索引
合并一个索引,另一个列
有效地使用命名索引来简化合并语法
BACK TO TOP
基于索引的联接
TL;DR
有几个选项,根据用途,有些比其他更简单 案例。
DataFrame.merge
与left_index
和right_index
(或left_on
和right_on
使用名称索引) 支持内/左/右/全 一次只能加入两个 支持列-列、索引-列、索引-索引连接DataFrame.join
(加入索引) 支持内/左(默认)/右/全 可以一次加入多个 DataFrames 支持索引-索引连接pd.concat
(加入索引) 支持内部/完整(默认) 可以一次加入多个 DataFrames 支持索引-索引连接
索引到索引连接
设置与基础
import pandas as pd
import numpy as np
np.random.seed([3, 14])
left = pd.DataFrame(data='value': np.random.randn(4),
index=['A', 'B', 'C', 'D'])
right = pd.DataFrame(data='value': np.random.randn(4),
index=['B', 'D', 'E', 'F'])
left.index.name = right.index.name = 'idxkey'
left
value
idxkey
A -0.602923
B -0.402655
C 0.302329
D -0.524349
right
value
idxkey
B 0.543843
D 0.013135
E -0.326498
F 1.385076
通常,inner join on index 如下所示:
left.merge(right, left_index=True, right_index=True)
value_x value_y
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
其他连接遵循类似的语法。
值得注意的替代品
DataFrame.join
默认加入索引。 DataFrame.join
默认执行 LEFT OUTER JOIN,所以这里需要 how='inner'
。
left.join(right, how='inner', lsuffix='_x', rsuffix='_y')
value_x value_y
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
请注意,我需要指定 lsuffix
和 rsuffix
参数,否则 join
会出错:
left.join(right)
ValueError: columns overlap but no suffix specified: Index(['value'], dtype='object')
由于列名相同。如果它们的名称不同,这将不是问题。
left.rename(columns='value':'leftvalue').join(right, how='inner')
leftvalue value
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
pd.concat
加入索引,可以一次加入两个或多个 DataFrame。默认情况下它会进行完全外连接,所以这里需要how='inner'
..
pd.concat([left, right], axis=1, sort=False, join='inner')
value value
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
有关concat
的更多信息,请参阅this post。
索引到列连接
要使用左侧索引、右侧列执行内连接,您将使用 DataFrame.merge
组合 left_index=True
和 right_on=...
。
right2 = right.reset_index().rename('idxkey' : 'colkey', axis=1)
right2
colkey value
0 B 0.543843
1 D 0.013135
2 E -0.326498
3 F 1.385076
left.merge(right2, left_index=True, right_on='colkey')
value_x colkey value_y
0 -0.402655 B 0.543843
1 -0.524349 D 0.013135
其他联接遵循类似的结构。请注意,只有merge
可以对列连接执行索引。您可以连接多个列,前提是左侧的索引级别数等于右侧的列数。
join
和 concat
不能混合合并。您需要使用DataFrame.set_index
将索引设置为前置步骤。
有效地使用命名索引 [pandas >= 0.23]
如果您的索引已命名,则从 pandas >= 0.23 开始,DataFrame.merge
允许您将索引名称指定为 on
(或 left_on
和 right_on
根据需要)。
left.merge(right, on='idxkey')
value_x value_y
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
前面用left的索引,right的column合并的例子,可以用left_on
和left的索引名:
left.merge(right2, left_on='idxkey', right_on='colkey')
value_x colkey value_y
0 -0.402655 B 0.543843
1 -0.524349 D 0.013135
继续阅读
跳转到 Pandas Merging 101 中的其他主题以继续学习:
Merging basics - basic types of joins
Index-based joins*
Generalizing to multiple DataFrames
Cross join
*你在这里
【讨论】:
【参考方案6】:这篇文章将涉及以下主题:
如何正确泛化到多个 DataFrame(以及为什么merge
在这里有缺点)
合并唯一键
在非唯一键上合并
BACK TO TOP
泛化到多个 DataFrames
通常情况下,当多个 DataFrame 要合并在一起时会出现这种情况。天真地,这可以通过链接 merge
调用来完成:
df1.merge(df2, ...).merge(df3, ...)
但是,对于许多 DataFrame,这很快就会失控。此外,可能需要对未知数量的 DataFrame 进行泛化。
这里我介绍pd.concat
用于唯一 键上的多路连接,DataFrame.join
用于非唯一 键上的多路连接。首先,设置。
# Setup.
np.random.seed(0)
A = pd.DataFrame('key': ['A', 'B', 'C', 'D'], 'valueA': np.random.randn(4))
B = pd.DataFrame('key': ['B', 'D', 'E', 'F'], 'valueB': np.random.randn(4))
C = pd.DataFrame('key': ['D', 'E', 'J', 'C'], 'valueC': np.ones(4))
dfs = [A, B, C]
# Note, the "key" column values are unique, so the index is unique.
A2 = A.set_index('key')
B2 = B.set_index('key')
C2 = C.set_index('key')
dfs2 = [A2, B2, C2]
唯一键的多路合并
如果您的键(这里的键可以是列或索引)是唯一的,那么您可以使用pd.concat
。请注意,pd.concat
在索引上加入 DataFrame。
# merge on `key` column, you'll need to set the index before concatenating
pd.concat([
df.set_index('key') for df in dfs], axis=1, join='inner'
).reset_index()
key valueA valueB valueC
0 D 2.240893 -0.977278 1.0
# merge on `key` index
pd.concat(dfs2, axis=1, sort=False, join='inner')
valueA valueB valueC
key
D 2.240893 -0.977278 1.0
省略join='inner'
以获得完整的外部连接。请注意,您不能指定 LEFT 或 RIGHT OUTER 连接(如果需要,请使用 join
,如下所述)。
重复键的多路合并
concat
速度很快,但也有缺点。它不能处理重复。
A3 = pd.DataFrame('key': ['A', 'B', 'C', 'D', 'D'], 'valueA': np.random.randn(5))
pd.concat([df.set_index('key') for df in [A3, B, C]], axis=1, join='inner')
ValueError: Shape of passed values is (3, 4), indices imply (3, 2)
在这种情况下,我们可以使用join
,因为它可以处理非唯一键(请注意,join
在其索引上连接 DataFrame;它在后台调用 merge
并执行 LEFT OUTER JOIN,除非另有说明)。
# join on `key` column, set as the index first
# For inner join. For left join, omit the "how" argument.
A.set_index('key').join(
[df.set_index('key') for df in (B, C)], how='inner').reset_index()
key valueA valueB valueC
0 D 2.240893 -0.977278 1.0
# join on `key` index
A3.set_index('key').join([B2, C2], how='inner')
valueA valueB valueC
key
D 1.454274 -0.977278 1.0
D 0.761038 -0.977278 1.0
继续阅读
跳到 Pandas Merging 101 中的其他主题以继续学习:
Merging basics - basic types of joins
Index-based joins
Generalizing to multiple DataFrames*
Cross join
*你在这里
【讨论】:
以上是关于熊猫合并101的主要内容,如果未能解决你的问题,请参考以下文章