Pandas融合101

hm2xizp9  于 2022-11-27  发布在  其他
关注(0)|答案(8)|浏览(180)
  • 如何执行(INNER|(LEFT)|RIGHT|有Pandas吗?
  • 如何在合并后为缺少的行添加NaN?
  • 如何在合并后删除NaN?
  • 我可以在索引上合并吗?
  • 如何合并多个DataFrame?
  • 与Pandas交叉连接
  • 谁?什么?为什么?

......还有更多。我看到这些反复出现的问题询问Pandas合并功能的各个方面。今天关于合并及其各种用例的大部分信息都分散在几十个措辞糟糕、无法搜索的帖子中。这里的目的是为后代整理一些更重要的观点。
这篇问答文章将是一系列关于Pandas常用习语的实用用户指南的下一篇文章(参见this post on pivotingthis post on concatenation,我稍后会谈到)。
请注意,这篇文章 * 不是 * 要取代the documentation,所以请也阅读这篇文章!

目录

为了方便访问。

3okqufwl

3okqufwl1#

这篇文章旨在给予读者一个入门的SQL风格的合并与Pandas,如何使用它,什么时候不使用它。
具体来说,这篇文章将经历以下内容:

  • 基础知识-联接类型(左、右、外、内)
  • 使用不同的列名合并
  • 合并多个列
  • 避免在输出中出现重复的合并键列

这篇文章(和其他职位由我在这个线程)不会经历:

  • 与性能相关的讨论和时间安排(目前)。在适当的情况下,大多数值得注意的是提到了更好的替代方案。
  • 处理后缀、删除额外的列、重命名输出以及其他特定的用例。更好)处理的职位,所以弄清楚!
    注意除非另有说明,否则在演示各种功能时,大多数示例都默认使用INNER JOIN操作。

此外,这里所有的DataFrame都可以被复制和复制,这样你就可以玩它们了。另外,关于如何从剪贴板读取DataFrame,请参阅this post
最后,JOIN操作的所有可视化表示都是使用Google Drawings手工绘制的。

说得够多了-告诉我如何使用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

这只会传回leftright中共用相同索引键的数据列(在此范例中为“B”和“D”)。

左外部联接或左联接表示为

这可以通过指定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替换。
类似地,对于右外部联接或右联接...

...指定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的密钥,并且用NaN替换来自left的缺失数据。
最后,对于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。
该文档很好地总结了这些不同的合并:
x1c4d 1x指令集

其他联接-左排除、右排除和完全排除/反联接

如果您需要分两步执行左排除联接右排除联接
对于左排除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

类似地,对于右排除联接,

(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的名称为keyLeftright的名称为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中的keyLeftright中的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操作时可能很重要。

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

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

或者,在名称不同的情况下,

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

其他有用的merge*操作和函数

本节只介绍最基本的内容,旨在激发您的兴趣。有关更多示例和案例,请参阅documentation on merge , join , and concat以及指向函数规范的链接。

继续阅读

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

qxgroojn

qxgroojn2#

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

arknldoa

arknldoa3#

联接101

这些动画可能会更好地解释你的视觉效果。Garrick Aden-Buie tidyexplain repo

内部链接

外部联接或完全联接

右连接

左连接

5n0oy7gb

5n0oy7gb4#

在回答这个问题时,我将考虑以下实际例子:

  1. pandas.concat
  2. pandas.DataFrame.merge以合并来自一个索引的 Dataframe 和另一个索引的列的 Dataframe 。
    我们将对每种情况使用不同的 Dataframe 。

1. pandas.concat*********

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

*2018年价格,尺寸为(8784, 5)

Year  Month  Day  Hour  Price
0  2018      1    1     1   6.74
1  2018      1    1     2   4.74
2  2018      1    1     3   3.66
3  2018      1    1     4   2.30
4  2018      1    1     5   2.30
5  2018      1    1     6   2.06
6  2018      1    1     7   2.06
7  2018      1    1     8   2.06
8  2018      1    1     9   2.30
9  2018      1    1    10   2.30

*2019年价格,尺寸为(8760, 5)

Year  Month  Day  Hour  Price
0  2019      1    1     1  66.88
1  2019      1    1     2  66.88
2  2019      1    1     3  66.00
3  2019      1    1     4  63.64
4  2019      1    1     5  58.85
5  2019      1    1     6  55.47
6  2019      1    1     7  56.00
7  2019      1    1     8  61.09
8  2019      1    1     9  61.01
9  2019      1    1    10  61.00

可以使用pandas.concat将它们组合起来,只需

import pandas as pd

frames = [Price2018, Price2019]

df_merged = pd.concat(frames)

这会产生大小为(17544, 5)的DataFrame
如果一个人想清楚地了解所发生的事情,它是这样工作的

Source

2.pandas.DataFrame.merge的文件

在本节中,我们将考虑一个具体的案例:合并一个 Dataframe 的索引和另一个 Dataframe 的列
假设 Dataframe Geo包含54列,Datedatetime64[ns]类型的列之一。

Date         1         2  ...        51        52        53
0 2010-01-01 00:00:00  0.565919  0.892376  ...  0.593049  0.775082  0.680621
1 2010-01-01 01:00:00  0.358960  0.531418  ...  0.734619  0.480450  0.926735
2 2010-01-01 02:00:00  0.531870  0.221768  ...  0.902369  0.027840  0.398864
3 2010-01-01 03:00:00  0.475463  0.245810  ...  0.306405  0.645762  0.541882
4 2010-01-01 04:00:00  0.954546  0.867960  ...  0.912257  0.039772  0.627696

Dataframe Price中有一列的价格名为Price,索引对应于日期(Date

Price
Date                      
2010-01-01 00:00:00  29.10
2010-01-01 01:00:00   9.57
2010-01-01 02:00:00   0.00
2010-01-01 03:00:00   0.00
2010-01-01 04:00:00   0.00

为了合并它们,可以如下使用pandas.DataFrame.merge

df_merged = pd.merge(Price, Geo, left_index=True, right_on='Date')

其中GeoPrice是先前的 Dataframe 。
这将产生以下 Dataframe

Price                Date         1  ...        51        52        53
0  29.10 2010-01-01 00:00:00  0.565919  ...  0.593049  0.775082  0.680621
1   9.57 2010-01-01 01:00:00  0.358960  ...  0.734619  0.480450  0.926735
2   0.00 2010-01-01 02:00:00  0.531870  ...  0.902369  0.027840  0.398864
3   0.00 2010-01-01 03:00:00  0.475463  ...  0.306405  0.645762  0.541882
4   0.00 2010-01-01 04:00:00  0.954546  ...  0.912257  0.039772  0.627696
pbpqsu0x

pbpqsu0x5#

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

  • 在不同条件下与索引合并
  • 基于索引得连接得选项:mergejoinconcat
  • 合并索引
  • 合并一个的索引,另一个的列
  • 有效地使用命名索引来简化合并语法

BACK TO TOP

基于索引的联接

TL;DR的名称

有几个选项,有些比其他更简单,这取决于用例。

  • 使用left_indexright_indexDataFrame.merge(或使用命名索引的left_onright_on
  • 支持内部/左侧/右侧/完全
  • 我一次只能加入两个
  • 支持列-列、索引-列、索引-索引连接
  • DataFrame.join(索引上的联接)
  • 支持内部/左(默认)/右/完全
  • 一次可以联接多个DataFrame
  • 支持索引-索引连接
  • pd.concat(索引上的连接)
  • 支持内部/完全(默认)
  • 一次可以联接多个DataFrame
  • 支持索引-索引连接

索引到索引连接

设置与基础

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

其他连接遵循类似的语法。

值得注意的替代方案

1.**DataFrame.join**默认为索引上的联接。DataFrame.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

请注意,我需要指定lsuffixrsuffix参数,因为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

1.**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的更多信息,请参阅此帖子。

列连接的索引

要使用左索引、右列执行内部连接,您将使用DataFrame.mergeleft_index=Trueright_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可以执行索引到列的联接。如果左侧的索引级别数等于右侧的列数,则可以联接多个列。
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,列为right,可以使用索引名称为left的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中的其他主题继续学习:

yhived7q

yhived7q6#

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

  • 如何正确地泛化到多个DataFrame(以及为什么merge在这里有缺点)
  • 按唯一键合并
  • 在非唯一键上合并

BACK TO TOP

泛化为多个DataFrame

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

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

然而,对于许多 Dataframe 来说,这很快就失去了控制。此外,可能需要对未知数量的 Dataframe 进行归纳。
这里我将介绍pd.concatDataFrame.join,前者用于 unique 键上的多路连接,后者用于 non-unique 键上的多路连接。

# 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

对于FULL OUTER JOIN,省略join='inner'。请注意,不能指定LEFT或RIGHT OUTER联接(如果需要,请使用join,如下所述)。

对具有重复项的键进行多路合并

concat速度很快,但也有缺点,它不能处理重复项。
第一个
在这种情况下,我们可以使用join,因为它可以处理非唯一键(注意,join在DataFrame的索引上连接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中的其他主题继续学习:

ecr0jaav

ecr0jaav7#

我认为你应该在解释中包括这个,因为这是一个我经常看到的相关合并,我相信它被称为cross-join。这是一个当唯一的df不共享列时发生的合并,它只是将2个df并排合并:
设置:

names1 = [{'A':'Jack', 'B':'Jill'}]

names2 = [{'C':'Tommy', 'D':'Tammy'}]

df1=pd.DataFrame(names1)
df2=pd.DataFrame(names2)
df_merged= pd.merge(df1.assign(X=1), df2.assign(X=1), on='X').drop('X', 1)

这将创建一个虚拟的X列,在X上合并,然后删除它以生成
df_merged:

A     B      C      D
0  Jack  Jill  Tommy  Tammy
2ekbmq32

2ekbmq328#

Pandas目前不支持合并语法中的不等连接;一个选项是使用pyjanitor中的conditional_join函数-我是这个库的贡献者:

# pip install pyjanitor
import pandas as pd
import janitor 

left.conditional_join(right, ('value', 'value', '>'))

   left           right
    key     value   key     value
0     A  1.764052     D -0.977278
1     A  1.764052     F -0.151357
2     A  1.764052     E  0.950088
3     B  0.400157     D -0.977278
4     B  0.400157     F -0.151357
5     C  0.978738     D -0.977278
6     C  0.978738     F -0.151357
7     C  0.978738     E  0.950088
8     D  2.240893     D -0.977278
9     D  2.240893     F -0.151357
10    D  2.240893     E  0.950088
11    D  2.240893     B  1.867558

left.conditional_join(right, ('value', 'value', '<'))

  left           right
   key     value   key     value
0    A  1.764052     B  1.867558
1    B  0.400157     E  0.950088
2    B  0.400157     B  1.867558
3    C  0.978738     B  1.867558

列作为元组的变量参数传递,每个元组由左 Dataframe 中的一列、右 Dataframe 中的一列和联接运算符(可以是(>, <, >=, <=, !=)中的任何一个)组成。在上面的示例中,由于列名重叠,因此返回了MultiIndex列。
从性能上看,这比单纯的交叉连接要好:

np.random.seed(0)
dd = pd.DataFrame({'value':np.random.randint(100000, size=50_000)})
df = pd.DataFrame({'start':np.random.randint(100000, size=1_000), 
                   'end':np.random.randint(100000, size=1_000)})

dd.head()

   value
0  68268
1  43567
2  42613
3  45891
4  21243

df.head()

   start    end
0  71915  47005
1  64284  44913
2  13377  96626
3  75823  38673
4  29151    575

%%timeit
out = df.merge(dd, how='cross')
out.loc[(out.start < out.value) & (out.end > out.value)]
5.12 s ± 19 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit df.conditional_join(dd, ('start', 'value' ,'<'), ('end', 'value' ,'>'))
280 ms ± 5.56 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit df.conditional_join(dd, ('start', 'value' ,'<'), ('end', 'value' ,'>'), use_numba=True)
124 ms ± 12.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

out = df.merge(dd, how='cross')
out = out.loc[(out.start < out.value) & (out.end > out.value)]
A = df.conditional_join(dd, ('start', 'value' ,'<'), ('end', 'value' ,'>'))
columns = A.columns.tolist()
A = A.sort_values(columns, ignore_index = True)
out = out.sort_values(columns, ignore_index = True)

A.equals(out)
True

相关问题