pandas对象中得数据可以通过一些内置的方式进行合并:
——pandas.merge可根据一个或多个键将不同DataFrame中的行连接起来。SQL或其他关系型数据库的用户对此应该会比较熟悉,因为它实现的就是数据库的连接操作。
——pandas.concat可以沿着一条轴将多个对象堆叠到一起。
——combine_first可以将重复数据编接在一起,用一个对象中的值填充另一个对象中的缺失值。
(1)数据集的合并(merge)或连接(join)运算是通过一个或多个键将行链接起来的。
df1 = DataFrame({'key':['b','b','a','c','a','a','b'],'data1':range(7)})
df2 = DataFrame({'key':['a','b','d'],'data2':range(3)})
df1
Out[3]:
key data1
0 b 0
1 b 1
2 a 2
3 c 3
4 a 4
5 a 5
6 b 6
df2
Out[4]:
key data2
0 a 0
1 b 1
2 d 2
(2)这是一种多对一的合并。
df1中的数据有多个被标记为a和b的行,而df2中key列的每个值则仅对应一行。对这些对象调用merge即可得到:
pd.merge(df1,df2) #没有指明用哪个列连接时,merge就会被重叠列的列名当做键。
Out[6]:
key data1 data2
0 b 0 1
1 b 1 1
2 b 6 1
3 a 2 0
4 a 4 0
5 a 5 0
pd.merge(df1,df2,on='key') #最好显式指定一下
Out[7]:
key data1 data2
0 b 0 1
1 b 1 1
2 b 6 1
3 a 2 0
4 a 4 0
5 a 5 0
(3)如果两个对象的列名不同,也可以分别进行指定:
df3 = DataFrame({'1key':['b','b','a','c','a','a','b'],'data1':range(7)})
df4 = DataFrame({'rkey':['a','b','d'],'data2':range(3)})
pd.merge(df3,df4,left_on='1key',right_on='rkey')
Out[11]:
1key data1 rkey data2
0 b 0 b 1
1 b 1 b 1
2 b 6 b 1
3 a 2 a 0
4 a 4 a 0
5 a 5 a 0
(4)默认情况下,merge做的是inner连接,结果中的键是交集。其他方式还有“left”、“right”、“outer”,外连接是并集,组合了左连接和右连接的效果。多对多的合并操作非常简单,无需额外的工作。
df1 = DataFrame({'key':['b','b','a','c','a','b'],'data1':range(6)})
df2 = DataFrame({'key':['a','b','a','b','d'],'data2':range(5)})
df1
Out[13]:
key data1
0 b 0
1 b 1
2 a 2
3 c 3
4 a 4
5 b 5
df2
Out[14]:
key data2
0 a 0
1 b 1
2 a 2
3 b 3
4 d 4
pd.merge(df1,df2,on='key',how='left')
Out[15]:
key data1 data2
0 b 0 1.0
1 b 0 3.0
2 b 1 1.0
3 b 1 3.0
4 a 2 0.0
5 a 2 2.0
6 c 3 NaN
7 a 4 0.0
8 a 4 2.0
9 b 5 1.0
10 b 5 3.0
(5)多对多连接产生的是行的笛卡尔积。由于左边的DataFrame有3个“b”行,右边的有2个,所以最终结果中有6个“b”行。
left = DataFrame({'key1':['foo','foo','bar'],'key2':['one','two','one'],'lval':[1,2,3]})
right = DataFrame({'key1':['foo','foo','bar','bar'],'key2':['one','one','one','two'],'rval':[4,5,6,7]})
left
Out[18]:
key1 key2 lval
0 foo one 1
1 foo two 2
2 bar one 3
right
Out[19]:
key1 key2 rval
0 foo one 4
1 foo one 5
2 bar one 6
3 bar two 7
pd.merge(left,right,on=['key1','key2'],how='outer') #可以这样理解,多个键形成一系列元组,并将其当做单个连接键
Out[20]:
key1 key2 lval rval
0 foo one 1.0 4.0
1 foo one 1.0 5.0
2 foo two 2.0 NaN
3 bar one 3.0 6.0
4 bar two NaN 7.0
(6)对重复列名的处理
merge的suffixes选项,用于指定附加到左右两个DataFrame对象的重叠列名上的字符串:
pd.merge(left,right,on='key1')
Out[21]:
key1 key2_x lval key2_y rval
0 foo one 1 one 4
1 foo one 1 one 5
2 foo two 2 one 4
3 foo two 2 one 5
4 bar one 3 one 6
5 bar one 3 two 7
pd.merge(left,right,on='key1',suffixes=('_left','_right'))
Out[22]:
key1 key2_left lval key2_right rval
0 foo one 1 one 4
1 foo one 1 one 5
2 foo two 2 one 4
3 foo two 2 one 5
4 bar one 3 one 6
5 bar one 3 two 7
left | 参与合并的左侧DataFrame |
right | 参与合并的右侧DataFrame |
how | inner、outer、left、right其中一个。默认为inner |
on | 用于连接的列名。必须存在于左右两个DataFrame对象中。如果未指定,且其他连接键也未指定,则以left和right列名的交集作为连接键 |
left_on | 左侧DataFrame中用作连接键的列 |
right_on | 右侧DataFrame中用作连接键的列 |
left_index | 将左侧的行索引用作其连接键 |
right_index | 类似于left_index |
sort | 根据连接键对合并后的数据进行排序,默认为True。有时在处理大数据集时,禁用该选项可获得更好的性能。 |
suffixes | 字符串值元组,用于追加到重叠列名的末尾,默认为('_x','_y')。例如,如果左右两个DataFrame对象都有“data”,则结果中就会出现“data_x”和“data_y” |
copy | 设置为False,可以在某些特殊情况下避免将数据复制到结果数据结构中。默认总是复制。 |
(1)有时,DataFrame中的连接键位于其索引中,这时我们可以传入left_index=True或right_index=True以说明索引应该被用作连接键
left1 = DataFrame({'key':['a','b','a','a','b','c'],'value':range(6)})
right1 = DataFrame({'group_val':[3.5,7]},index=['a','b'])
left1
Out[24]:
key value
0 a 0
1 b 1
2 a 2
3 a 3
4 b 4
5 c 5
right1
Out[25]:
group_val
a 3.5
b 7.0
pd.merge(left1,right1,left_on='key',right_index=True)
Out[26]:
key value group_val
0 a 0 3.5
2 a 2 3.5
3 a 3 3.5
1 b 1 7.0
4 b 4 7.0
(2)由于默认的merge方法是求取连接键的交集,可通过外连接的方式得到它们的并集。对于层次化索引的数据,事情就有点复杂了
lefth = DataFrame({'key1':['O','O','O','N','N'],'key2':[2000,2001,2002,2001,2002],'data':np.arange(5.)})
righth = DataFrame(np.arange(12).reshape((6,2)),index=[['N','N','O','O','O','O'],[2001,2000,2000,2000,2001,2002]],columns=['e1','e2'])
lefth
Out[28]:
key1 key2 data
0 O 2000 0.0
1 O 2001 1.0
2 O 2002 2.0
3 N 2001 3.0
4 N 2002 4.0
righth
Out[29]:
e1 e2
N 2001 0 1
2000 2 3
O 2000 4 5
2000 6 7
2001 8 9
2002 10 11
这种情况下,必须以列表形式指明用作合并键的多个列。
pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True)
Out[3]:
key1 key2 data e1 e2
0 O 2000 0.0 4 5
0 O 2000 0.0 6 7
1 O 2001 1.0 8 9
2 O 2002 2.0 10 11
3 N 2001 3.0 0 1
pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True,how='outer')
Out[4]:
key1 key2 data e1 e2
0 O 2000 0.0 4.0 5.0
0 O 2000 0.0 6.0 7.0
1 O 2001 1.0 8.0 9.0
2 O 2002 2.0 10.0 11.0
3 N 2001 3.0 0.0 1.0
4 N 2002 4.0 NaN NaN
4 N 2000 NaN 2.0 3.0
同时使用合并双方的索引也没问题。
left2 = DataFrame([[1.,2.],[3.,4.],[5.,6.]],index=['a','c','e'],columns=['O','N'])
right2 = DataFrame([[7.,8.],[9.,10.],[11.,12.],[13,14]],index=['b','c','d','e'],columns=['M','A'])
left2
Out[6]:
O N
a 1.0 2.0
c 3.0 4.0
e 5.0 6.0
right2
Out[7]:
M A
b 7.0 8.0
c 9.0 10.0
d 11.0 12.0
e 13.0 14.0
pd.merge(left2,right2,how='outer',left_index=True,right_index=True)
Out[8]:
O N M A
a 1.0 2.0 NaN NaN
b NaN NaN 7.0 8.0
c 3.0 4.0 9.0 10.0
d NaN NaN 11.0 12.0
e 5.0 6.0 13.0 14.0
(3)join
①实现按索引合并。②用于合并多个带有相同或相似索引的DataFrame对象,而不管他们之间有没有重叠的列。
left2.join(right2,how='outer')
Out[9]:
O N M A
a 1.0 2.0 NaN NaN
b NaN NaN 7.0 8.0
c 3.0 4.0 9.0 10.0
d NaN NaN 11.0 12.0
e 5.0 6.0 13.0 14.0
DataFrame的join方法是在连接键上做左连接。
③它还支持参数DataFrame的索引跟调用者DataFrame的某个列之间的连接:
left1.join(right1,on='key')
Out[14]:
key value group_val
0 a 0 3.5
1 b 1 7.0
2 a 2 3.5
3 a 3 3.5
4 b 4 7.0
5 c 5 NaN
④对于简单的索引合并,还可以向join传入一组DataFrame。
another = DataFrame([[7.,8.],[9.,10.],[11.,12.],[16.,17.]],index=['a','c','e','f'],columns=['N','O'])
left2.join([right2,another],how='outer')
(1)另一种数据合并运算也被称为连接(concatenation)、绑定(binding)、堆叠(stacking)。NumPy有一个用于合并原始NumPy数组的Concatenation函数:
arr = np.arange(12).reshape((3,4))
arr
Out[19]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
np.concatenate([arr,arr],axis=1)
Out[20]:
array([[ 0, 1, 2, 3, 0, 1, 2, 3],
[ 4, 5, 6, 7, 4, 5, 6, 7],
[ 8, 9, 10, 11, 8, 9, 10, 11]])
(2)pandas的concat
s1 = Series([0,1],index=['a','b'])
s2 = Series([2,3,4],index=['c','d','e'])
s3 = Series([5,6],index=['f','g'])
①默认情况下,concat是在axis=0上工作的,最终产生一个新的Series。如果传入axis=1,会变成一个DataFrame(axis=1):
pd.concat([s1,s2,s3])
Out[23]:
a 0
b 1
c 2
d 3
e 4
f 5
g 6
dtype: int64
pd.concat([s1,s2,s3],axis=1)
Out[24]:
0 1 2
a 0.0 NaN NaN
b 1.0 NaN NaN
c NaN 2.0 NaN
d NaN 3.0 NaN
e NaN 4.0 NaN
f NaN NaN 5.0
g NaN NaN 6.0
②传入join='inner'得到它们的交集
s4 = pd.concat([s1*5,s3])
s4
Out[27]:
a 0
b 5
f 5
g 6
dtype: int64
pd.concat([s1,s4],axis=1)
Out[26]:
0 1
a 0.0 0
b 1.0 5
f NaN 5
g NaN 6
pd.concat([s1,s4],axis=1,join='inner')
Out[28]:
0 1
a 0 0
b 1 5
③join_axes指定要在其他轴上使用的索引
pd.concat([s1,s4],axis=1,join_axes=[['a','c','b','e']])
Out[29]:
0 1
a 0.0 0.0
c NaN NaN
b 1.0 5.0
e NaN NaN
④在连接轴创建一个层次化索引
参与连接的片段在结果中区分不开,可使用keys参数进行连接轴上的层次化索引
result = pd.concat([s1,s2,s3],keys=['one','two','three'])
result
Out[31]:
one a 0
b 1
two c 2
d 3
e 4
three f 5
g 6
dtype: int64
result.unstack()
Out[32]:
a b c d e f g
one 0.0 1.0 NaN NaN NaN NaN NaN
two NaN NaN 2.0 3.0 4.0 NaN NaN
three NaN NaN NaN NaN NaN 5.0 6.0
pd.concat([s1,s2,s3],axis=1,keys=['one','two','three']) #沿着axis=1对Series进行合并,则keys就会成为DataFrame的列头
Out[37]:
one two three
a 0.0 NaN NaN
b 1.0 NaN NaN
c NaN 2.0 NaN
d NaN 3.0 NaN
e NaN 4.0 NaN
f NaN NaN 5.0
g NaN NaN 6.0
同样的逻辑对DataFrame对象也是一样。
⑤如果传入的不是列表而是一个字典,则字典的键就会被当做keys选项的值:
pd.concat({'level1':df1,'level2':df2},axis=1)
Out[41]:
level1 level2
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
⑥用于管理层次化索引创建方式的参数names
pd.concat([df1,df2],axis=1,keys=['level1','level2'],names=['upper','lower'])
Out[43]:
upper level1 level2
lower one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
⑦去除无意义的行索引,重新索引
df1 = DataFrame(np.random.randn(3,4),columns=['a','b','c','d'])
df2 = DataFrame(np.random.randn(2,3),columns=['b','d','a'])
df1
Out[46]:
a b c d
0 0.178245 1.452040 2.204483 -0.468347
1 -0.644702 -0.003995 0.102923 -1.867448
2 -0.715122 -1.139546 0.493268 -0.148000
df2
Out[47]:
b d a
0 -0.293157 -0.195911 -0.478124
1 1.614451 0.498994 0.246414
pd.concat([df1,df2])
Out[48]:
a b c d
0 0.178245 1.452040 2.204483 -0.468347
1 -0.644702 -0.003995 0.102923 -1.867448
2 -0.715122 -1.139546 0.493268 -0.148000
0 -0.478124 -0.293157 NaN -0.195911
1 0.246414 1.614451 NaN 0.498994
pd.concat([df1,df2],ignore_index=True)
Out[49]:
a b c d
0 0.178245 1.452040 2.204483 -0.468347
1 -0.644702 -0.003995 0.102923 -1.867448
2 -0.715122 -1.139546 0.493268 -0.148000
3 -0.478124 -0.293157 NaN -0.195911
4 0.246414 1.614451 NaN 0.498994
objs | 参与连接的pandas对象的列表或字典。唯一必需的参数 |
axis | 指明连接的轴向,默认为0 |
join | “inner”、“outer”其中之一,默认为“outer”。指明其他轴向上的索引是按交集(inner)还是并集(outer)进行合并 |
join_axes | 指明用于其他n-1条轴的索引,不执行并集 |
keys | 与连接对象有关的值,用于形成连接轴向上的层次化索引。可以是任意值的列表或数组、元组数组、数组列表(如果将levels设置成多级数组的话) |
levels | 指定用作层次化索引各级别上的索引,如果设置了keys的话(就是外层级别的索引) |
names | 用于创建分层级别的名称,如果设置了keys和(或)levels的话 |
verify_integrity | 检查结果对象新轴上的重复情况,如果发现则引发异常。默认(False)允许重复 |
ignore_index | 不保留连接轴上的索引,产生一组新索引range(total_length) |
有时可能有索引全部或部分重叠的两个数据集,我们使用NumPy的where函数,用于表达一种矢量化的if-else:
a = Series([np.nan,2.5,np.nan,3.5,4.5,np.nan],index=['f','e','d','c','b','a'])
b = Series(np.arange(len(a),dtype=np.float64),index=['f','e','d','c','b','a'])
b[-1] = np.nan
a
Out[51]:
f NaN
e 2.5
d NaN
c 3.5
b 4.5
a NaN
dtype: float64
b
Out[52]:
f 0.0
e 1.0
d 2.0
c 3.0
b 4.0
a NaN
dtype: float64
np.where(pd.isnull(a),b,a)
Out[53]: array([0. , 2.5, 2. , 3.5, 4.5, nan])
Series有一个combine_first方法,实现的是一样的功能,而且会进行数据对齐:
b[:-2].combine_first(a[2:])
Out[54]:
a NaN
b 4.5
c 3.0
d 2.0
e 1.0
f 0.0
dtype: float64
对于DataFrame,combine_first会在列上做同样的事情,即用参数对象中的数据为调用者对象的缺失数据“打补丁”。