前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >合并Pandas的DataFrame方法汇总

合并Pandas的DataFrame方法汇总

作者头像
老齐
发布2021-03-11 15:16:07
5.6K0
发布2021-03-11 15:16:07
举报
文章被收录于专栏:老齐教室老齐教室

Pandas是数据分析、机器学习等常用的工具,其中的DataFrame又是最常用的数据类型,对它的操作,不得不熟练。在《跟老齐学Python:数据分析》一书中,对DataFrame对象的各种常用操作都有详细介绍。本文根据书中介绍的内容,并参考其他文献,专门汇总了合并操作的各种方法。

Pandas提供好几种方法和函数来实现合并DataFrame的操作,一般的操作结果是创建一个新的DataFrame,而对原始数据没有任何影响。

方法1:merge()

先创建一个DataFrame对象,后面也会用到它。如下所示,df1包括姓名、电子邮件和用户id。

代码语言:javascript
复制
import pandas as pd

df1 = pd.DataFrame({'user_id': ['id001', 'id002', 'id003', 'id004', 'id005', 'id006', 'id007'],
                    'first_name': ['Rivi', 'Wynnie', 'Kristos', 'Madalyn', 'Tobe', 'Regan', 'Kristin'],
                    'last_name': ['Valti', 'McMurty', 'Ivanets', 'Max', 'Riddich', 'Huyghe', 'Illis'],
                    'email': ['rvalti0@example.com', 'wmcmurty1@example.com', 'kivanets2@example.com',
                              'mmax3@example.com', 'triddich4@example.com', 'rhuyghe@example.com', 'killis4@example.com']
                    })

为了能够进行合并的操作,还需要再创建一个df2,如下所示。

代码语言:javascript
复制
df2 = pd.DataFrame({'user_id': ['id001', 'id002', 'id003', 'id004', 'id005'],
                    'image_url': ['http://example.com/img/id001.png', 'http://example.com/img/id002.jpg',
                                  'http://example.com/img/id003.bmp', 'http://example.com/img/id004.jpg',
                                  'http://example.com/img/id005.png']
                    })

所创建的两个DataFrame数据如下:

代码语言:javascript
复制
# df1
  user_id first_name last_name                  email
0   id001       Rivi     Valti    rvalti0@example.com
1   id002     Wynnie   McMurty  wmcmurty1@example.com
2   id003    Kristos   Ivanets  kivanets2@example.com
3   id004    Madalyn       Max      mmax3@example.com
4   id005       Tobe   Riddich  triddich4@example.com
5   id006      Regan    Huyghe    rhuyghe@example.com
6   id007    Kristin     Illis    killis4@example.com

#df2
  user_id                         image_url
0   id001  http://example.com/img/id001.png
1   id002  http://example.com/img/id002.jpg
2   id003  http://example.com/img/id003.bmp
3   id004  http://example.com/img/id004.jpg
4   id005  http://example.com/img/id005.png

merge()函数将df1df2合并。首先,看一下这个函数可以接受的参数:

代码语言:javascript
复制
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
         left_index=False, right_index=False, sort=True,
         suffixes=('_x', '_y'), copy=True, indicator=False,
         validate=None)

leftright外,大多数参数都有默认值,这两个参数是我们要合并的DataFrames的名称。函数本身将返回一个新的DataFrame,用变量df3_merged引用。

代码语言:javascript
复制
df3_merged = pd.merge(df1, df2)

两个DataFrames都有一个同名的列user_id,所以 merge()函数会自动根据此列合并两个对象——此种情景可以称为在键user_id上合并。

如果有两个DataFrame没有相同名称的列,可以使用left_on='left_column_name'right_on='right_column_name'显式地指定两个DataFrames上的键。

打印df3_merged ,看看它的内容:

代码语言:javascript
复制
  user_id first_name last_name                  email                         image_url
0   id001       Rivi     Valti    rvalti0@example.com  http://example.com/img/id001.png
1   id002     Wynnie   McMurty  wmcmurty1@example.com  http://example.com/img/id002.jpg
2   id003    Kristos   Ivanets  kivanets2@example.com  http://example.com/img/id003.bmp
3   id004    Madalyn       Max      mmax3@example.com  http://example.com/img/id004.jpg
4   id005       Tobe   Riddich  triddich4@example.com  http://example.com/img/id005.png

你会注意到, df3_merged只有5行,而原来的df1有7行。为什么会这样?

how参数的默认值设置为inner时,将从左DataFrame和右DataFrame的交集生成一个新的DataFrame。因此,如果其中一个表中缺少user_id ,它就不会在合并的DataFrame中。

即使交换了左右行的位置,结果仍然如此。

解决方法,就是在使用merge()时,将参数 how的值设置为left

代码语言:javascript
复制
df_left_merge = pd.merge(df1, df2, how='left')

print(df_left_merge)

这就是所谓的“左联接”,这样得到了包含左DataFrame  (df1) 和右DataFrame (df2)的所有元素的DataFrame。运行上述代码显示以下内容:

代码语言:javascript
复制
  user_id first_name last_name                  email                         image_url
0   id001       Rivi     Valti    rvalti0@example.com  http://example.com/img/id001.png
1   id002     Wynnie   McMurty  wmcmurty1@example.com  http://example.com/img/id002.jpg
2   id003    Kristos   Ivanets  kivanets2@example.com  http://example.com/img/id003.bmp
3   id004    Madalyn       Max      mmax3@example.com  http://example.com/img/id004.jpg
4   id005       Tobe   Riddich  triddich4@example.com  http://example.com/img/id005.png
5   id006      Regan    Huyghe    rhuyghe@example.com                               NaN
6   id007    Kristin     Illis    killis4@example.com                               NaN

与左DataFrame没有任何匹配值的单元被填充为NaN

再试试“右联接”,创建以下的合并DataFrame:

代码语言:javascript
复制
df_right_merge = pd.merge(df1, df2, how='right')

print(df_right_merge)

如你所料,“右联接”将返回左DataFrame中与右DataFrame匹配的所有值:

代码语言:javascript
复制
  user_id first_name last_name                  email                         image_url
0   id001       Rivi     Valti    rvalti0@example.com  http://example.com/img/id001.png
1   id002     Wynnie   McMurty  wmcmurty1@example.com  http://example.com/img/id002.jpg
2   id003    Kristos   Ivanets  kivanets2@example.com  http://example.com/img/id003.bmp
3   id004    Madalyn       Max      mmax3@example.com  http://example.com/img/id004.jpg
4   id005       Tobe   Riddich  triddich4@example.com  http://example.com/img/id005.png

由于df2 中的每一行在df1中都有一个值,所以在本例中,right联接类似于inner联接。

让我们看一下 outer 联接。为了更好地说明它们是如何工作的,需要交换DataFrames的位置,并为“左联接”和“外联接”创建两个新变量:

代码语言:javascript
复制
df_left = pd.merge(df2, df1, how='left', indicator=True)
df_outer = pd.merge(df2, df1, how='outer', indicator=True)

print(df_left)
print(df_outer)

请记住,左边的DataFrame是df2,右边的DataFrame是df1。使用how='outer' 合并在键上匹配的DataFrames,但也包括丢失或不匹配的值。

在上面的示例中,还设置了参数 indicatorTrue,以便Pandas在DataFrame的末尾添加一个额外的_merge 列。此列告诉我们是否在左、右DataFrame或两个DataFrames中都找到相应的那一行。

df_left 如下所示:

代码语言:javascript
复制
  user_id                         image_url first_name last_name                  email _merge
0   id001  http://example.com/img/id001.png       Rivi     Valti    rvalti0@example.com   both
1   id002  http://example.com/img/id002.jpg     Wynnie   McMurty  wmcmurty1@example.com   both
2   id003  http://example.com/img/id003.bmp    Kristos   Ivanets  kivanets2@example.com   both
3   id004  http://example.com/img/id004.jpg    Madalyn       Max      mmax3@example.com   both
4   id005  http://example.com/img/id005.png       Tobe   Riddich  triddich4@example.com   both

然而, df_outer 有这些数据:

代码语言:javascript
复制
  user_id                         image_url first_name last_name                  email      _merge
0   id001  http://example.com/img/id001.png       Rivi     Valti    rvalti0@example.com        both
1   id002  http://example.com/img/id002.jpg     Wynnie   McMurty  wmcmurty1@example.com        both
2   id003  http://example.com/img/id003.bmp    Kristos   Ivanets  kivanets2@example.com        both
3   id004  http://example.com/img/id004.jpg    Madalyn       Max      mmax3@example.com        both
4   id005  http://example.com/img/id005.png       Tobe   Riddich  triddich4@example.com        both
5   id006                               NaN      Regan    Huyghe    rhuyghe@example.com  right_only
6   id007                               NaN    Kristin     Illis    killis4@example.com  right_only

请注意,在 df_outer中,“id006”和“id007”只存在于右DataFrame中(在本例中是df1)。如果在不交换位置的情况下比较左联接和外联接,最终会得到两个相同的结果。

方法2:join()

与Pandas函数merge() 不同,join()是DataFrame本身的方法,即:DataFrame.join(other, on=None, how='left', lsuffix='', rsuffix='', sort=False)

用来调用join() 方法的DataFrame是左DataFrame。other参数中的DataFrame是右DataFrame。

参数 on 参数的值可以用 ['key1', 'key2' ...] 来定义匹配的键;how 参数的值是 leftrightouterinner 等,默认为 left

下面将 df2 并入  df1

代码语言:javascript
复制
df_join = df1.join(df2, rsuffix='_right')

print(df_join)

merge()函数一样,join() 方法自动尝试匹配具有相同名称的键(列)。在上述示例中,它是user_id键。

上面的代码执行结果是:

代码语言:javascript
复制
  user_id first_name last_name                  email user_id_right                         image_url
0   id001       Rivi     Valti    rvalti0@example.com         id001  http://example.com/img/id001.png
1   id002     Wynnie   McMurty  wmcmurty1@example.com         id002  http://example.com/img/id002.jpg
2   id003    Kristos   Ivanets  kivanets2@example.com         id003  http://example.com/img/id003.bmp
3   id004    Madalyn       Max      mmax3@example.com         id004  http://example.com/img/id004.jpg
4   id005       Tobe   Riddich  triddich4@example.com         id005  http://example.com/img/id005.png
5   id006      Regan    Huyghe    rhuyghe@example.com           NaN                               NaN
6   id007    Kristin     Illis    killis4@example.com           NaN                               NaN

你可能注意到一个名为user_id_right的“复制列”。如果不想显示该列,可以将user_id 列设置为两列上的索引,以便在联接时不带后缀:

代码语言:javascript
复制
df_join_no_duplicates = df1.set_index('user_id').join(df2.set_index('user_id'))

print(df_join_no_duplicates)

这样做可以让我们摆脱user_id列,并将其设置为索引列,从而产生了一个更清晰的DataFrame:

代码语言:javascript
复制
        first_name last_name                  email                         image_url
user_id                                                                              
id001         Rivi     Valti    rvalti0@example.com  http://example.com/img/id001.png
id002       Wynnie   McMurty  wmcmurty1@example.com  http://example.com/img/id002.jpg
id003      Kristos   Ivanets  kivanets2@example.com  http://example.com/img/id003.bmp
id004      Madalyn       Max      mmax3@example.com  http://example.com/img/id004.jpg
id005         Tobe   Riddich  triddich4@example.com  http://example.com/img/id005.png
id006        Regan    Huyghe    rhuyghe@example.com                               NaN
id007      Kristin     Illis    killis4@example.com                               NaN

方法3:append()

正如Pandas官方文档所指出的,由于concat()append() 方法返回DataFrames的新副本,过度使用它可能会影响程序的性能。

这种追加的操作,比较适合于将一个DataFrame的每行合并到另外一个DataFrame的尾部,即得到一个新的DataFrame,它包含2个DataFrames的所有的行,而不是在它们的列上匹配数据。

df2 追加到 df1 并打印结果:

代码语言:javascript
复制
df_append = df1.append(df2, ignore_index=True)

print(df_append)

使用append()将不匹配任何键上的DataFrames ,它只将另一个DataFrame添加到第一个DataFrame并返回它的副本。如果这两个DataFrames 的形状不匹配,Pandas将用NaN替换任何不匹配的单元格。

代码语言:javascript
复制
   user_id first_name last_name                  email                         image_url
0    id001       Rivi     Valti    rvalti0@example.com                               NaN
1    id002     Wynnie   McMurty  wmcmurty1@example.com                               NaN
2    id003    Kristos   Ivanets  kivanets2@example.com                               NaN
3    id004    Madalyn       Max      mmax3@example.com                               NaN
4    id005       Tobe   Riddich  triddich4@example.com                               NaN
5    id006      Regan    Huyghe    rhuyghe@example.com                               NaN
6    id007    Kristin     Illis    killis4@example.com                               NaN
7    id001        NaN       NaN                    NaN  http://example.com/img/id001.png
8    id002        NaN       NaN                    NaN  http://example.com/img/id002.jpg
9    id003        NaN       NaN                    NaN  http://example.com/img/id003.bmp
10   id004        NaN       NaN                    NaN  http://example.com/img/id004.jpg
11   id005        NaN       NaN                    NaN  http://example.com/img/id005.png

方法4:concat()

concat()merge()join()相比,更灵活,因为它允许按行或按列组合DataFrames 。

以下是带参数的完整函数:

代码语言:javascript
复制
pandas.concat(objs, axis=0, join='outer', ignore_index=False, keys=None,
              levels=None, names=None, verify_integrity=False, sort=False, copy=True)

下面是 concat()函数最常用的参数:

  • objs:将要连接的DataFrame 对象([df1,df2,…])的列表
  • axis:定义连接的方向,0 表示0轴方向,即以行为单位链接;1 1轴方向,即以列为单位连接
  • join 的值可以是 inner (交集)或 outer(并集)
  • ignore_index:默认设置为 False ,即索引值为原有DataFrames中的状态,这可能会导致索引值重复。如果设置为 True ,它将忽略原始值并按顺序重新创建索引值
  • keys:用于设置多级索引,可以将它看作附加在DataFrame左外侧的索引的另一个层级的索引,它可以帮助我们在值不唯一时区分索引

用与 df2 相同的列类型创建一个新的DataFrame,但这个DataFrame包含id006id007image_url

代码语言:javascript
复制
df2_addition = pd.DataFrame({'user_id': ['id006', 'id007'],
                             'image_url': ['http://example.com/img/id006.png',
                                           'http://example.com/img/id007.jpg']
                             })

为了按行联接df2df2_addition,可以将它们作为objs参数传递到一个列表中,并将结果DataFrame赋给一个新变量:

代码语言:javascript
复制
df_row_concat = pd.concat([df2, df2_addition])

print(df_row_concat)

成功地填充了缺少的值:

代码语言:javascript
复制
  user_id                         image_url
0   id001  http://example.com/img/id001.png
1   id002  http://example.com/img/id002.jpg
2   id003  http://example.com/img/id003.bmp
3   id004  http://example.com/img/id004.jpg
4   id005  http://example.com/img/id005.png
0   id006  http://example.com/img/id006.png
1   id007  http://example.com/img/id007.jpg

不过,请看最左边一栏中的索引,存在索引“0”和“1”的重复。为了获得全新的唯一索引值,将True传给ignore_index参数:

代码语言:javascript
复制
df_row_concat = pd.concat([df2, df2_addition], ignore_index=True)

现在,df_row_concat具有唯一的索引值:

代码语言:javascript
复制
  user_id                         image_url
0   id001  http://example.com/img/id001.png
1   id002  http://example.com/img/id002.jpg
2   id003  http://example.com/img/id003.bmp
3   id004  http://example.com/img/id004.jpg
4   id005  http://example.com/img/id005.png
5   id006  http://example.com/img/id006.png
6   id007  http://example.com/img/id007.jpg

正如前面提到的,concat()可以在水平和竖直(0轴和1轴)方向上合并,要按列(即在1轴方向上合并)将两个DataFrames连接在一起,要将axis值从默认值0更改为1

代码语言:javascript
复制
df_column_concat = pd.concat([df1, df_row_concat], axis=1)

print(df_column_concat)

你会注意到,它的工作方式与merge不同,在一个键上匹配两个表:

代码语言:javascript
复制
  user_id first_name last_name                  email user_id                         image_url
0   id001       Rivi     Valti    rvalti0@example.com   id001  http://example.com/img/id001.png
1   id002     Wynnie   McMurty  wmcmurty1@example.com   id002  http://example.com/img/id002.jpg
2   id003    Kristos   Ivanets  kivanets2@example.com   id003  http://example.com/img/id003.bmp
3   id004    Madalyn       Max      mmax3@example.com   id004  http://example.com/img/id004.jpg
4   id005       Tobe   Riddich  triddich4@example.com   id005  http://example.com/img/id005.png
5   id006      Regan    Huyghe    rhuyghe@example.com   id006  http://example.com/img/id006.png
6   id007    Kristin     Illis    killis4@example.com   id007  http://example.com/img/id007.jpg

甚至于右边的DataFrame可以没有user_id列,也会得到类似上面的相同结果。函数concat()将两个DataFrames粘在一起,同时考虑DataFrames索引值和表格形状。它不会像merge()join()那样按键匹配。有兴趣的话,可以通过更改join参数的值尝试不同形式的组合,从而了解其差异!

方法5:combine_first()和update()

假设有一个DataFrame,但是它存在缺失数据,希望能够从另一个DataFrame中讲丢失的数据填充进来。这样,就要保留第一个DataFrame中的所有非缺失值,同时用第二个DataFrame可用的非缺失值(如果有这样的非缺失值)替换第一个DataFrame中的所有NaN

代码语言:javascript
复制
import numpy as np

df_first = pd.DataFrame({'COL 1': ['X', 'X', np.nan],
                         'COL 2': ['X', np.nan, 'X'],
                         'COL 3': [np.nan, 'X', 'X']},
                        index=range(0, 3))

df_second = pd.DataFrame({'COL 1': [np.nan, 'O', 'O'],
                          'COL 2': ['O', 'O', 'O']},
                         index=range(0, 3))

print(df_first)
print(df_second)

df_first 有3列,每列中有1个缺失值:

代码语言:javascript
复制
  COL 1 COL 2 COL 3
0     X     X   NaN
1     X   NaN     X
2   NaN     X     X

df_second只有2列,第一列中缺少一个值:

代码语言:javascript
复制
  COL 1 COL 2
0   NaN     O
1     O     O
2     O     O

下面用df_second中所有对应的值来填充df_first` 中缺失值:

代码语言:javascript
复制
df_tictactoe = df_first.combine_first(df_second)

print(df_tictactoe)

combine_first() 方法只会按索引顺序替换NaN值,并且会保留第一个DataFrame中所有非缺失的值:

代码语言:javascript
复制
  COL 1 COL 2 COL 3
0     X     X   NaN
1     X     O     X
2     O     X     X

另一方面,如果想用 df_second中相应的值(不管它们是否为NaN)覆盖df_first中的值,可以使用 update()方法。

再创建另一个DataFrame:

代码语言:javascript
复制
df_third = pd.DataFrame({'COL 1': ['O'], 'COL 2': ['O'], 'COL 3': ['O']})

print(df_third)

输出:

代码语言:javascript
复制
  COL 1 COL 2 COL 3
0     O     O     O

现在用df_third中的值更新df_first

代码语言:javascript
复制
df_first.update(df_third)

print(df_first)

请记住,与combine_first()不同,update()不会返回新的DataFrame,它原地修改df_first,更改相应的值:

代码语言:javascript
复制
  COL 1 COL 2 COL 3
0     O     O     O
1     X   NaN     X
2   NaN     X     X

update() 函数的 overwrite参数默认设置为True,这就是为什么它会更改所有相应的值,而不是只更改NaN值。如果将其更改为False,就仅替换NaN

代码语言:javascript
复制
df_tictactoe.update(df_first, overwrite=False)

print(df_tictactoe)

以下是df_tictactoeDataFrame的最终状态:

代码语言:javascript
复制
  COL 1 COL 2 COL 3
0     X     X     O
1     X     O     X
2     O     X     X

结论

Pandas为合并DataFrames提供了强大的工具,但很难确定死板的条条框框,来决定什么时候用什么函数。虽然大多数情况下,merge() 已经足够了,但在某些情况下,可能需要使用concat()来按行合并,或者使用join(),或者使用combine_first()update()来填充缺失值。甚至可以使用append()添加数据行。

总之,具体问题具体分析。

参考文献

[1]. https://stackabuse.com/how-to-merge-dataframes-in-pandas/

[2]. 跟老齐学Python:数据分析. 齐伟. 北京:电子工业出版社(与本书相关的资料:http://www.itdiffer.com/data.html)


点击“阅读原文”,查看有关资料

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-03-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老齐教室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 方法1:merge()
  • 方法2:join()
  • 方法3:append()
  • 方法4:concat()
  • 方法5:combine_first()和update()
  • 结论
  • 参考文献
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档