【Chapter 8.2】合并数据集
pandas里有几种方法可以合并数据:
- pandas.merge 按一个或多个key把DataFrame中的行连接起来。这个和SQL或其他一些关系型数据库中的join操作相似。
- pandas.concat 在一个axis(轴)上,串联或堆叠(stack)多个对象。
- combine_first 实例方法(instance method)能合并相互之间有重复的数据,并用一个对象里的值填满缺失值
这里每一个都会给出一些例子。这些用法贯穿这本书。
1 数据库风格的DataFrame Joins
Merge或join操作,能通过一个或多个key,把不同的数据集的行连接在一起。这种操作主要集中于关系型数据库。pandas中的merge函数是这种操作的主要切入点:
df2 = pd.DataFrame({'key': ['a', 'b', 'd'],
'data2': range(3)})
df2
Out[575]:
data2 key
0 0 a
1 1 b
2 2 d
df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1': range(7)})
df1
Out[577]:
data1 key
0 0 b
1 1 b
2 2 a
3 3 c
4 4 a
5 5 a
6 6 b
这个例子是many-to-one join(多个变为一个的连接);在df1中有标签为a和b的行,而df2中的key列,每一行只有对应的一个值。调用merge我们可以得到:
pd.merge(df1, df2)#没有指定连接键,默认用重叠列名,没有指定连接方式
Out[576]:
data1 key data2
0 0 b 1
1 1 b 1
2 6 b 1
3 2 a 0
4 4 a 0
5 5 a 0
这里我们并没有指定按哪一列来连接。如果我们没有指定,merge会用两个对象中都存在的列名作为key(键)。当然,最好还是清楚指定比较好:
pd.merge(df1, df2, on='key')
Out[578]:
data1 key data2
0 0 b 1
1 1 b 1
2 6 b 1
3 2 a 0
4 4 a 0
5 5 a 0
如果每一个对象中的列名不一会,我们可以分别指定:
df3 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1': range(7)})
df4 = pd.DataFrame({'rkey': ['a', 'b', 'd'],
'data2': range(3)})
df3
Out[582]:
data1 lkey
0 0 b
1 1 b
2 2 a
3 3 c
4 4 a
5 5 a
6 6 b
df4
Out[583]:
data2 rkey
0 0 a
1 1 b
2 2 d
pd.merge(df3, df4, left_on='lkey', right_on='rkey')
pd.merge(df3, df4, left_on='lkey', right_on='rkey')
Out[584]:
data1 lkey data2 rkey
0 0 b 1 b
1 1 b 1 b
2 6 b 1 b
3 2 a 0 a
4 4 a 0 a
5 5 a 0 a
我们可能注意到,在结果中并没有c和d。因为merge默认是inner join(内连接),结果中的key是交集的结果,或者在两个表格中都有的集合。其他一些可选项,比如left, right, outer。outer join(外连接)取key的合集,其实就是left join和right join同时应用的效果:
pd.merge(df1, df2, how='outer')
Out[585]:
data1 key data2
0 0.0 b 1.0
1 1.0 b 1.0
2 6.0 b 1.0
3 2.0 a 0.0
4 4.0 a 0.0
5 5.0 a 0.0
6 3.0 c NaN
7 NaN d 2.0
多对多的合并有些不直观。看下面的例子:
df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
'data1': range(6)})
df1
Out[586]:
data1 key
0 0 b
1 1 b
2 2 a
3 3 c
4 4 a
5 5 b
df2 = pd.DataFrame({'key': ['a', 'b', 'a', 'b', 'd'],
'data2': range(5)})
df2
Out[587]:
data2 key
0 0 a
1 1 b
2 2 a
3 3 b
4 4 d
pd.merge(df1, df2, on='key', how='left')
Out[588]:
data1 key data2
0 0 b 1.0
1 0 b 3.0
2 1 b 1.0
3 1 b 3.0
4 2 a 0.0
.. ... .. ...
6 3 c NaN
7 4 a 0.0
8 4 a 2.0
9 5 b 1.0
10 5 b 3.0
[11 rows x 3 columns]
many-to-many join是对行进行笛卡尔集运算。(两个集合X和Y的笛卡儿积(Cartesian product),又称直积,在集合论中表示为X × Y,是所有可能的有序对组成的集合。比如1到13是一个集合,四种花色是一个集合,二者的笛卡尔积就有52个元素)。这里在左侧的DataFrame中有三行含b,右边的DataFrame则有两行含b,于是结果是有六行含b。这个join方法只会让不相同的key值出现在最后的结果里:
pd.merge(df1, df2, how='inner')
Out[589]:
data1 key data2
0 0 b 1
1 0 b 3
2 1 b 1
3 1 b 3
4 5 b 1
5 5 b 3
6 2 a 0
7 2 a 2
8 4 a 0
9 4 a 2
用多个key来连接的话,用一个含有多个列名的list来指定:
left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'],
'key2': ['one', 'two', 'one'],
'lval': [1, 2, 3]})
right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'],
'key2': ['one', 'one', 'one', 'two'],
'rval': [4, 5, 6, 7]})
pd.merge(left, right, on=['key1', 'key2'], how='outer')
Out[591]:
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
哪一种key组合会出现在结果里取决于merge方法的选择,可以把多个key当做一个tuple组成的单一key(尽管实际上并不是这样)。
注意:当我们讲列和列进行连接时,DataFrame中的index对象会被丢弃。
最后一个问题是在做merge操作的时候,如何处理重叠的列名。当我们想要手动去解决重叠问题时(参考重命名axis labels的部分),merge有一个suffixes选项,能让我们指定字符串,添加重叠的列名到左、右DataFrame:
pd.merge(left, right, on='key1')
Out[592]:
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[593]:
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
indicator 添加特殊的列_merge,它可以指明每个行的来源,它的值有left_only、right_only或both,根据每行的合并数据的来源。
2 在index上做归并
在一些情况下,用于归并的key(键),可能是DataFrame中的index。这种情况下,可以使用left_index=True 或 right_index=True来指明,哪一个index被用来作为归并键:
left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],
'value': range(6)})
left1
Out[595]:
key value
0 a 0
1 b 1
2 a 2
3 a 3
4 b 4
5 c 5
right1 = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b'])
right1
Out[596]:
group_val
a 3.5
b 7.0
pd.merge(left1, right1, left_on='key', right_index=True)
Out[597]:
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
merge的默认方法是用key的交集,我们也可以设定用合集,即outer join:
pd.merge(left1, right1, left_on='key', right_index=True, how='outer')
Out[598]:
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
5 c 5 NaN
对于那些有多层级索引的数据,就更复杂了。index上的merge默认会是multiple-key merge(复数键归并):
lefth = pd.DataFrame({'key1': ['Ohio', 'Ohio', 'Ohio',
'Nevada', 'Nevada'],
'key2': [2000, 2001, 2002, 2001, 2002],
'data': np.arange(5.)})
lefth
Out[606]:
data key1 key2
0 0.0 Ohio 2000
1 1.0 Ohio 2001
2 2.0 Ohio 2002
3 3.0 Nevada 2001
4 4.0 Nevada 2002
righth = pd.DataFrame(np.arange(12).reshape((6, 2)),
index=[['Nevada', 'Nevada', 'Ohio', 'Ohio',
'Ohio', 'Ohio'],
[2001, 2000, 2000, 2000, 2001, 2002]],
columns=['event1', 'event2'])
righth
Out[607]:
event1 event2
Nevada 2001 0 1
2000 2 3
Ohio 2000 4 5
2000 6 7
2001 8 9
2002 10 11
在这个例子里,我们必须指明将多列归并作为一个list(注意处理重复index的方法是令how='outer'):
pd.merge(lefth, righth, left_on=['key1', 'key2'],
right_index=True)
Out[608]:
data key1 key2 event1 event2
0 0.0 Ohio 2000 4 5
0 0.0 Ohio 2000 6 7
1 1.0 Ohio 2001 8 9
2 2.0 Ohio 2002 10 11
3 3.0 Nevada 2001 0 1
pd.merge(lefth, righth, left_on=['key1', 'key2'],
right_index=True, how='outer')
Out[609]:
data key1 key2 event1 event2
0 0.0 Ohio 2000 4.0 5.0
0 0.0 Ohio 2000 6.0 7.0
1 1.0 Ohio 2001 8.0 9.0
2 2.0 Ohio 2002 10.0 11.0
3 3.0 Nevada 2001 0.0 1.0
4 4.0 Nevada 2002 NaN NaN
4 NaN Nevada 2000 2.0 3.0
同时使用两个对象里的index来归并也是可能的:
left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]],
index=['a', 'c', 'e'],
columns=['Ohio', 'Nevada'])
left2
Out[610]:
Ohio Nevada
a 1.0 2.0
c 3.0 4.0
e 5.0 6.0
right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]],
index=['b', 'c', 'd', 'e'],
columns=['Missouri', 'Alabama'])
right2
Out[611]:
Missouri Alabama
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[612]:
Ohio Nevada Missouri Alabama
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实例,可以直接用index来连接。这个也可以用于与其他DataFrame进行连接,要有一样的index但不能有重叠的列:
left2.join(right2, how='outer')
Out[615]:
Ohio Nevada Missouri Alabama
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
最后,对于简单的index-on-index连接,可以直接给join传入一个DataFrame。(作为备选,也可以使用最普遍的concat函数,这个在下一节会做介绍):
another = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]],
index=['a', 'c', 'e', 'f'],
columns=['New York', 'Oregon'])
another
Out[616]:
New York Oregon
a 7.0 8.0
c 9.0 10.0
e 11.0 12.0
f 16.0 17.0
left2.join([right2, another])
Out[617]:
Ohio Nevada Missouri Alabama New York Oregon
a 1.0 2.0 NaN NaN 7.0 8.0
c 3.0 4.0 9.0 10.0 9.0 10.0
e 5.0 6.0 13.0 14.0 11.0 12.0
left2.join([right2, another], how='outer')
Out[618]:
Ohio Nevada Missouri Alabama New York Oregon
a 1.0 2.0 NaN NaN 7.0 8.0
b NaN NaN 7.0 8.0 NaN NaN
c 3.0 4.0 9.0 10.0 9.0 10.0
d NaN NaN 11.0 12.0 NaN NaN
e 5.0 6.0 13.0 14.0 11.0 12.0
f NaN NaN NaN NaN 16.0 17.0
2 沿着轴串联
另一种结合方式被称为可互换的,比如concatenation, binding, or stacking(串联,绑定,堆叠)。Numpy中的concatenate函数可以作用于numpy 数组:
arr = np.arange(12.).reshape((3, 4))
arr
Out[621]:
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
np.concatenate([arr, arr], axis=1)
Out[622]:
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.]])
而在pandas的对象中,比如Series和DataFrame,labeled axes(便签化的轴)能让我们做更泛化的数组串联操作。不过我们可能会有下面一些疑问:
- 如果一个对象在其他轴上的index不同,我们应不应该在这些轴上把不同的元素合并起来,或者只用交集?
- 经过串联操作后,连接的部分在输出对象里应不应该是可被识别的?
- concatenation axis(串联轴)含有的数据需要被保留吗?在很多情况下,DataFrame中一些用整数做的label(标签)其实最好在串联后被删除。
pandas中的concat函数能解决上面这些问题。这里会给出几个例子来说明。假设我们有三个Series,他们指明没有index overlap(索引重叠):
s1 = pd.Series([0, 1], index=['a', 'b'])
s2 = pd.Series([2, 3, 4], index=['c', 'd', 'e'])
s3 = pd.Series([5, 6], index=['f', 'g'])
调用concat,把上面的series放在一个list里,结果会把值和索引都整合在一起:
pd.concat([s1, s2, s3])
Out[624]:
a 0
b 1
c 2
d 3
e 4
f 5
g 6
dtype: int64
默认情况下,concat中axis=0,结果会得到一个新的而series。如果令axis=1, 结果会变成一个DataFrame(axis=1 是列):
In [42]:
pd.concat([s1, s2, s3], axis=1)
Out[625]:
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中的outer join一样。你也可以通过设定join='inner'来使用交集:
s4 = pd.concat([s1, s3])
s4
Out[628]:
a 0
b 1
f 5
g 6
dtype: int64
pd.concat([s1, s4], axis=1)
Out[629]:
0 1
a 0.0 0
b 1.0 1
f NaN 5
g NaN 6
pd.concat([s1, s4], axis=1, join='inner')
Out[630]:
0 1
a 0 0
b 1 1
因为join='inner',所以f和g标签消失了。
你也可以在join_axes中指定使用哪些轴:
pd.concat([s1, s4], axis=1, join_axes=[['a', 'c', 'b', 'e']])
Out[631]:
0 1
a 0.0 0.0
c NaN NaN
b 1.0 1.0
e NaN NaN
一个潜在的问题是串联的部分在结果里是不可辨识的。假设我们想在串联轴上创建一个多层级索引,我们需要用到keys参数:
result = pd.concat([s1, s1, s3], keys=['one', 'two', 'three'])
result
Out[633]:
one a 0
b 1
two a 0
b 1
three f 5
g 6
dtype: int64
result.unstack()
Out[634]:
a b f g
one 0.0 1.0 NaN NaN
two 0.0 1.0 NaN NaN
three NaN NaN 5.0 6.0
如果是设定axis=1,那么keys会变为DataFrame的column header(列头):
print(s1)
print(s2)
print(s3)
a 0
b 1
dtype: int64
c 2
d 3
e 4
dtype: int64
f 5
g 6
dtype: int64
pd.concat([s1, s2, s3], axis=1, keys=['one', 'two', 'three'])
Out[636]:
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对象上:
df1 = pd.DataFrame(np.arange(6).reshape(3, 2), index=['a', 'b', 'c'],
columns=['one', 'two'])
df1
Out[637]:
one two
a 0 1
b 2 3
c 4 5
df2 = pd.DataFrame(5 + np.arange(4).reshape(2, 2), index=['a', 'c'],
columns=['three', 'four'])
df2
Out[638]:
three four
a 5 6
c 7 8
pd.concat([df1, df2], axis=1, keys=['level1', 'level2'])
Out[639]:
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
如果导入一个dict而不是list,那么dict的key会被用于上面concat中的keys选项:
pd.concat({'level1': df1, 'level2': df2}, axis=1)
Out[640]:
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
还有其他一些选项负责多层级索引的设定(表8-3)。比如,可以给创建的axis level(轴层级)用names参数来命名:
pd.concat([df1, df2], axis=1, keys=['level1', 'level2'],
names=['upper', 'lower'])
Out[641]:
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
最后我们关心的是,在DataFrame中,行索引(row index)没有包含相关的数据:
df1 = pd.DataFrame(np.random.randn(3, 4), columns=['a', 'b', 'c', 'd'])
df1
Out[642]:
a b c d
0 0.681009 0.974517 -0.736966 -0.902725
1 -0.049008 0.339831 -0.266341 -0.663389
2 0.128945 -0.181098 -0.170917 0.024014
df2 = pd.DataFrame(np.random.randn(2, 3), columns=['b', 'd', 'a'])
df2
Out[643]:
b d a
0 0.797382 0.533538 0.440353
1 0.268646 -0.330523 -1.575735
这种情况下,可以设置ignore_index=True:
pd.concat([df1, df2], ignore_index=True)
Out[644]:
a b c d
0 0.681009 0.974517 -0.736966 -0.902725
1 -0.049008 0.339831 -0.266341 -0.663389
2 0.128945 -0.181098 -0.170917 0.024014
3 0.440353 0.797382 NaN 0.533538
4 -1.575735 0.268646 NaN -0.330523
3 用重叠来合并数据
另一种数据合并方法既不属于merge,也不属于concatenation。比如两个数据集,index可能完全覆盖,或覆盖一部分。这里举个例子,考虑下numpy的where函数,可以在数组上进行类似于if-else表达式般的判断:
a = pd.Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan],
index=['f', 'e', 'd', 'c', 'b', 'a'])
a
Out[645]:
f NaN
e 2.5
d NaN
c 3.5
b 4.5
a NaN
dtype: float64
b = pd.Series(np.arange(len(a), dtype=np.float64),
index=['f', 'e', 'd', 'c', 'b', 'a'])
b
Out[646]:
f 0.0
e 1.0
d 2.0
c 3.0
b 4.0
a 5.0
dtype: float64
np.where(pd.isnull(a), b, a)
Out[647]: array([0. , 2.5, 2. , 3.5, 4.5, 5. ])
Series有一个combine_first方法,效果和上面是一样,而且还会自动对齐(比如把index按字母进行排列):
b[:-2].combine_first(a[2:])
Out[648]:
a NaN
b 4.5
c 3.0
d 2.0
e 1.0
f 0.0
dtype: float64
对于DataFrame, combine_first可以在列与列之间做到同样的事情,可以认为是用传递的对象,给调用对象中的缺失值打补丁:
df1 = pd.DataFrame({'a': [1., np.nan, 5., np.nan],
'b': [np.nan, 2., np.nan, 6.],
'c': range(2, 18, 4)})
df1
Out[649]:
a b c
0 1.0 NaN 2
1 NaN 2.0 6
2 5.0 NaN 10
3 NaN 6.0 14
df2 = pd.DataFrame({'a': [5., 4., np.nan, 3., 7.],
'b': [np.nan, 3., 4., 6., 8.]})
df2
Out[650]:
a b
0 5.0 NaN
1 4.0 3.0
2 NaN 4.0
3 3.0 6.0
4 7.0 8.0
df1.combine_first(df2)
Out[651]:
a b c
0 1.0 NaN 2.0
1 4.0 2.0 6.0
2 5.0 4.0 10.0
3 3.0 6.0 14.0
4 7.0 8.0 NaN