目录
- 前言
- 一、回顾Numpy数组的合并
- 二、concat方法合并DataFrame对象
- 三、append方法的使用
- 四、merge方法合并DataFrame对象
- (一)比较merge与concat
- (二)参数on、left_on和right_on的用法
- (三)合并时四种不同的连接规则
- (四)left_index与right_index参数的用法
- 五、join方法的使用
- 六、小结
前言
合并是指把两个甚至多个 DataFrame 对象连接起来,与合并相关的方法有四个:concat
,append
,merge
,join
。
它们的主要区别:
concat
支持多个 DataFrame 对象的水平和垂直排放,即可以列合并也可以行合并;但与merge
不同,它的合并不基于列值匹配。append
只能行合并,与concat
做行合并相比,形式更为简化。merge
的合并是列合并,合并时会基于列值匹配,类似于 SQL 语言的多表连接查询;merge
只能对两个 DataFrame 对象同时合并。join
也是列合并,但它的合并不是基于列值匹配而是基于行索引/列索引的匹配,特定情况下与concat
做列合并的效果相当。
import pandas as pd
import numpy as np
一、回顾Numpy数组的合并
Numpy 数组的合并使用np.concatenate()
方法。
1、多个一维列表合并的情形
x = [1, 2, 3]
y = [4, 5, 6]
z = [7, 8, 9]
np.concatenate([x, y, z]) # 因为x、y和z都是一维的,所以合并以后也是一维的
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
2、二维Numpy数组合并的情形
m = np.array([[1,2],[3,4]])
n = np.array([[5,6],[7,8]])
p = np.array([[9,10],[11,12]])
# 默认是按行方向合并,相当于axis=0
np.concatenate([m,n,p])
array([[ 1, 2],
[ 3, 4],
[ 5, 6],
[ 7, 8],
[ 9, 10],
[11, 12]])
np.vstack((m,n,p)) # 等价于np.concatenate([m,n,p],axis=0)
array([[ 1, 2],
[ 3, 4],
[ 5, 6],
[ 7, 8],
[ 9, 10],
[11, 12]])
如果按列合并,必须使用axis=1
参数。
np.concatenate([m,n,p],axis=1)
array([[ 1, 2, 5, 6, 9, 10],
[ 3, 4, 7, 8, 11, 12]])
np.hstack((m,n,p)) # 等价于np.concatenate([m,n,p],axis=1)
array([[ 1, 2, 5, 6, 9, 10],
[ 3, 4, 7, 8, 11, 12]])
二、concat方法合并DataFrame对象
np.concatenate
与pd.concat
最主要的差异就是 Pandas 合并时会保留索引,并且允许索引是重复的。
pd.concat
既可以行合并,也可以列合并;并且沿着哪个轴合并,合并对象上该轴的索引将全部保留;例如按行合并(对应于axis=0
),此时参与合并的所有 DataFrame 对象的行索引则全部保留,并且由上到下按序排列。
而另一轴的索引取决于join
参数是'outer'
还是'inner'
,前者做并集后者做交集;例如当按行合并(对应于axis=0
)时,另一轴的索引是指列索引,结果的列索引将由参与合并的所有 DataFrame 对象的列索引做并集(对应于join='outer'
)或做交集(对应于join='outer'
)而得到
pd.concat()
方法原型:
pd.concat(objs, axis=0, join='outer', ignore_index=False, keys=None, levels=None, names=None, verify_integrity=False, sort=False, copy=True)
objs
:要连接的 pandas 对象列表或字典。axis
:指定连接的轴,0 表示按行连接,1 表示按列连接。join
:指定连接方式,‘inner’ 表示取交集,‘outer’ 表示取并集。ignore_index
:如果为 True,则忽略原始索引,生成新的连续索引。keys
:使用层次化索引进行连接。levels
:如果 keys 参数被指定,则 levels 参数表示索引层级。names
:如果 keys 参数被指定,则 names 参数表示索引名称。verify_integrity
:如果为 True,则检查结果对象是否包含重复索引。sort
:如果为 True,则按索引进行排序。copy
:如果为 False,则不复制数据。
df1 = pd.DataFrame({'A':['A1','A2'],'B':['B1','B2'],'C':['C1','C2']},index=[1,2])
df1
df2 = pd.DataFrame({'A':['A3','A4'],'B':['B3','B4'],'D':['D3','D4']},index=[1,3])
df2
没有axis=0
或者明确给出axis=0
,都是按行合并,此时列索引取并集。
pd.concat([df1,df2])
明确给出axis=0
,则是按列合并,此时行索引取并集。
pd.concat([df1,df2],axis=1)
之所以上面两个例子都是并集,原因在于concat
的join
参数默认值是'outer'
,表示取并集(类似于SQL的外连接);而当明确指明join='inner'
时,将取交集计算结果(类似于SQL的内连接)。
当join='inner'
时,按行合并,则列索引取交集。
pd.concat([df1,df2],join='inner')
补充:ignore_index参数的使用:
ignore_index=True
会忽略原来的行索引,使用新的位置索引。
pd.concat([df1,df2],join='inner',ignore_index=True)
当join='inner'
时,按列合并,则行索引取交集。
pd.concat([df1,df2],axis=1,join='inner')
三、append方法的使用
append
可以把两个 DataFrame 对象按行合并,其功能等价于上面讲的pd.concat([df1,df2])
。
df.append()
方法原型:
DataFrame.append(other, ignore_index=False, verify_integrity=False, sort=None)
other
:可以是 DataFrame、Series 或者包含 DataFrame 或 Series 的列表,表示要附加到原始 DataFrame 的数据。ignore_index
:如果为 True,则忽略附加的数据的索引,并为结果 DataFrame 分配一个新的整数索引。默认为 False。verify_integrity
:如果为 True,则在附加操作之前检查结果 DataFrame 中的新索引是否唯一。如果新索引不唯一,则会引发 ValueError。默认为 False。sort
:如果为 True,则对结果 DataFrame 进行排序。默认为 None,表示不进行排序。
df1.append(df2)
如果要使用append
方法为df1
对象增加记录行,需要把要增加的记录行构成df2
,并且写成如下形式:df1=df1.append(df2)
。上面语句之所以要赋值,是因为 Pandas 中的append
不会直接修改原始的df1
对象。
四、merge方法合并DataFrame对象
(一)比较merge与concat
df3 = pd.DataFrame({'employee': ['Bob', 'Jake', 'Lisa', 'Sue'],
'group': ['Accounting', 'Engineering', 'Engineering', 'HR']})
df3
df4 = pd.DataFrame({'employee': ['Lisa', 'Bob', 'Jake', 'Sue'],
'hire_date': [2004, 2008, 2012, 2014]})
df4
下面的示例比较merge
与concat
,可以看出:
(1)merge
主要基于列值匹配而进行列合并,类似于SQL中的连接操作,而concat
并没有基于列值匹配进行合并。
(2)merge
中的两个合并对象只用逗号分隔,而concat
中的两个合并对象要构成列表。
一对一连接:在起连接作用的关键列(employee)上,通过列值匹配进行合并。
pd.merge(df3, df4)
concat
并没有基于两个employee
列的相同值匹配进行合并。
pd.concat([df3,df4],axis=1)
df5 = pd.DataFrame({'group': ['Accounting', 'Accounting',
'Engineering', 'Engineering', 'HR', 'HR'],
'skills': ['math', 'spreadsheets', 'coding', 'linux',
'spreadsheets', 'organization']})
df5
pd.merge()
方法原型:
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=('_x', '_y'), copy=True, indicator=False, validate=None)
left
:要合并的左侧 DataFrame。right
:要合并的右侧 DataFrame。how
:指定要使用的合并方法。可选值包括:
‘left’:保留左侧 DataFrame 中的所有行,并将右侧 DataFrame 中与左侧匹配的行合并到结果中。如果右侧 DataFrame 中没有匹配的行,则将 NaN 填充到结果中的相应位置。
‘right’:保留右侧 DataFrame 中的所有行,并将左侧 DataFrame 中与右侧匹配的行合并到结果中。如果左侧 DataFrame 中没有匹配的行,则将 NaN 填充到结果中的相应位置。
‘inner’:保留左右两侧 DataFrame 中都存在的行,并将它们合并到结果中。
‘outer’:保留左右两侧 DataFrame 中的所有行,并将它们合并到结果中。如果某一侧 DataFrame 中没有匹配的行,则将 NaN 填充到结果中的相应位置。on
:指定要合并的列(或列的名称)。如果两个 DataFrame 中的列名相同,并且没有指定该参数,则将这些列作为合并的键。如果要合并的列名不同,可以分别使用left_on
和right_on
参数指定左右两侧的列名。left_on
:指定左侧 DataFrame 中用作合并键的列。right_on
:指定右侧 DataFrame 中用作合并键的列。left_index
:如果为 True,则使用左侧 DataFrame 的索引作为合并键。right_index
:如果为 True,则使用右侧 DataFrame 的索引作为合并键。sort
:如果为 True,则对合并后的结果进行排序。suffixes
:如果在合并过程中遇到了重叠的列名,则添加到重叠列名的后缀。copy
:如果为 False,则不复制数据。默认为 True。indicator
:如果为 True,则在结果中添加一个名为 “_merge” 的列,指示每行的合并方式(如 “left_only”、“right_only”、“both”)。validate
:验证合并操作的类型。可选值包括:“one_to_one”、“one_to_many”、“many_to_one”、“many_to_many”。
(二)参数on、left_on和right_on的用法
使用on
参数显式设置起连接作用的关键列是两个 DataFrame 对象的group
列。
pd.merge(df3, df5,on='group')
当两个 DataFrame 的关键列的列名不同时,需要使用left_on
和right_on
参数实现列值匹配。
df6 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],
'salary': [70000, 80000, 120000, 90000]})
pd.merge(df3, df6, left_on="employee", right_on="name")
上面合并后employee
列和name
列是重复的,可以使用drop
方法删除多余的name
列。
pd.merge(df3, df6, left_on="employee", right_on="name") .drop('name',axis=1)
(三)合并时四种不同的连接规则
重新设置df3
包含空数据(NaN
)。
df3 = pd.DataFrame({'employee': ['Bob', 'Jake', 'Lisa', 'Sue','Tom'],
'group': ['Accounting', 'Engineering', 'Engineering', 'HR',np.NaN]})
df3 # 注意Tom目前没有所属部门
df7 = pd.DataFrame({'group': ['Accounting', 'Engineering', 'HR','Sale'],
'supervisor': ['Carly', 'Guido', 'Steve','Jack']})
df7
merge
默认的连接方式是'inner'
(即内连接),基于列值匹配时取交集,或者明确指明连接方式为how='inner'
,两个数据集能匹配上的记录才会出现在结果中。
pd.merge(df3,df7,how='inner') # 等价于pd.merge(df3,df7)
how='outer'
指明连接方式是外连接,此时基于列值匹配时取并集。两个数据集中不匹配的记录也都会出现在结果中(例如下面最后两条记录)。
pd.merge(df3,df7,how='outer')
how='left'
指明连接方式是左连接,此时基于列值匹配时会全部保留左边数据集的记录。而右边数据集中不匹配的记录则不会被合并到结果中。
注意:Sale
部门因为没有职员与之匹配,所以Sale
部门没有出现在结果中。
pd.merge(df3,df7,how='left')
how='right'
指明连接方式是右连接,此时基于列值匹配时会全部保留右边数据集的记录。而左边数据集中不匹配的记录则不会被合并到结果中。
注意:职员Tom
因为没有部门与之匹配,所以Tom
没有出现在结果中。
pd.merge(df3,df7,how='right')
(四)left_index与right_index参数的用法
merge
既可以合并列,也可以合并索引,下面先设置employee
为两个数据集df3
和df4
的索引,然后基于该索引进行合并。
df3 = df3.set_index('employee')
df3
df4 = df4.set_index('employee')
df4
基于索引合并就需要使用left_index
与right_index
参数。
注意:基于索引合并只取交集,而不能指定join
参数。
# pd.merge(df3,df4) # 不使用left_index与right_index参数会出错
pd.merge(df3,df4,left_index=True,right_index=True)
五、join方法的使用
df1
df2
df.join()
方法原型:
DataFrame.join(other, on=None, how='left', lsuffix='', rsuffix='', sort=False)
other
:要连接的另一个 DataFrame 或 Series 对象。on
:指定连接的列名或索引级别。如果为 None,则默认使用索引进行连接。how
:指定连接方式,默认为左连接(‘left’),可选值包括 ‘left’, ‘right’, ‘outer’, ‘inner’。lsuffix
:左侧 DataFrame 列名后缀,用于解决重叠的情况。rsuffix
:右侧 DataFrame 列名后缀,用于解决重叠的情况。sort
:如果为 True,则根据连接键对结果进行排序。
join
方法就是基于索引进行的列合并,如果两个数据集有重复的列名,需指定lsuffix
,rsuffix
参数。
join
方法默认是左连接(how='left'
),只保留左边的全部记录,对列除了加后缀不做处理,直接水平方向合并在一起。
df1.join(df2,lsuffix='_l', rsuffix='_r')
当连接方式how='outer'
时,等价于axis=1
时的concat
合并。
df1.join(df2,lsuffix='_l', rsuffix='_r',how='outer')
pd.concat([df1,df2],axis=1) # 与上面等价
六、小结
concat
默认的合并方式是行拼接,取并集(axis=0,join='outer'
);
merge
默认的合并方式是基于列值进行列拼接,取交集(how='inner'
);
join
默认的合并方式是基于行索引进行列合并,并且默认为左连接。