Pandas DataFrame连接表 几种连接方法的对比


Pandas.DataFrame操作表连接有三种方式:merge, join, concat。下面就来说一说这三种方式的特性和用法。

先看两张表:

 

Screen Shot 2018-08-31 at 2.24.44 PM.png

 

Screen Shot 2018-08-31 at 2.24.52 PM.png

  1. merge。相当于SQL中的JOIN。该函数的典型应用场景是,两张表有相同内容的列(即SQL中的键),现在我们想把两张表整合到一张表里。在此典型情况下,结果集的行数并没有增加,列数则为两个元数据的列数和减去连接键的数量。

1.1 参数说明:

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)

left与right:两个不同的DataFrame
how:指的是合并(连接)的方式有inner(内连接),left(左外连接),right(右外连接),outer(全外连接);默认为inner!
on : 指的是用于连接的列索引名称。必须存在右右两个DataFrame对象中,如果没有指定且其他参数也未指定则以两个DataFrame的列名交集做为连接键
left_on:左则DataFrame中用作连接键的列名;这个参数中左右列名不相同,但代表的含义相同时非常有用。
right_on:右则DataFrame中用作 连接键的列名
left_index:使用左则DataFrame中的行索引做为连接键,用到这个参数时,就有点类似于接下来要说的JOIN函数了。
right_index:使用右则DataFrame中的行索引做为连接键
sort:默认为True,将合并的数据进行排序。在大多数情况下设置为False可以提高性能
suffixes:字符串值组成的元组,用于指定当左右DataFrame存在相同列名时在列名后面附加的后缀名称,默认为('_x','_y')
copy:默认为True,总是将数据复制到数据结构中;大多数情况下设置为False可以提高性能
indicator:在 0.17.0中还增加了一个显示合并数据中来源情况;如只来自己于左边(left_only)、两者(both)

1.2 merge的特征
1.2.1 默认以重叠列名当做链接键
1.2.2 默认是INNER JOIN。
1.2.3 可以多键连接,'on'参数后传入多键列表即可
1.2.4 如果两个对象的列表不同,可以用left_on, right_on指定。
1.2.5 也可以用行索引当连接键,使用参数left_index=True, right_index=True. 但是这种情况下最好用JOIN

现在来看例子:
开头介绍的两张表除了列名有重叠,内容并没有重叠的地方,所以并不是典型的merge场景。但是,用merge能不能合并呢?也可以。
如果用merge:pd.merge(df1, df2),会得到一张空表
必须指定行索引参数left_index, right_index:

pd.merge(df1, df2, left_index=True, right_index=True, how='left')

Screen Shot 2018-08-31 at 2.30.56 PM.png

 

这种是「非典型」应用,这种表的场景,更多的时候我们用JOIN函数来实现:

  1. JOIN 拼接列,主要用于基于行索引上的合并。
  • 只要两个表列名不同,不加任何参数就可以直接用。
  • 如果两个表有重复的列名,需指定lsuffix, rsuffix参数。
  • 其中参数的意义与merge方法基本相同,只是join方法默认为左外连接how=left
df1.join(df2, lsuffix='_l', rsuffix='_r') # 列名重复的时候需要指定lsuffix, rsuffix参数

Screen Shot 2018-08-31 at 3.10.52 PM.png

JOIN最适合的情况是基于行索引,上述例子因为列名有重复(即使内容没有重复),所以必须在JOIN的时候设置lsuffix, rsuffix参数,否则会报错。
如果列名不重复,则直接用' df1.join(df2) '即可。

但是! 如果我们想用JOIN实现基于列索引的合并,也是完全可以的。请注意以下的讨论全是关于用JOIN实现列索引合并的,即如何正确使用JOIN函数中的「ON」参数。
用JOIN实现基于列索引的合并主要考虑三种情况:

  • 列名不同,列内容有相同
  • 列名相同,列内容有相同
  • 列名不同,列内容也不同

(1) 列名不同,列内容有相同,需要用到 l.join(r.set_index(key of r), on='key of l')


left = pd.DataFrame({'key1': ['foo', 'bar1'], 'lval': [1, 2]})
right = pd.DataFrame({'key2': ['foo', 'bar'], 'rval': [4, 5]})
left.join(right.set_index('key2'), on='key1')

Screen Shot 2018-08-31 at 4.05.30 PM.png

 

这种JOIN的写法等同于前面提到的merge设置left_on,right_on。

pd.merge(left, right,left_on='key1', right_on='key2') # 列名不同,但内容有相同可以当键

Screen Shot 2018-08-31 at 4.10.38 PM.png

 

因为merge默认是内连接,所以返回的结果只有一行,而JOIN返回的结果是以左表的key列为准,有两行。

(2)列名相同,内容有相同,需要用到l.join(r.set_index(key), on='key')。

left = pd.DataFrame({'key': ['foo', 'bar1'], 'lval': [1, 2]})
right = pd.DataFrame({'key': ['foo', 'bar'], 'rval': [4, 5]})
left.join(right.set_index('key'), on='key',lsuffix='_l', rsuffix='_r')

Screen Shot 2018-08-31 at 4.18.25 PM.png

 

这种JOIN的写法等同于前面提到的merge设置不带任何参数,而且这种情况下merge会去掉重复的列

pd.merge(left, right) # 列名不同,但内容有相同,所以依然可以作为键来用

Screen Shot 2018-08-31 at 4.20.42 PM.png

 

同样,因为merge默认是内连接,所以返回的结果只有一行,而JOIN返回的结果是以左表的key列为准,有两行。

特别注意,即使列名相同了,也必须用到' set_index(key)' 否则连接集会显示如下:

left.join(right,on='key',lsuffix='_l', rsuffix='_r')

Screen Shot 2018-08-31 at 4.34.59 PM.png

另外值得注意的一点,不指定'ON= '参数的情况下,JOIN是按行索引连接,也就是简单的水平连接两个表,不对列进行任何操作。如下代码返回的结果:

left.join(right,lsuffix='_l', rsuffix='_r')

Screen Shot 2018-08-31 at 4.25.30 PM.png

 

这个结果其实和用concat进行行操作是一模一样的:

pd.concat([left, right], axis=1)

(3)列名不同,内容也不同
这种情况是典型的行索引,不能用JOIN的ON参数进行列连接。

  1. concat 轴向连接。就是单纯地把两个表拼在一起,这个过程也被称作绑定(binding)或堆叠(stacking)。因此可以想见,这个函数的关键参数应该是 axis,用于指定连接的轴向。axis=1 在行中操作,axis=0是在列中操作。
    默认是axis=0,即垂直堆叠。
df1=pd.DataFrame(np.random.randn(3,4),columns=['a','b','c','d'])
df2=pd.DataFrame(np.random.randn(2,3),columns=['b','d','a'])
pd.concat([df1, df2], axis=1) # 对行操作,相当于水平连接

注意到这里,左表和右表没有一个单元格是一样的,只是按照行索引水平堆在了一起,所以可以理解为相当于

pd.merge(df1,df2,left_index=True,right_index=True,how='outer') 

或者

df1.join(df2, lsuffix="_l")

效果都是生成这样一张表

 

Screen Shot 2018-08-31 at 1.24.10 PM.png

最后看看CONCAT的垂直堆叠。垂直堆叠就是axis=0,这种情况下有个参数比较特殊,叫' ignore_index= ',默认情况下是False。如果设成了True,就是把结果的合并表重新编排行索引。否则,行索引还是原来两个表里的值,比如"0,1,2,0,1"

pd.concat([df1, df2], axis=0, ignore_index=True)

Screen Shot 2018-08-31 at 1.26.39 PM.png

如果两张表的列名都不相同,垂直堆叠会生扩展不同的列,生成一张更宽的表。