Python for Data Analysis IV

Posted by Gloomymoon on 2016-11-23

7 数据规整化:清理、转换、合并、重塑

数据分析和建模工作中大量的编程使用在数据准备上的:加载、清洗、转换以及重塑,大部分情况下存放在文本或数据库中的数据并不能满足应用的要求,幸运的是,pandas和Python标准库提供了一组高级、灵活、高效的核心函数和算法,使你能够轻松操作数据。

7.1 合并数据集

  • pandas.merge可以根据一个或多个键将不同的DataFrame中的行连接起来,类似于SQL中的join操作。
  • pandas.concat可以沿一条轴将多个对象堆叠到一起,,类似于SQL中的union all
  • 实例方法combine_first可以将重复数据编制在一起,用一个对象中的值填充另一个对象中的缺失值。

数据库风格的DataFrame合并

1
2
3
4
5
df1 = 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
3
df3 = 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链接,结果中的键是交集。可以在参数中指定其他连接方式:leftrightouter。多对多的合并操作无需额外的参数和工作,产生的是行的笛卡儿积。。

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
7
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]})
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
4
left1 = 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
7
lefth = 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
	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

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

DataFrame还有一个join实例方法,它能更方便地实现按索引合并,而且可用于合并多个带有相似索引的DataFrame对象,不管之间有没有重叠的列。

1
2
3
4
left2 = DataFrame([[1., 2.], [3., 4.], [5., 6.]], index=['a', 'c', 'e'], columns=['Ohio', 'Nevada'])
right2 = DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]], index=['b', 'c', 'd', 'e'], columns=['Missouri', 'Alabama'])

left2.join(right2, how='outer')

Output:

1
2
3
4
5
6
	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

与merge方法不同DataFrame的join方法默认实在连接键上做左连接。对于简单的索引合并,可以向join传入一组DataFrame(后面会介绍更为通用的concat函数)。

1
2
3
another = DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]],
index=['a', 'c', 'e', 'f'], columns=['New York', 'Oregon'])
left2.join([right2, another], how='outer')

Output:

1
2
3
4
5
6
7
	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

轴向连接

轴向连接(concatenation)也称作绑定(binding)、堆叠(stacking),类似于SAS中的set方法。

对于pandas对象(Series和DataFrame),连接运算时还要考虑如下问题:

  • 如果各对象其他轴上的索引不同,那些轴应该是做并集还是交集?

  • 结果对象中的分组需要各不相同吗?

  • 用户连接的轴是否重要

pandas的concat函数提供了一种解决这些问题的可靠连接方法。

假设有三个没有重叠索引的Series,对这些对象调用concat可以将值和索引拼接在一起:

1
2
3
4
s1 = Series([0, 1], index=['a', 'b'])
s2 = Series([2, 3, 4], index=['c', 'd', 'e'])
s3 = Series([5, 6], index=['f', 'g'])
pd.concat([s1, s2, s3])

Output:

1
2
3
4
5
6
7
8
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是列),这种情况下另外一条轴上没有重叠。

1
2
3
4
5
6
pd.concat([s1, s2, s3], axis=1)

s4 = pd.concat([s1 * 5, s3])
s4

pd.concat([s1, s4], axis=1)

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	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

a 0
b 5
f 5
g 6
dtype: int64

0 1
a 0.0 0
b 1.0 5
f NaN 5
g NaN 6

可以通过join_axes指定要在其他轴上使用的索引:

1
pd.concat([s1, s4], axis=1, join_axes=[['a', 'c', 'b', 'e']])

Output:

1
2
3
4
5
	0	1
a 0.0 0.0
c NaN NaN
b 1.0 5.0
e NaN NaN

但是这样无法在结果数据中区分参与连接的片段来源,使用keys参数可以创建一个表明来源的索引,unstack方法稍后详细讲解。

1
2
3
result = pd.concat([s1, s1, s3], keys=['one', 'two', 'three'])
result
result.unstack()

Output:

1
2
3
4
5
6
7
8
9
10
11
12
one    a    0
b 1
two a 0
b 1
three f 5
g 6
dtype: int64

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对Series进行合并,则keys就会成为DataFrame的列头。

1
pd.concat([s1, s2, s3], axis=1, keys=['one', 'two', 'three'])

Output:

1
2
3
4
5
6
7
8
	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选项的值。

1
2
3
4
5
6
df1 = DataFrame(np.arange(6).reshape(3, 2), index=['a', 'b', 'c'],
columns=['one', 'two'])
df2 = DataFrame(5 + np.arange(4).reshape(2, 2), index=['a', 'c'],
columns=['three', 'four'])
pd.concat({'level1': df1, 'level2': df2}, axis=1)
pd.concat([df1, df2], axis=1, keys=['level1', 'level2'])

Output都是:

1
2
3
4
5
	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

最后一个是ignore_index=True选项,用于忽略原始数据上的行索引,产生一种range(total_length)的新索引。

合并重叠数据

Series和DataFrame有一个combine_first方法,可以用参数对象中的数据为调用者对象的缺失数据“打补丁”。