pandas里有几种方法可以合并数据:
这里每一个都会给出一些例子。这些用法贯穿这本书。
Merge或join操作,能通过一个或多个key,把不同的数据集的行连接在一起。这种操作主要集中于关系型数据库。pandas中的merge函数是这种操作的主要切入点:
import pandas as pd
import numpy as np
df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1': range(7)})
df1
df2 = pd.DataFrame({'key': ['a', 'b', 'd'],
'data2': range(3)})
df2
这个例子是many-to-one join(多个变为一个的结合);在df1中有标签为a和b的行,而df2中的key列,每一行只有对应的一个值。调用merge我们可以得到:
pd.merge(df1, df2)
这里我们并没有指定按哪一列来合并。如果我们没有指定,merge会用两个对象中都存在的列名作为key(键)。当然,最好还是清楚指定比较好:
pd.merge(df1, df2, on='key')
如果每一个对象中的列名不一会,我们可以分别指定:
df3 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1': range(7)})
df4 = pd.DataFrame({'rkey': ['a', 'b', 'd'],
'data2': range(3)})
pd.merge(df3, df4, left_on='lkey', right_on='rkey')
我们可能注意到,在结果中并没有c和d。因为merge默认是inner join(内联结),结果中的key是交集的结果,或者在两个表格中都有的集合。其他一些可选项,比如left, right, outer。outer join(外联结)取key的合集,其实就是left join和right join同时应用的效果:
pd.merge(df1, df2, how='outer')
many-to-many(多对多)结合也被定义好了,不过可能不是那么直观。这里有一个例子:
df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
'data1': range(6)})
df1
df2 = pd.DataFrame({'key': ['a', 'b', 'a', 'b', 'd'],
'data2': range(5)})
df2
pd.merge(df1, df2, on='key', how='left')
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')
用多个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')
哪一种key组合会出现在结果里取决于merge方法的选择,可以把多个key当做一个tuple组成的单一key(尽管实际上并不是这样)。
注意:当我们讲列和列进行联结时,DataFrame中的index对象会被丢弃。
最后一个问题是在做merge操作的时候,如何处理重叠的列名。当我们想要手动去解决重叠问题时(参考重命名axis labels的部分),merge有一个suffixes选项,能让我们指定字符串,添加重叠的列名到左、右DataFrame:
pd.merge(left, right, on='key1')
pd.merge(left, right, on='key1', suffixes=('_left', '_right'))
在一些情况下,用于合并的key(键),可能是DataFrame中的index。这种情况下,可以使用left_index=True 或 right_index=True来指明,哪一个index被用来作为合并键:
left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],
'value': range(6)})
left1
right1 = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b'])
right1
pd.merge(left1, right1, left_on='key', right_index=True)
merge的默认方法是用key的交集,我们也可以设定用合集,即outer join:
pd.merge(left1, right1, left_on='key', right_index=True, how='outer')
对于那些有多层级索引的数据,就更复杂了。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
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
在这个例子里,我们必须指明将多列合并作为一个list(注意处理重复index的方法是令how='outer'):
pd.merge(lefth, righth, left_on=['key1', 'key2'],
right_index=True)
pd.merge(lefth, righth, left_on=['key1', 'key2'],
right_index=True, how='outer')
同时使用两个对象里的index来合并也是可能的:
left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]],
index=['a', 'c', 'e'],
columns=['Ohio', 'Nevada'])
left2
right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]],
index=['b', 'c', 'd', 'e'],
columns=['Missouri', 'Alabama'])
right2
pd.merge(left2, right2, how='outer', left_index=True, right_index=True)
DataFrame有一个很便利的join实例,可以直接用index来合并。这个也可以用于与其他DataFrame进行合并,要有一样的index但不能由重叠的列:
left2.join(right2, how='outer')
由于一些历史原因,在早期的pandas版本中,DataFrame的join方法是在结合键上做left join(左联结),这样会保留左侧Dataframe的行索引。这也支持把传入的dataframe的index与被调用的DataFrame的column联结在一起:
left1.join(right1, on='key')
最后,对于简单的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
left2.join([right2, another])
left2.join([right2, another], how='outer')
另一种结合方式被称为可互换的,比如concatenation, binding, or stacking(连接,绑定,堆叠)。Numpy中的concatenate函数可以作用于numpy 数组:
arr = np.arange(12.).reshape((3, 4))
arr
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
np.concatenate([arr, arr], axis=1)
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(便签化的轴)能让我们做更泛化的数组连接操作。不过我们可能会有下面一些疑问:
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])
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 是列):
pd.concat([s1, s2, s3], axis=1)
这种情况下,不会与其他轴产生重叠,效果与join中的outer join一样。你也可以通过设定join='inner'来使用交集:
s4 = pd.concat([s1, s3])
s4
a 0
b 1
f 5
g 6
dtype: int64
pd.concat([s1, s4], axis=1)
pd.concat([s1, s4], axis=1, join='inner')
因为join='inner',所以f和g标签消失了。
你也可以在join_axes中指定使用哪些轴:
pd.concat([s1, s4], axis=1, join_axes=[['a', 'c', 'b', 'e']])
一个潜在的问题是连接的部分在结果里是不可辨识的。假设我们想在连接轴上创建一个多层级索引,我们需要用到keys参数:
result = pd.concat([s1, s1, s3], keys=['one', 'two', 'three'])
result
one a 0
b 1
two a 0
b 1
three f 5
g 6
dtype: int64
result.unstack()
如果是设定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'])
这种逻辑也可以扩展到DataFrame对象上:
df1 = pd.DataFrame(np.arange(6).reshape(3, 2), index=['a', 'b', 'c'],
columns=['one', 'two'])
df1
df2 = pd.DataFrame(5 + np.arange(4).reshape(2, 2), index=['a', 'c'],
columns=['three', 'four'])
df2
pd.concat([df1, df2], axis=1, keys=['level1', 'level2'])
如果导入一个dict而不是list,那么dict的key会被用于上面concat中的keys选项:
pd.concat({'level1': df1, 'level2': df2}, axis=1)
还有其他一些选项负责多层级索引的设定(表8-3)。比如,可以给创建的axis level(轴层级)用names参数来命名:
pd.concat([df1, df2], axis=1, keys=['level1', 'level2'],
names=['upper', 'lower'])
最后我们关心的是,在DataFrame中,行索引(row index)没有包含相关的数据:
df1 = pd.DataFrame(np.random.randn(3, 4), columns=['a', 'b', 'c', 'd'])
df1
df2 = pd.DataFrame(np.random.randn(2, 3), columns=['b', 'd', 'a'])
df2
这种情况下,可以设置ignore_index=True:
pd.concat([df1, df2], ignore_index=True)
另一种数据结合方法既不属于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
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
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)
array([ 0. , 2.5, 2. , 3.5, 4.5, 5. ])
Series有一个combine_first方法,效果和上面是一样,而且还会自动对齐(比如把index按字母进行排列):
b[:-2].combine_first(a[2:])
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
df2 = pd.DataFrame({'a': [5., 4., np.nan, 3., 7.],
'b': [np.nan, 3., 4., 6., 8.]})
df2
df1.combine_first(df2)
阅读量:1160
点赞量:0
收藏量:0