一、merge、join与concat的区别

Pandas.DataFrame操作表连接有三种方式:merge, join, concat。三者的使用很容易搅浑,三者之间的区别什么呢?

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

JOIN最适合的情况是基于行索引的合并,类似merge。不过也可以用于列的合并,只不过不建议,在一些情况下其操作可能等价于merge或concat。也多用于横向拼接。

concat方法相当于数据库中的全连接(UNION ALL),可以指定按某个轴进行连接,也可以指定连接的方式join(outer,inner 只有这两种)。与数据库不同的是concat不会去重,要达到去重的效果可以使用drop_duplicates方法。其一般多用于纵向连接。

二、merge

merge因为上一篇《pandas小结(六)merge数据合并》刚到过了,这里只再列一个示例,具体可以参考输出进行理解。

 1#coding=utf-8
 2from pandas import Series,DataFrame,merge
 3import numpy as np
 4data=DataFrame([{"id":0,"name":'lxh',"age":20,"cp":'lm'},{"id":1,"name":'xiao',"age":40,"cp":'ly'},{"id":2,"name":'hua',"age":4,"cp":'yry'},{"id":3,"name":'be',"age":70,"cp":'old'}])
 5data1=DataFrame([{"id":100,"name":'lxh','cs':10},{"id":101,"name":'xiao','cs':40},{"id":102,"name":'hua2','cs':50}])
 6data2=DataFrame([{"id":0,"name":'lxh','cs':10},{"id":101,"name":'xiao','cs':40},{"id":102,"name":'hua2','cs':50}])
 7print(data,'\n' + '-'*30 + '\n',data1,'\n' + '-'*30 + '\n',data2,'\n' + '-'*30 + '\n')
 8print("#单个列名做为内链接的连接键\n"+'-'*30 + '\n',merge(data,data1,on="name",suffixes=('_a','_b')))
 9print("#多列名做为内链接的连接键\n"+'-'* 30 + '\n',merge(data,data2,on=("name","id")))
10print('#不指定on则以两个DataFrame的列名交集做为连接键\r\n',merge(data,data2)) #这里使用了id与name
11#使用右边的DataFrame的行索引做为连接键
12##设置行索引名称
13indexed_data1=data1.set_index("name")
14print("#使用右边的DataFrame的行索引做为连接键\n"+'-'*30 + '\n',merge(data,indexed_data1,left_on='name',right_index=True))
15print('#左外连接\n'+'-'*30 + '\n',merge(data,data1,on="name",how="left",suffixes=('_a','_b')))
16print('#左外连接1\n'+'-'*30 + '\n',merge(data1,data,on="name",how="left"))
17print('#右外连接\n'+'-'*30 + '\n',merge(data,data1,on="name",how="right"))
18data3=DataFrame([{"mid":0,"mname":'lxh','cs':10},{"mid":101,"mname":'xiao','cs':40},{"mid":102,"mname":'hua2','cs':50}])
19#当左右两个DataFrame的列名不同,当又想做为连接键时可以使用left_on与right_on来指定连接键
20print("#使用left_on与right_on来指定列名字不同的连接键\n"+'-'*30 + '\n',merge(data,data3,left_on=["name","id"],right_on=["mname","mid"]))

三、join

join方法提供了一个简便的方法用于将两个DataFrame中的不同的列索引合并成为一个DataFrame,语法如下:

1join(self, other, on=None, how='left', lsuffix='', rsuffix='',sort=False)

其中参数的意义与merge方法基本相同,只是join方法默认为左外连接how=left。

JOIN 拼接列,主要用于基于行索引上的合并。只要两个表列名不同,不加任何参数就可以直接用。如果两个表有重复的列名,需指定lsuffix, rsuffix参数。其中参数的意义与merge方法基本相同,只是join方法默认为左外连接。列名重复的时候需要指定lsuffix, rsuffix参数。否则会报错。用法如下:

1how=df1.join(df2, lsuffix='_l', rsuffix='_r')

具体示例如下:

1#coding=utf-8
2from pandas import Series,DataFrame,merge
3data=DataFrame([{"id":0,"name":'lxh',"age":20,"cp":'lm'},{"id":1,"name":'xiao',"age":40,"cp":'ly'},{"id":2,"name":'hua',"age":4,"cp":'yry'},{"id":3,"name":'be',"age":70,"cp":'old'}],index=['a','b','c','d'])
4data1=DataFrame([{"sex":0},{"sex":1},{"sex":2}],index=['a','b','e'])
5print('#使用默认的左连接\n'+'-'*30 + '\n',data.join(data1))  #这里可以看出自动屏蔽了data中没有的index=e 那一行的数据
6print('#使用右连接\n'+'-'*30 + '\n',data.join(data1,how="right")) #这里出自动屏蔽了data1中没有index=c,d的那行数据;等价于data1.join(data)
7print('#使用内连接\n'+'-'*30 + '\n',data.join(data1,how='inner'))
8print('#使用全外连接\n+'-'*30 + '\n'',data.join(data1,how='outer'))

想用JOIN实现基于列索引的合并,也是完全可以的。列合并时,需要特别注意on参数的使用,其可能存在如下三种情况:

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

1、列名不同,列内容有相同

列名不同,列内容有相同,需要用到 l.join(r.set_index(key of r), on='key of l'),这种JOIN的写法等同于前面提到的merge设置left_on,right_on。具体代码如下:

1left = pd.DataFrame({'key1': ['foo', 'bar1'], 'lval': [1, 2]})
2right = pd.DataFrame({'key2': ['foo', 'bar'], 'rval': [4, 5]})
3left.join(right.set_index('key2'), on='key1')
4# 其等价于如下的代码:
5pd.merge(left, right,left_on='key1', right_on='key2')

不过需要注意的是,如果按上面的示例会发现输出结果是不一样的,其中join的输出是两条,merge的输出是一条,这是因为merge默认是内连接,所以返回的结果只有一行,而JOIN返回的结果是以左表的key列为准,有两行。所以想要完全一样,还城指定连接类型。

2、列名相同,内容部分相同

列名相同,内容部分相同,需要用到l.join(r.set_index(key), on='key')。这种JOIN的写法等同于前面提到的merge设置不带任何参数pd.merge(left, right),而且这种情况下merge会去掉重复的列。同样,因为merge默认是内连接,所以返回的结果只有一行,而JOIN返回的结果是以左表的key列为准,有两行。所以还是需要连接类型。

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

特别注意,即使列名相同了,也必须用到’set_index(key)’ ,如果不加,会导致被合并的right的后面的值显示为NaN。具体可以使用left.join(right,lsuffix='_l', rsuffix='_r')进行测试。

上面的这个操作同样也可以使用concat进行实现,无法是加个axis=1参数,即以列的维度进行合并。如下:

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

3、列名不同,内容也不同

这种情况是典型的行索引,不能用JOIN的ON参数进行列连接。

四、concat

concat 轴向连接。就是单纯地把两个表拼在一起,这个过程也被称作绑定(binding)或堆叠(stacking)。因此可以想见,这个函数的关键参数应该是 axis,用于指定连接的轴向。axis=1 在行中操作,axis=0是在列中操作。默认是axis=0,即垂直堆叠。如下的示例中,左表和右表没有一个单元格是一样的,只是按照行索引水平堆在了一起,所以使用concat、merge、join都可以实现,代码如下:

1df1=pd.DataFrame(np.random.randn(3,4),columns=['a','b','c','d'])
2df2=pd.DataFrame(np.random.randn(2,3),columns=['b','d','a'])
3pd.concat([df1, df2], axis=1) # 对行操作,相当于水平连接

其等价于如下两个操作:

1pd.merge(df1,df2,left_index=True,right_index=True,how='outer')
23df1.join(df2, lsuffix="_l")

更多内容可以参考pydata.org上的示例