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方法,可以用参数对象中的数据为调用者对象的缺失数据“打补丁”。
