导读:在数据分析过程中,有时候需要将不同的数据文件进行合并处理。本文主要介绍三种数据合并方法。
Pandas提供了多功能、高性能的内存连接操作,本质上类似于SQL等关系数据库,比如,merge、join、concat等方法可以方便地将具有多种集合逻辑的Series或DataFrame数据合并、拼接在一起,用于实现索引和关系代数功能。
merge方法主要基于数据表共同的列标签进行合并,
join方法主要基于数据表的index标签进行合并,
concat方法是对数据表进行行拼接或列拼接。
01
merge方法
merge方法的主要应用场景是针对存在同一个或多个相同列标签(主键)的包含不同特征的两个数据表,通过主键的连接将这两个数据表进行合并。其语法格式如下:
result=pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
left_index=False, right_index=False,…)
常用的参数含义说明如下。
left/right
:参与合并的左/右侧的Series或DataFrame对象(数据表)。how
:数据合并的方式。默认为'inner'
,表示内连接(交集),'outer'
表示外连接(并集),'left'
表示基于左侧数据列的左连接,'right'
表示基于右侧数据列的右连接。on
:指定用于连接的列标签,可以是一个列标签,也可以是一个包含多个列标签的列表。默认为left
和right
中相同的列标签。left_on/right_on
:当left
和right
中合并的列标签名称不同时,用来分别指定左/右两表合并的列标签。left_index/right_index
:布尔类型,默认为False
。当设置为True
时,则以左/右侧的行标签作为连接键。
下面通过代码清单1演示merge方法的用法。
代码清单1 merge方法的用法示例1 import pandas as pd2 left = pd.DataFrame({'key1': ['K0', 'K1', 'K2'],'key2': ['K0', 'K1', 'K0'],'A': ['A0', 'A1', 'A2'],'B': ['B0', 'B1', 'B2']})3 right = pd.DataFrame({'key1': ['K0', 'K1', 'K2'],'key2': ['K0', 'K0','K0'],'C': ['C0', 'C1', 'C2',], 'D': ['D0', 'D1', 'D2']})4 print('left:\n',left)5 print('right:\n',right)6 result1 = pd.merge(left, right, on='key1') # 内连接7 print('根据key1列将left和right内连接:\n',result1)8 result2 = pd.merge(left, right, on=['key1', 'key2'])9 print('根据key1和key2列将left和right内连接:\n',result2)10 result3 = pd.merge(left, right, how='outer', on=['key1', 'key2'])11 print('根据key1和key2列将left和right外连接:\n',result3)12 result4 = pd.merge(left, right, how='left', on=['key1', 'key2'])13 print('根据key1和key2列将left和right左连接:\n',result4)14 result5 = pd.merge(left, right, how='right', on=['key1', 'key2'])15 print('根据key1和key2列将left和right右连接:\n',result5)
程序执行结束后,输出结果如下:
left:key1 key2 A B0 K0 K0 A0 B01 K1 K1 A1 B12 K2 K0 A2 B2right:key1 key2 C D0 K0 K0 C0 D01 K1 K0 C1 D12 K2 K0 C2 D2根据key1列将left和right内连接:key1 key2_x A B key2_y C D0 K0 K0 A0 B0 K0 C0 D01 K1 K1 A1 B1 K0 C1 D12 K2 K0 A2 B2 K0 C2 D2根据key1和key2列将left和right内连接:key1 key2 A B C D0 K0 K0 A0 B0 C0 D01 K2 K0 A2 B2 C2 D2根据key1和key2列将left和right外连接:key1 key2 A B C D0 K0 K0 A0 B0 C0 D01 K1 K1 A1 B1 NaN NaN2 K2 K0 A2 B2 C2 D23 K1 K0 NaN NaN C1 D1根据key1和key2列将left和right左连接:key1 key2 A B C D0 K0 K0 A0 B0 C0 D01 K1 K1 A1 B1 NaN NaN2 K2 K0 A2 B2 C2 D2根据key1和key2列将left和right右连接:key1 key2 A B C D0 K0 K0 A0 B0 C0 D01 K2 K0 A2 B2 C2 D22 K1 K0 NaN NaN C1 D1
下面对代码清单1中的代码做简要说明。
第2行代码通过字典创建了一个3行4列的DataFrame对象
left
,如第4行print
函数的输出结果所示。第3行代码通过字典创建了一个3行4列的DataFrame对象
right
,如第5行print
函数的输出结果所示。第6行代码通过
merge
方法将left
与right
合并,on='key1'
指定根据列标签key1
进行合并,合并方式默认为内连接,合并后的结果为一个3行7列的DataFrame对象,如第7行print
函数的输出结果所示。内连接是取
left
和right
的交集,由于left
和right
中key1
列的数据完全相同,因此保留了两个数据表中的所有行。除key1
之外,left
和right
中还存在另一个相同的列标签key2
,为了在合并后的对象中加以区分,Pandas自动将left
中的key2
重命名为key2_x
,right
中的key2
重命名为key2_y
。第8行代码通过
merge
方法将left
与right
合并,on=['key1', 'key2']
指定根据列标签key1
和key2
进行合并,合并方式默认为内连接,合并后的结果为一个2行6列的DataFrame对象,如第9行print
函数的输出结果所示。由于
left
和right
中key2
列数据不完全相同,因此要取left
和right
的交集,只将['key1', 'key2']
两列组合数据完全相同的行进行合并,即将第1行和第3行合并,并自动调整合并后DataFrame对象的index
。第10行代码通过
merge
方法将left
与right
合并,on=['key1', 'key2']
指定根据列标签key1
和key2
进行合并,how='outer'
指定合并方式为外连接,合并后的结果为一个4行6列的DataFrame对象,如第11行print
函数的输出结果所示。外连接是取
left
和right
的并集,['key1', 'key2']
两列组合数据对应的行都会进行合并。对于left
和right
中没有的列标签,要在对应位置设置NA,并自动调整合并后DataFrame对象的index
。第12行代码通过
merge
方法将left
与right
合并,on=['key1', 'key2']
指定根据列标签key1
和key2
进行合并,how='left'
指定合并方式为左连接,合并后的结果为一个3行6列的DataFrame对象,如第13行print
函数的输出结果所示。左连接是保留
left
的所有数据,只取right
中与left
的['key1', 'key2']
组合数据相同的行进行合并。对于left
中没有的列标签,要在对应位置设置NA,并自动调整合并后DataFrame对象的index
。第14行代码通过
merge
方法将left
与right
合并,on=['key1', 'key2']
指定根据列标签key1
和key2
进行合并,how='right'
指定合并方式为右连接,合并后的结果为一个3行6列的DataFrame对象,如第15行print
函数输出结果所示。右连接是保留
right
的所有数据,只取left
中与right的['key1', 'key2']
组合数据相同的行进行合并。对于right
中没有的列标签,要在对应位置设置NA,并自动调整合并后DataFrame对象的index
。
Tips
1)使用merge
合并两个数据表,如果左侧或右侧的数据表中没有某个列标签,则连接表中对应的值将设置为NA。
2)merge
方法不会修改原始数据表,而是生成一个合并后的副本。
02
join方法
Pandas还提供了一种基于index标签的快速合并方法——join方法。join连接数据的方法与merge一样,包括内连接、外连接、左连接和右连接。其语法格式如下:
result = data.join(other, on=None, how='left',…)
data
是一个Series或DataFrame对象(数据表)。other
:要合并的Series或DataFrame对象(数据表)。on
:可以是一个data
中的列标签,也可以是一个包含多个data
列标签的列表,表示other
要在data
的特定列上对齐。在实际应用中,如果other
的index
的值与data某一列的值相等,可以通过将other
的index
和data
中的特定列对齐进行合并,这类似于Excel中的VLOOKUP操作。how
:数据合并的方式。默认为'left'
,表示左连接,基于data
的index
标签进行连接;'right'
表示右连接,基于other
的index
标签进行连接;'inner'
表示内连接(交集);'outer'
表示外连接(并集)。
下面通过代码清单2演示join方法的用法。
代码清单2 join方法的用法示例1 import pandas as pd2 left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],'B': ['B0', 'B1', 'B2']}, index=['K0', 'K1', 'K2'])3 right = pd.DataFrame({'C': ['C0', 'C2', 'C3'], 'D': ['D0', 'D2', 'D3']}, index=['K0', 'K2', 'K3'])4 print('left:\n',left)5 print('right:\n',right)6 result1 = left.join(right)7 print('left和right左连接(join方法):\n',result1)8 result2 = pd.merge(left, right, left_index=True, right_index=True, how='left')9 print('left和right左连接(merge方法):\n',result2)10 result3 = left.join(right, how='inner')11 print('left和right内连接(join方法):\n',result3)12 result4 = pd.merge(left, right, left_index=True, right_index=True, how='inner')13 print('left和right内连接(merge方法):\n',result4)14 left2 = pd.DataFrame({'key': ['K0', 'K1', 'K0'],'A': ['A0', 'A1', 'A2'],'B': ['B0', 'B1', 'B2']})15 print('left2:\n',left2)16 result5 = left2.join(right,on='key')17 print('left2和right左连接(join方法):\n',result5)18 result6= pd.merge(left2, right, left_on='key', right_index=True, how='left');19 print('left2和right左连接(merge方法):\n',result6)
程序执行结束后,输出结果如下:
left:A BK0 A0 B0K1 A1 B1K2 A2 B2right:C DK0 C0 D0K2 C2 D2K3 C3 D3left和right左连接(join方法):A B C DK0 A0 B0 C0 D0K1 A1 B1 NaN NaNK2 A2 B2 C2 D2left和right左连接(merge方法):A B C DK0 A0 B0 C0 D0K1 A1 B1 NaN NaNK2 A2 B2 C2 D2left和right内连接(join方法):A B C DK0 A0 B0 C0 D0K2 A2 B2 C2 D2left和right内连接(merge方法):A B C DK0 A0 B0 C0 D0K2 A2 B2 C2 D2left2:key A B0 K0 A0 B01 K1 A1 B12 K0 A2 B2left2和right左连接(join方法):key A B C D0 K0 A0 B0 C0 D01 K1 A1 B1 NaN NaN2 K0 A2 B2 C0 D0left2和right左连接(merge方法):key A B C D0 K0 A0 B0 C0 D01 K1 A1 B1 NaN NaN2 K0 A2 B2 C0 D0
下面对代码清单2中的代码做简要说明。
第2行代码通过字典创建了一个3行2列的DataFrame对象
left
,index
被设置为['K0', 'K1', 'K2']
,如第4行print
函数的输出结果所示。第3行代码通过字典创建了一个3行2列的DataFrame对象
right
,index
被设置为['K0', 'K2', 'K3']
,如第5行print
函数的输出结果所示。第6行代码通过
join
方法将left
与right
合并,合并方式默认为基于left
的左连接,合并后的结果为一个3行4列的DataFrame对象,如第7行print
函数的输出结果所示。第8行代码通过
merge
方法将left
与right
合并,合并方式和结果与第6行代码相同,left_index和right_index
参数被设置为True,表示以left
和right
的index
行标签作为连接键,如第9行print
函数的输出结果所示。第10行代码通过
join
方法将left
与right
合并,how='inner'
指定合并方式为内连接,合并后的结果为一个2行4列的DataFrame对象,如第11行print
函数的输出结果所示。第12行代码通过
merge
方法将left
与right
合并,合并方式和结果与第10行代码相同,left_index
和right_index
参数被设置为True
,表示以left
和right
的index
行标签作为连接键,如第13行print
函数的输出结果所示。第14行代码通过字典创建了一个3行3列的DataFrame对象
left2
,没有设置index
参数,如第15行print
函数的输出结果所示。第16行代码通过
join
方法将left2
与right
合并,由于left2
与right
不具有相同的行标签,但是right
的index
与left2
的key
列有相同的数值,因此通过on='key'
指定将left2
中的key
与right
中的index
对齐,合并方式默认为左连接,合并后的结果为一个3行5列的DataFrame对象,如第17行print
函数的输出结果所示。第18行代码通过
merge
方法将left2
与right
合并,合并方式和结果与第16行代码相同,left_on='key'
表示表left
以key
列为连接键,right_index=True
表示表right
以index
行标签为连接键,how='left'
表示连接方式为左连接,如第19行print
函数的输出结果所示。
Tips
1)join方法实现的数据表合并也可以用merge方法实现,但join方法更简单、更快速。
2)join方法不会修改原始数据表,而是生成一个合并后的副本。
03
concat方法
concat方法的功能为沿着一个特定轴,对一组相同类型的Pandas对象执行连接操作。如果操作对象是DataFrame,还可以同时在其他轴上执行索引的可选集合逻辑操作(并集或交集)。concat方法接受一列或一组相同类型的对象,并通过一些可配置的处理将它们连接起来,这些处理可用于其他轴。其语法格式如下:
result=pd.concat(objs, axis=0, join='outer', ignore_index=False, keys=None,…)
常用的参数含义说明如下。
objs
是需要拼接的对象集合,一般为Series或DataFrame对象的列表或者字典。axis
表示连接的轴向,默认为0,表示纵向拼接,即基于列标签的拼接,拼接之后行数增加。axis=1
时表示横向拼接,即基于行标签的拼接,拼接之后列数增加。join
表示连接方式,默认为'outer'
,拼接方法为外连接(并集)。join='inner'
时,拼接方法为内连接(交集)。ignore_index
是布尔类型,默认为False
,表示保留连接轴上的标签。如果将其设置为True
,则不保留连接轴上的标签,而是产生一组新的标签。keys
是列表类型。如果连接轴上有相同的标签,为了区分,可以用keys在最外层定义标签的分组情况,形成连接轴上的层次化索引。
下面通过代码清单3演示concat方法的用法。
代码清单3 concat方法的用法示例1 import pandas as pd2 df1 = pd.DataFrame({'A':['A0','A1','A2'], 'B':['B0','B1','B2'], 'C':['C0','C1','C2'], 'D':['D0','D1','D2']}, index=[0,1,2])3 df2 = pd.DataFrame({'A':['A3','A4', 'A5'], 'B':['B3','B4', 'B5'],'C': ['C3','C4', 'C5'], 'D':['D3','D4', 'D5']}, index=[3,4,5])4 df3 = pd.DataFrame({'A':['A6','A7','A8'], 'B':['B6','B7','B8'], 'C':['C6','C7','C8'], 'D':['D6','D7','D8']}, index=[6,7,8])5 df4 = pd.DataFrame({'B':['B2','B3','B6'], 'D':['D2','D3','D6'], 'F':['F2','F3','F6']}, index=[2,3,6])6 result1 = pd.concat([df1,df2,df3])7 print('df1、df2和df3纵向外拼接:\n',result1)8 result2=pd.concat([df1,df2],axis=1,keys=['df1','df2'])9 print('df1和df2横向外拼接(concat方法):\n',result2)10 result3=df1.join(df2, how='outer',lsuffix='_df1',rsuffix='_df2')11 print('df1和df2横向外拼接(join方法):\n',result3)12 result4=pd.concat([df1,df3])13 print('df1和df3纵向外拼接:\n',result4)14 result5=pd.concat([df1,df3],ignore_index=True)15 print('df1和df3纵向外拼接并生成新的行标签:\n',result5)16 result6=pd.concat([result1,df4], axis=1, join='inner', keys = ['result1','df2'])17 print('result1和df4横向内拼接:\n',result6)
程序执行结束后,输出结果如下:
df1、df2和df3纵向外拼接:A B C D0 A0 B0 C0 D01 A1 B1 C1 D12 A2 B2 C2 D23 A3 B3 C3 D34 A4 B4 C4 D45 A5 B5 C5 D56 A6 B6 C6 D67 A7 B7 C7 D78 A8 B8 C8 D8df1和df2横向外拼接(concat方法):df1 df2A B C D A B C D0 A0 B0 C0 D0 NaN NaN NaN NaN1 A1 B1 C1 D1 NaN NaN NaN NaN2 A2 B2 C2 D2 NaN NaN NaN NaN3 NaN NaN NaN NaN A3 B3 C3 D34 NaN NaN NaN NaN A4 B4 C4 D45 NaN NaN NaN NaN A5 B5 C5 D5df1和df2横向外拼接(join方法):A_df1 B_df1 C_df1 D_df1 A_df2 B_df2 C_df2 D_df20 A0 B0 C0 D0 NaN NaN NaN NaN1 A1 B1 C1 D1 NaN NaN NaN NaN2 A2 B2 C2 D2 NaN NaN NaN NaN3 NaN NaN NaN NaN A3 B3 C3 D34 NaN NaN NaN NaN A4 B4 C4 D45 NaN NaN NaN NaN A5 B5 C5 D5df1和df3纵向外拼接:A B C D0 A0 B0 C0 D01 A1 B1 C1 D12 A2 B2 C2 D26 A6 B6 C6 D67 A7 B7 C7 D78 A8 B8 C8 D8df1和df3纵向外拼接并生成新的行标签:A B C D0 A0 B0 C0 D01 A1 B1 C1 D12 A2 B2 C2 D23 A6 B6 C6 D64 A7 B7 C7 D75 A8 B8 C8 D8result1和df4横向内拼接:A B C D B D F2 A2 B2 C2 D2 B2 D2 F23 A3 B3 C3 D3 B3 D3 F36 A6 B6 C6 D6 B6 D6 F6
下面对代码清单3中的代码做简要说明。
第2~5行代码分别通过字典创建了4个3行4列的DataFrame对象
df1
、df2
、df3
、df4
,index
分别被设置为[0,1,2]
、[3,4,5]
、[6,7,8]
、[2,3,6]
。第6行代码通过
concat
方法将df1
、df2
和df3
拼接,采用默认的参数设置,即纵向外拼接。由于df1、df2和df3的列标签完全相等,但行标签没有重叠的部分,拼接后的结果为一个9行4列的DataFrame对象,如第7行print
函数的输出结果所示。第8行代码通过
concat
方法将df1
和df2
拼接,axis=1
表示横向拼接,拼接方式默认为外拼接。由于df1
和df2
的列标签完全相等,拼接后的列会有重复的列标签。为了便于区分,设置参数keys=['df1','df2']
在最外层定义标签的分组情况,df1
的列标签的外层索引为'df1'
,df2
的列标签的外层索引为'df2'
。拼接后的结果为一个6行8列的DataFrame对象,如第9行print
函数的输出结果所示。第10行代码通过
join
方法将df1
和df2
拼接,拼接方式与第8行代码相同。how='outer'
设置为外拼接,为了区分拼接后的对象中重复的列标签,设置lsuffix='_df1'
,指定df1
的列名加上后缀'_df1'
;设置rsuffix='_df2'
,指定df2
的列名加上后缀'_df2'
,如第11行print
函数的输出结果所示。可以看到,result3
中的元素数据与result2
相同,不同之处在于result2
采用外层索引的方式区分重复列,而result3
采用列名加后缀的方法。第12行代码通过
concat
方法将df1
和df3
拼接,采用默认的参数设置,即纵向外拼接。拼接后的结果为一个6行4列的DataFrame对象,如第13行print
函数的输出结果所示,可以看到result3
的行标签完全保留了df1
和df3
的行标签。第14行代码在第12行代码的基础上,增加了参数设置
ignore_index=True
,表示会重新生成新的整数序列作为拼接后的DataFrame对象的行标签,如第15行print
函数的输出结果所示。第16行代码通过
concat
方法将第6行代码的result1
和df4
拼接,axis=1
表示横向拼接,join='inner'
指定内拼接。拼接后的结果为一个3行7列的DataFrame对象,如第17行print
函数的输出结果所示,保留了result1
和df4
中相同的行标签。
Tips
1)在实际应用中,join方法常用于基于行标签对数据表的列进行拼接,concat方法则常用于基于列标签对数据表的行进行拼接。
2)concat方法不会修改原始数据表,而是生成一个合并后的副本。
本文摘编于《Python数据分析与应用》
本书基于作者多年来的课程教学经验和利用Python进行数据分析的工程经验编写而成,面向数据分析的初学者,使其具备利用Python开展数据分析工作、解决各专业问题的思维和能力。高校计算机、大数据、人工智能及其他相关专业均可使用本书作为数据分析课程教材。
扫码关注【华章计算机】视频号
每天来听华章哥讲书
更多精彩回顾
书讯 | 9月书讯 | 秋天的第一本书,来了
资讯 | DB-Engines 9月数据库排名:SnowFlake坐上了火箭
书单 | 你们要的Java学习路线图来了
干货 | 火山引擎 A/B 测试的思考与实践
收藏 | 两本书助你构建智能计算系统知识树
上新 | 【新书速递】Julia设计模式
赠书 | 【第74期】安全工程师最详细学习和职业规划路线(书籍推荐和导图下载)
点击阅读全文购买