ChatGPT解决这个技术问题 Extra ChatGPT

如何与熊猫执行 (INNER| (LEFT|RIGHT|FULL) OUTER) JOIN?

如何在合并后为缺失的行添加 NaN?

合并后如何摆脱 NaN?

我可以合并索引吗?

如何合并多个 DataFrame?

与熊猫交叉加入

合并?加入?连接?更新?谁?什么?为什么?!

... 和更多。我已经看到这些反复出现的问题询问熊猫合并功能的各个方面。今天,关于合并及其各种用例的大部分信息都分散在几十个措辞不当、无法搜索的帖子中。这里的目的是为后代整理一些更重要的观点。

本问答旨在成为有关 Pandas 常用习语的一系列有用用户指南中的下一部分(请参阅 this post on pivotingthis post on concatenation,我稍后会谈到)。

请注意,这篇文章不是要替代 the documentation,所以也请阅读!一些例子取自那里。

目录

为了方便访问。

合并基础 - 连接的基本类型(首先阅读此内容)

基于索引的连接

泛化到多个 DataFrame

交叉连接


c
creanion

这篇文章旨在为读者提供 SQL 风格与 Pandas 合并的入门知识,如何使用它以及何时不使用它。

特别是,本文将介绍以下内容:

基础知识 - 连接类型(LEFT、RIGHT、OUTER、INNER)与不同的列名合并与多个列合并避免输出中出现重复的合并键列

合并不同的列名

与多列合并

避免输出中出现重复的合并键列

这篇文章(以及我在这个线程上的其他帖子)不会经过:

与性能相关的讨论和时间安排(目前)。在适当的情况下,主要提到了更好的替代方案。

处理后缀、删除额外列、重命名输出和其他特定用例。还有其他(阅读:更好的)帖子可以解决这个问题,所以弄清楚吧!

注意大多数示例在演示各种功能时默认为 INNER JOIN 操作,除非另有说明。此外,这里的所有 DataFrame 都可以复制和复制,以便您可以使用它们。此外,请参阅这篇关于如何从剪贴板读取 DataFrames 的帖子。最后,JOIN 操作的所有视觉表示都是使用 Google 绘图手绘的。灵感来自这里。

说得够多了 - 告诉我如何使用合并!

设置和基础

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 替换的缺失值

要执行 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

这仅返回 leftright 中共享公共键的行(在本例中为“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。

该文档很好地总结了这些各种合并:

https://i.stack.imgur.com/5qDIy.png

其他 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_onright_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 时,如果您只需要输出中的 keyLeftkeyRight(但不是两者),您可以首先将索引设置为一个初步步骤。

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 操作时可能很重要。

仅合并其中一个 DataFrame 中的一列

例如,考虑

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(或 left_onright_on,视情况而定)指定一个列表。

left.merge(right, on=['key1', 'key2'] ...)

或者,如果名称不同,

left.merge(right, left_on=['lkey1', 'lkey2'], right_on=['rkey1', 'rkey2'])

其他有用的合并*操作和功能

在索引上将 DataFrame 与 Series 合并:请参阅此答案。

除了合并之外,DataFrame.update 和 DataFrame.combine_first 在某些情况下也用于将一个 DataFrame 与另一个 DataFrame 更新。

pd.merge_ordered 是有序 JOIN 的有用函数。

pd.merge_asof(阅读:merge_asOf)对于近似连接很有用。

本部分仅涵盖最基本的内容,旨在激发您的兴趣。如需更多示例和案例,请参阅 documentation on merge, join, and concat 以及函数规范的链接。

继续阅读

跳转到 Pandas Merging 101 中的其他主题继续学习:

合并基础 - 连接的基本类型 *

基于索引的连接

泛化到多个 DataFrame

交叉连接

*你在这里。


如果有人对每篇文章末尾的目录感到困惑,我将这个庞大的答案分成 4 个单独的答案,3 个在这个问题上,1 个在另一个问题上。以前的设置方式使得向人们推荐特定主题变得更加困难。这使您现在可以轻松地为单独的主题添加书签!
这是一个很棒的资源!我唯一的问题是为什么叫它合并而不是加入,加入而不是合并?
a
ansev

pd.concat([df0, df1], kwargs) 的补充视觉视图。请注意,kwarg axis=0axis=1 的含义不如 df.mean()df.apply(func) 直观

https://i.stack.imgur.com/1rb1R.jpg


这是一个很好的图表。请问你是怎么制作的?
google doc 的内置“插入 ==> 绘图... ==> 新”(截至 2019 年 5 月)。但是,要明确一点:我为这张图片使用 google doc 的唯一原因是因为我的笔记存储在 google doc 中,我想要一张可以在 google doc 中快速修改的图片。其实现在你提到它,谷歌文档的绘图工具非常整洁。
哇,这太棒了。来自 SQL 世界,“垂直”连接在我的脑海中并不是一个连接,因为表的结构总是固定的。现在甚至认为 pandas 应该合并 concatmerge,方向参数为 horizontalvertical
@Ufos 这不正是 axis=1axis=0 吗?
是的,现在有 mergeconcat 和轴等等。然而,正如@eliu 所展示的,这都是 merge 与“left”和“right”以及“horizontal”或“vertical”的相同概念。就我个人而言,每次我必须记住哪个“轴”是 0 和哪个是 1 时,我都必须查看文档。
A
Anurag Dhadse

加入101

这些动画可能会更好地直观地向您解释。学分:Garrick Aden-Buie tidyexplain repo

内部联接

https://i.stack.imgur.com/3qpXx.gif

外部联接或完全联接

https://i.stack.imgur.com/dG8mw.gif

右连接

https://i.stack.imgur.com/JpPRH.gif

左连接

https://i.stack.imgur.com/s5hgJ.gif


这些太棒了!
我感谢为实现这一目标而付出的努力。做得很漂亮。
G
Gonçalo Peres

在这个答案中,我将考虑实际的例子。

第一个是 pandas.concat

第二个,合并来自一个索引和另一个列的数据帧。

1pandas.concat

考虑以下具有相同列名的 DataFrames

Preco2018 大小 (8784, 5)

https://i.stack.imgur.com/rsqan.png

Preco 2019 尺寸 (8760, 5)

https://i.stack.imgur.com/uZoyW.png

具有相同的列名。

您可以使用 pandas.concat 组合它们,只需

import pandas as pd

frames = [Preco2018, Preco2019]

df_merged = pd.concat(frames)

这导致 DataFrame 具有以下大小 (17544, 5)

https://i.stack.imgur.com/8gAVG.png

如果你想可视化,它最终会像这样工作

https://i.stack.imgur.com/3KFNZ.png

(Source)

2.按列和索引合并

在这一部分中,我将考虑一个具体的案例:如果想要合并一个数据帧的索引和另一个数据帧的列。

假设一个数据框 Geo 有 54 列,是日期 Data 的列之一,它的类型是 datetime64[ns]

https://i.stack.imgur.com/gvSIB.png

具有一列价格和索引的数据框 Price 对应于日期

https://i.stack.imgur.com/Dp7Jm.png

在这种特定情况下,要合并它们,可以使用 pd.merge

merged = pd.merge(Price, Geo, left_index=True, right_on='Data')

这导致以下数据框

https://i.stack.imgur.com/yJVD3.png


6
6 revs, 2 users 100%

这篇文章将讨论以下主题:

在不同条件下与索引合并 基于索引的连接选项:合并、连接、合并索引 合并一个索引、另一个列

基于索引的连接的选项:合并、连接、连接

合并索引

合并一个索引,另一个列

有效地使用命名索引来简化合并语法

BACK TO TOP

基于索引的连接

TL;博士

有几个选项,根据用例,一些选项比其他选项更简单。 DataFrame.merge with left_index and right_index(或left_on和right_on使用命名索引)支持inner/left/right/full一次只能连接两个支持column-column, index-column, index-index joins DataFrame.join (join on index) 支持 inner/left (default)/right/full 可以一次加入多个 DataFrame 支持 index-index 联接 pd.concat (joins on index) 支持 inner/full (default) 可以一次加入多个 DataFrame 支持 index-索引连接

索引到索引连接

设置和基础

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

通常,索引上的内部连接如下所示:

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:列重叠但未指定后缀: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 加入索引,可以加入两个或一次更多的数据帧。默认情况下它进行完全外连接,所以这里需要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 的更多信息,请参阅这篇文章。

列连接索引

要使用左侧索引、右侧列执行内连接,您将使用 left_index=Trueright_on=... 的组合 DataFrame.merge

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 可以对列连接执行索引。您可以连接多个列,前提是左侧的索引级别数等于右侧的列数。

joinconcat 不能混合合并。您需要使用 DataFrame.set_index 将索引设置为前置步骤。

有效使用命名索引 [pandas >= 0.23]

如果您的索引已命名,则从 pandas >= 0.23 开始,DataFrame.merge 允许您将索引名称指定为 on(或 left_onright_on 根据需要)。

left.merge(right, on='idxkey')

         value_x   value_y
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

对于上一个与左索引合并右列的示例,您可以将left_on与左索引名称一起使用:

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 中的其他主题继续学习:

合并基础 - 连接的基本类型

基于索引的连接*

泛化到多个 DataFrame

交叉连接

你在这里


6
6 revs, 2 users 87%

这篇文章将讨论以下主题:

如何正确泛化到多个 DataFrame(以及为什么 merge 在这里有缺点)

合并唯一键

合并非唯一键

BACK TO TOP

泛化到多个 DataFrame

通常,当多个 DataFrame 要合并在一起时会出现这种情况。天真地,这可以通过链接 merge 调用来完成:

df1.merge(df2, ...).merge(df3, ...)

但是,对于许多 DataFrame,这很快就会失控。此外,可能需要对未知数量的 DataFrame 进行泛化。

这里我介绍 pd.concat 用于 unique 键上的多路连接,而 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' 以获得 FULL OUTER JOIN。请注意,您不能指定 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([B2, C2], 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 中的其他主题继续学习:

合并基础 - 连接的基本类型

基于索引的连接

泛化到多个 DataFrame *

交叉连接

你在这里