7 数据规整化:清理、转换、合并、重塑
数据分析和建模工作中大量的编程使用在数据准备上的:加载、清洗、转换以及重塑,大部分情况下存放在文本或数据库中的数据并不能满足应用的要求,幸运的是,pandas和Python标准库提供了一组高级、灵活、高效的核心函数和算法,使你能够轻松操作数据。
7.1 合并数据集
pandas.merge
可以根据一个或多个键将不同的DataFrame中的行连接起来,类似于SQL中的join
操作。pandas.concat
可以沿一条轴将多个对象堆叠到一起,,类似于SQL中的union all
。- 实例方法
combine_first
可以将重复数据编制在一起,用一个对象中的值填充另一个对象中的缺失值。
数据库风格的DataFrame合并1
2
3
4
5df1 = DataFrame({'key': ['b','b','a','c','a','a','a'], 'data1':range(7)})
df2 = DataFrame({'key': ['a','b','d'], 'data2':range(3)})
df1
df2
pd.merge(df1, df2)
Output:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 data1 key
0 0 b
1 1 b
2 2 a
3 3 c
4 4 a
5 5 a
6 6 b
data2 key
0 0 a
1 1 b
2 2 d
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
会将重叠的列名当作键,因此最好是显示指定连接的键。1
pd.merge(df1, df2, on='key')
Output:1
2
3
4
5
6
7 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
如果两个对象的列名不同,也可以分别进行指定1
2
3df3 = DataFrame({'lkey': ['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='lkey', right_on='rkey')
Output:1
2
3
4
5
6
7 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
merge
方法默认情况下进行inner
链接,结果中的键是交集。可以在参数中指定其他连接方式:left
、right
、outer
。多对多的合并操作无需额外的参数和工作,产生的是行的笛卡儿积。。1
pd.merge(df1, df2, how='outer')
Output:1
2
3
4
5
6
7
8
9 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
6 3 c NaN
7 NaN d 2
要根据多个键进行合并,传入一个由列名组成的列表即可。1
2
3
4
5
6
7left = 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]})
pd.merge(left, right, on=['key1', 'key2'], how='outer')
Output:1
2
3
4
5
6 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
注意进行merge时,DataFrame对象中的索引会被丢弃。
合并运算还需要考虑重复列名的处理,虽然可以手工处理列名重叠的问题(例如重命名轴标签),但是merge有一个更实用的suffixes选项,用于指定添加在左右两个DataFrame对象重叠列名上的字符串。1
pd.merge(left, right, on='key1', suffixes=('_left', '_right'))
Output:1
2
3
4
5
6
7 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
索引上的合并
有时候,DataFrame中的连接键位于其索引中,可以传入left_index=True或right_index=True指明索引应该被用作连接键。1
2
3
4left1 = DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],
'value': range(6)})
right1 = DataFrame({'group_val': [3.5, 7]}, index=['a', 'b'])
pd.merge(left1, right1, left_on='key', right_index=True)
Output:1
2
3
4
5
6 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默认是求取连接键的交集,可以通过外连接的方式得到它们的并集。1
pd.merge(left1, right1, left_on='key', right_index=True, how='outer')
对于层次化索引的数据,处理起来需要以列表的形式指明用作合并键的多个列。1
2
3
4
5
6
7lefth = DataFrame({'key1': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'], 'key2': [2000, 2001, 2002, 2001, 2002], 'data': np.arange(5.)})
righth = DataFrame(np.arange(12).reshape((6, 2)), index=[['Nevada', 'Nevada', 'Ohio', 'Ohio', 'Ohio', 'Ohio'], [2001, 2000, 2000, 2000, 2001, 2002]], columns=['event1', 'event2'])
pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True)
pd.merge(lefth, righth, left_on=['key1', 'key2'],
right_index=True, how='outer')
Output:
1 | data key1 key2 event1 event2 |
DataFrame还有一个join
实例方法,它能更方便地实现按索引合并,而且可用于合并多个带有相似索引的DataFrame对象,不管之间有没有重叠的列。
1 | left2 = DataFrame([[1., 2.], [3., 4.], [5., 6.]], index=['a', 'c', 'e'], columns=['Ohio', 'Nevada']) |
Output:
1 | Ohio Nevada Missouri Alabama |
与merge方法不同DataFrame的join
方法默认实在连接键上做左连接。对于简单的索引合并,可以向join
传入一组DataFrame(后面会介绍更为通用的concat
函数)。
1 | another = DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]], |
Output:
1 | Ohio Nevada Missouri Alabama New York Oregon |
轴向连接
轴向连接(concatenation)也称作绑定(binding)、堆叠(stacking),类似于SAS中的set方法。
对于pandas对象(Series和DataFrame),连接运算时还要考虑如下问题:
如果各对象其他轴上的索引不同,那些轴应该是做并集还是交集?
结果对象中的分组需要各不相同吗?
用户连接的轴是否重要
pandas的concat
函数提供了一种解决这些问题的可靠连接方法。
假设有三个没有重叠索引的Series,对这些对象调用concat可以将值和索引拼接在一起:
1 | s1 = Series([0, 1], index=['a', 'b']) |
Output:
1 | a 0 |
默认情况下,concat实在axis=0上拼接并产生一个新的Series,如果传入axis=1,则结果会变成一个DataFrame(axis=1是列),这种情况下另外一条轴上没有重叠。
1 | pd.concat([s1, s2, s3], axis=1) |
Output:
1 | 0 1 2 |
可以通过join_axes
指定要在其他轴上使用的索引:
1 | pd.concat([s1, s4], axis=1, join_axes=[['a', 'c', 'b', 'e']]) |
Output:
1 | 0 1 |
但是这样无法在结果数据中区分参与连接的片段来源,使用keys
参数可以创建一个表明来源的索引,unstack
方法稍后详细讲解。
1 | result = pd.concat([s1, s1, s3], keys=['one', 'two', 'three']) |
Output:
1 | one a 0 |
如果沿着axis=1对Series进行合并,则keys就会成为DataFrame的列头。
1 | pd.concat([s1, s2, s3], axis=1, keys=['one', 'two', 'three']) |
Output:
1 | one two three |
对DataFrame对象也是一样,如果传入的不是拼接的列表而是一个字典,则字典的键就会被当作keys选项的值。
1 | df1 = DataFrame(np.arange(6).reshape(3, 2), index=['a', 'b', 'c'], |
Output都是:
1 | level1 level2 |
最后一个是ignore_index=True选项,用于忽略原始数据上的行索引,产生一种range(total_length)的新索引。
合并重叠数据
Series和DataFrame有一个combine_first
方法,可以用参数对象中的数据为调用者对象的缺失数据“打补丁”。