《利用python进行数据分析》Chapter 8

本章主要内容为对存在于不同文件的数据或者以不易于进行分析的格式存储的数据进行数据联合、连接以及重新排列。

1. 分层索引

pandas的多层索引,即允许在一个轴向上拥有多个(两个或两个以上)索引层级。分层索引可以看作一种在更低维度的形式中处理更高维度数据的方式。

data = pd.Series(np.random.randn(9),
                index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'],
                        [1, 2, 3, 1, 3, 1, 2, 2, 3]])
data
a  1   -0.620699
   2    0.246469
   3   -2.736904
b  1    0.736691
   3    0.448997
c  1    0.567007
   2    0.698084
d  2    0.133280
   3   -0.173206
dtype: float64
data.index # 复合索引
MultiIndex([('a', 1),
            ('a', 2),
            ('a', 3),
            ('b', 1),
            ('b', 3),
            ('c', 1),
            ('c', 2),
            ('d', 2),
            ('d', 3)],
           )

a. 通过分层索引对象(也称部分索引),可以选择数据的子集:

data['b']
1    0.736691
3    0.448997
dtype: float64
data[['b', 'd']] # data.loc[['b', 'd']] 效果一致
b  1    0.736691
   3    0.448997
d  2    0.133280
   3   -0.173206
dtype: float64
data["b": "c"] # 前闭后闭
b  1    0.736691
   3    0.448997
c  1    0.567007
   2    0.698084
dtype: float64

b. 内部层级的索引也可以:

data.loc[:, 2] # loc是标签索引,2表示内层的索引,非整数iloc索引
a    0.246469
c    0.698084
d    0.133280
dtype: float64

c. 分层索引在重塑数据和数组透视表中应用广泛,常用stack和unstack方法:

data.unstack() # Series的分层索引转化为DataFrame
	1			2			3
a	-0.620699	0.246469	-2.736904
b	0.736691	NaN			0.448997
c	0.567007	0.698084	NaN
d	NaN			0.133280	-0.173206
data.unstack().stack() # stack 与 unstack是逆操作
a  1   -0.620699
   2    0.246469
   3   -2.736904
b  1    0.736691
   3    0.448997
c  1    0.567007
   2    0.698084
d  2    0.133280
   3   -0.173206
dtype: float64

d. DataFrame中则每个轴都可以使用分层索引,可以为分层索引指定名称:

frame = pd.DataFrame(np.arange(12).reshape((4, 3)),
                     index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
                     columns=[['Ohio', 'Ohio', 'Colorado'],
                              ['Green', 'Red', 'Green']])
frame
		Ohio		Colorado
		Green	Red	Green
a	1	0		1	2
	2	3		4	5
b	1	6		7	8
	2	9		10	11
frame.index.names = ['key1', 'key2']
frame.columns.names = ['state', 'color']
frame
		state	Ohio		Colorado
		color	Green	Red	Green
key1	key2			
a		1		0		1	2
		2		3		4	5
b		1		6		7	8
		2		9		10	11

e. 创建多层索引的方法有三种:元组列表 / 特定结构 / 笛卡尔积,具体内容参见收藏博客内容。

1.1 重排序和层级排序

a. swaplevel 可以将内外层的索引进行变更对调:

frame.swaplevel('key1', 'key2')
		state	Ohio		Colorado
		color	Green	Red	Green
key2	key1			
1		a		0		1	2
2		a		3		4	5
1		b		6		7	8
2		b		9		10	11

b. sort_index 只能在单一层级上对数据进行排序,可以在层级变换时配合使用来按照想要的索引排序:

frame.sort_index(level=1)
		state	Ohio		Colorado
		color	Green	Red	Green
key1	key2			
a		1		0		1	2
b		1		6		7	8
a		2		3		4	5
b		2		9		10	11
frame.swaplevel(0, 1).sort_index(level=0) # 排序的level 0 是层级变换后的外层索引
		state	Ohio	Colorado
		color	Green	Red	Green
key2	key1			
1		a		0		1	2
		b		6		7	8
2		a		3		4	5
		b		9		10	11
1.2 按层级进行汇总统计

DataFrame 和 Series中很多描述性统计和汇总性统计都有level选项,可以指定想要进行聚合的轴。

frame.sum(level='key2')
state	Ohio		Colorado
color	Green	Red	Green
key2			
1		6		8	10
2		12		14	16
frame.sum(level='color', axis=1)
		color	Green	Red
key1	key2		
a		1		2		1
		2		8		4
b		1		14		7
		2		20		10
1.3 使用DataFrame 的列进行索引

a. 有些情况下,我们想要将DataFrame中的某些列转换为行索引,可以通过set_index来实现:

frame = pd.DataFrame({
     'a': range(7), 'b': range(7, 0, -1),
                      'c': ['one', 'one', 'one', 'two', 'two',
                            'two', 'two'],
                      'd': [0, 1, 2, 0, 1, 2, 3]})
frame
	a	b	c	d
0	0	7	one	0
1	1	6	one	1
2	2	5	one	2
3	3	4	two	0
4	4	3	two	1
5	5	2	two	2
6	6	1	two	3

将 c 列和 d 列作为分层索引:

frame2 = frame.set_index(["c", "d"])
frame2
		a	b
c	d		
one	0	0	7
	1	1	6
	2	2	5
two	0	3	4
	1	4	3
	2	5	2
	3	6	1

默认情况下,被作为索引的列会从数据列中移除,也可以通过drop参数来保留:

frame.set_index(["c", "d"], drop=False)
		a	b	c	d
c	d				
one	0	0	7	one	0
	1	1	6	one	1
	2	2	5	one	2
two	0	3	4	two	0
	1	4	3	two	1
	2	5	2	two	2
	3	6	1	two	3

b. reset_index是set_index的反操作,可以将作为分层索引的列重新移回列中:

frame2.reset_index()
	c	d	a	b
0	one	0	0	7
1	one	1	1	6
2	one	2	2	5
3	two	0	3	4
4	two	1	4	3
5	two	2	5	2
6	two	3	6	1
2. 联合与合并数据集
2.1 数据库风格的DataFrame连接

a. 通过一个或多个键连接行的方式来联合数据集是关系型数据库常用的合并或连接操作。pandas中的merge(合并)函数可以实现各种join操作:

df1 = pd.DataFrame({
     'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
                    'data1': range(7)})
df2 = pd.DataFrame({
     'key': ['a', 'b', 'd'],
                    'data2': range(3)})
df1
	key	data1
0	b	0
1	b	1
2	a	2
3	c	3
4	a	4
5	a	5
6	b	6
df2
	key	data2
0	a	0
1	b	1
2	d	2
pd.merge(df1, df2) # 未指定连接的键,merge自动将重叠列名作为连接键
	key	data1	data2
0	b	0		1
1	b	1		1
2	b	6		1
3	a	2		0
4	a	4		0
5	a	5		0
pd.merge(df1, df2, on='key') # 结果同上
pd.merge(df1, df2, how='outer') # 外连接
	key	data1	data2
0	b	0.0		1.0
1	b	1.0		1.0
2	b	6.0		1.0
3	a	2.0		0.0
4	a	4.0		0.0
5	a	5.0		0.0
6	c	3.0		NaN
7	d	NaN		2.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)})
pd.merge(df3, df4, left_on='lkey', right_on='rkey') # 这里的连接方式是默认的inner
	lkey	data1	rkey	data2
0	b		0		b		1
1	b		1		b		1
2	b		6		b		1
3	a		2		a		0
4	a		4		a		0
5	a		5		a		0                   

b. 默认情况下,merge是内连接inner(结果中的键是两张表的交集),其他连接方式如下:
左连接left join:结果集为左侧数据表中的数据,再加上左侧与右侧数据表之间匹配的数据。
右连接right join:结果集为右侧数据表中的数据,再加上左侧与右侧数据表之间匹配的数据。
外连接outer join:结果的键是两张表的并集,联合了左连接和右连接的效果。
以上的示例为多对一的例子,多对多的连接是行的笛卡尔积。

df1 = pd.DataFrame({
     'key': ['b', 'b', 'a', 'c', 'a', 'b'],
                    'data1': range(6)})
df2 = pd.DataFrame({
     'key': ['a', 'b', 'a', 'b', 'd'],
                    'data2': range(5)})
df1
	key	data1
0	b	0
1	b	1
2	a	2
3	c	3
4	a	4
5	b	5
df2
	key	data2
0	a	0
1	b	1
2	a	2
3	b	3
4	d	4
pd.merge(df1, df2, on="key", how='left')
	key	data1	data2
0	b	0		1.0
1	b	0		3.0
2	b	1		1.0
3	b	1		3.0
4	a	2		0.0
5	a	2		2.0
6	c	3		NaN
7	a	4		0.0
8	a	4		2.0
9	b	5		1.0
10	b	5		3.0
pd.merge(df1, df2, on='key', how='right', indicator=True) # indicator参数可以显示数据来源
	key	data1	data2	_merge
0	b	0.0		1		both
1	b	1.0		1		both
2	b	5.0		1		both
3	b	0.0		3		both
4	b	1.0		3		both
5	b	5.0		3		both
6	a	2.0		0		both
7	a	4.0		0		both
8	a	2.0		2		both
9	a	4.0		2		both
10	d	NaN		4		right_only

c. 使用多个键进行合并时,传入一个列名的列表,可以把多个键看作一个元组数据(看作一个键)来判断结果,但实际上并不是以这种方法来实现的。

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]})
left
	key1	key2	lval
0	foo		one		1
1	foo		two		2
2	bar		one		3
right
	key1	key2	rval
0	foo		one		4
1	foo		one		5
2	bar		one		6
3	bar		two		7
pd.merge(left, right, on=['key1', 'key2'], how='outer', indicator=True)
	key1	key2	lval	rval	_merge
0	foo		one		1.0		4.0		both
1	foo		one		1.0		5.0		both
2	foo		two		2.0		NaN		left_only
3	bar		one		3.0		6.0		both
4	bar		two		NaN		7.0		right_only

d. 合并操作中重合的列名可以通过重命名轴标签的方式来更改,但merhe的 suffixes选项可以直接实现:

pd.merge(left, right, on='key1') # 默认后缀为_x,_y
	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"))
	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
2.2 根据索引合并

a. 在某些情况下,DataFrame中用于合并的键是它的索引(行索引),可以通过left_index或right_index=True来设定:

left1 = pd.DataFrame({
     'key': ['a', 'b', 'a', 'a', 'b', 'c'],
                     'value': range(6)})
right1 = pd.DataFrame({
     'group_val': [3.5, 7]}, index=['a', 'b'])
left1
	key	value
0	a	0
1	b	1
2	a	2
3	a	3
4	b	4
5	c	5
right1
	group_val
a	3.5
b	7.0
pd.merge(left1, right1, left_on='key', right_index=True, indicator=True) # 左表key键,右表index
	key	value	group_val	_merge
0	a	0		3.5			both
2	a	2		3.5			both
3	a	3		3.5			both
1	b	1		7.0			both
4	b	4		7.0			both

b. 多层索引数据的情况可以指定多个层级的索引作为连接键:

lefth = pd.DataFrame({
     'key1': ['Ohio', 'Ohio', 'Ohio',
                               'Nevada', 'Nevada'],
                      'key2': [2000, 2001, 2002, 2001, 2002],
                      'data': np.arange(5.)})
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'])
lefth
	key1	key2	data
0	Ohio	2000	0.0
1	Ohio	2001	1.0
2	Ohio	2002	2.0
3	Nevada	2001	3.0
4	Nevada	2002	4.0
righth
				event1	event2
Nevada	2001	0		1
		2000	2		3
Ohio	2000	4		5
		2000	6		7
		2001	8		9
		2002	10		11
pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True, indicator=True)
	key1	key2	data	event1	event2	_merge
0	Ohio	2000	0.0		4		5		both
0	Ohio	2000	0.0		6		7		both
1	Ohio	2001	1.0		8		9		both
2	Ohio	2002	2.0		10		11		both
3	Nevada	2001	3.0		0		1		both

c. DataFrame对于按照行索引合并有个方便的join方法:

left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]],
                     index=['a', 'c', 'e'],
                     columns=['Ohio', 'Nevada'])
right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]],
                      index=['b', 'c', 'd', 'e'],
                      columns=['Missouri', 'Alabama'])
left2
	Ohio	Nevada
a	1.0		2.0
c	3.0		4.0
e	5.0		6.0
right2
	Missouri	Alabama
b	7.0			8.0
c	9.0			10.0
d	11.0		12.0
e	13.0		14.0
left2.join(right2, how='outer') # 根据行索引连接
	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

join方法默认进行的是左连接:

left1
	key	value
0	a	0
1	b	1
2	a	2
3	a	3
4	b	4
5	c	5
right1
	group_val
a	3.5
b	7.0
left1.join(right1, on='key') # 默认为左连接
	key	value	group_val
0	a	0		3.5
1	b	1		7.0
2	a	2		3.5
3	a	3		3.5
4	b	4		7.0
5	c	5		NaN

多个DataFrame连接可以对join方法传入一个列表:

another = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]],
                       index=['a', 'c', 'e', 'f'],
                       columns=['New York', 'Oregon'])
another
	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]) # 效果与concat一致
	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
2.3 沿轴向连接

指数据拼接、绑定或堆叠。
a. Numpy的 concatenate(堆叠)函数可以实现此功能:

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]) # 默认为行增加的方向
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [ 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]])

b. pandas 的concat函数

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'])
pd.concat([s1, s2, s3]) # 默认是沿着axis=0的轴向生效,生成新的Series
a    0
b    1
c    2
d    3
e    4
f    5
g    6
dtype: int64
pd.concat([s1, s2, s3], axis=1, sort=True) # axis=1,返回一个DataFrame
	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

轴上有重叠的情况:

s4 = pd.concat([s1, s3])
s4
a    0
b    1
f    5
g    6
dtype: int64
pd.concat([s1, s4])
a    0
b    1
a    0
b    1
f    5
g    6
dtype: int64
pd.concat([s1, s4], axis=1, sort=False) # 默认为索引的合集(outer join)
	0	1
a	0.0	0
b	1.0	1
f	NaN	5
g	NaN	6
pd.concat([s1, s4], axis=1, join='inner', sort=False) # 指定inner join连接
	0	1
a	0	0
b	1	1

可以通过join_axes来指定连接的轴:

pd.concat([s1, s4], axis=1, join_axes=[['a', 'c', 'd', 'e']])
	0	1
a	0.0	0.0
c	NaN	NaN
d	NaN	NaN
e	NaN	NaN

c. 相对于merge函数的indicator选项可以显示数据的来源,concat可以通过keys选项来标记数据的来源:

result = pd.concat([s1, s1, s3], keys=['one', 'two', 'three'])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

沿着axis=1轴堆叠时,keys会成为相应的列名:

pd.concat([s1, s1, s3], axis=1, keys=['one', 'two', 'three'], sort=True)
	one	two	three
a	0.0	0.0	NaN
b	1.0	1.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'])
df2 = pd.DataFrame(5 + np.arange(4).reshape(2, 2), index=['a', 'c'],
                   columns=['three', 'four'])
df1
	one	two
a	0	1
b	2	3
c	4	5
df2
	three	four
a	5	6
c	7	8
pd.concat([df1, df2],  keys=['level1', 'level2'],sort=False)
			one	two	three	four
level1	a	0.0	1.0	NaN		NaN
		b	2.0	3.0	NaN		NaN
		c	4.0	5.0	NaN		NaN
level2	a	NaN	NaN	5.0		6.0
		c	NaN	NaN	7.0		8.0
pd.concat([df1, df2], axis=1, keys=['level1', 'level2'],sort=False)
	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		

也可以直接传递字典,则字典的键则会用于keys选项:

pd.concat({
     'level1': df1, 'level2': df2}, axis=1, sort=True) # 结果同上

names选项可以对生成的多层索引命名:

pd.concat([df1, df2], axis=1, keys=['level1', 'level2'], names=['upper', 'lower'],sort=True)
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

行索引均为默认的整数索引的情况下,执行堆叠时可能需要忽略索引,重新自动生成:

df1 = pd.DataFrame(np.random.randn(3, 4), columns=['a', 'b', 'c', 'd'])
df2 = pd.DataFrame(np.random.randn(2, 3), columns=['b', 'd', 'a'])
df1
	a			b			c			d
0	-0.970151	1.546842	0.456050	-0.463122
1	-0.864715	0.199794	-0.019771	-0.929000
2	-1.223396	-1.012773	-1.024818	1.356040
df2
	b			d			a
0	0.574782	-1.890248	0.288047
1	1.548688	1.090507	-0.507717
pd.concat([df1, df2], sort=True) # 行索引为0 1 2 0 1
	a			b			c			d
0	-0.007571	-0.384941	-1.076635	0.606689
1	-1.932406	0.654753	0.464274	-0.736232
2	-0.238861	-0.268542	-0.377466	1.036489
0	-2.753539	1.586244	NaN			-1.662601
1	-0.758141	0.536541	NaN			-1.393381
pd.concat([df1, df2], ignore_index=True, sort=True) # 忽略原行索引,自动生成新的整数索引
	a			b			c			d
0	-0.970151	1.546842	0.456050	-0.463122
1	-0.864715	0.199794	-0.019771	-0.929000
2	-1.223396	-1.012773	-1.024818	1.356040
3	0.288047	0.574782	NaN			-1.890248
4	-0.507717	1.548688	NaN			1.090507
2.4 联合重叠数据

想象这样一个场景,有两个存在重叠索引的数据集,想要用第二个数据集中相同所以的值来修补第一个数据集中同一个索引下的NA值

a = pd.Series([np.nan, 2.5, 0.0, 3.5, 4.5, np.nan],
              index=['f', 'e', 'd', 'c', 'b', 'a'])
b = pd.Series([0., np.nan, 2., np.nan, np.nan, 5.],
              index=list('abcdef'))
a
f    NaN
e    2.5
d    0.0
c    3.5
b    4.5
a    NaN
dtype: float64
b
a    0.0
b    NaN
c    2.0
d    NaN
e    NaN
f    5.0
dtype: float64

上面的需求可以使用np的where函数来实现:

np.where(pd.isnull(a), b, a)
array([0. , 2.5, 0. , 3.5, 4.5, 5. ])

Series的combine_first方法可以实现相同的目的:

b.combine_first(a)
a    0.0
b    4.5
c    2.0
d    0.0
e    2.5
f    5.0
dtype: float64

combine_first 同样适用于DataFrame数据中:

df1 = pd.DataFrame({
     'a': [1., np.nan, 5., np.nan],
                    'b': [np.nan, 2., np.nan, 6.],
                    'c': range(2, 18, 4)})
df2 = pd.DataFrame({
     'a': [5., 4., np.nan, 3., 7.],
                    'b': [np.nan, 3., 4., 6., 8.]})
df1
	a	b	c
0	1.0	NaN	2
1	NaN	2.0	6
2	5.0	NaN	10
3	NaN	6.0	14
df2
	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)
	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
3. 重塑和透视
3.1 使用多层索引进行重塑

a. 主要用到两个函数stack和unstack:
stack:将列中的数据透视到行,形象地理解为”旋转“数据
unstack:stack的逆操作,将行中的数据透视到列

data = pd.DataFrame(np.arange(6).reshape((2, 3)),
                    index=pd.Index(['Ohio', 'Colorado'], name='state'),
                    columns=pd.Index(['one', 'two', 'three'],
                    name='number'))
data
number		one	two	three
state			
Ohio		0	1	2
Colorado	3	4	5
result = data.stack()
result
state     number
Ohio      one       0
          two       1
          three     2
Colorado  one       3
          two       4
          three     5
dtype: int32
result.index # 列名透视到行后生成多层索引
MultiIndex([(    'Ohio',   'one'),
            (    'Ohio',   'two'),
            (    'Ohio', 'three'),
            ('Colorado',   'one'),
            ('Colorado',   'two'),
            ('Colorado', 'three')],
           names=['state', 'number'])
result.unstack() # 将行透视到列
number		one	two	three
state			
Ohio		0	1	2
Colorado	3	4	5

b. 对于多层索引来说默认情况下是对最内层的索引进行操作,也可以传入层级序号或层级名称来指定操作的层级:

result.unstack(0) # 与 result.unstack('state') 效果一致
state	Ohio	Colorado
number		
one		0		3
two		1		4
three	2		5
result.unstack(1) # 与 result.unstack('number') 效果一致
number		one	two	three
state			
Ohio		0	1	2
Colorado	3	4	5

c. 堆叠的过程中很可能出现NA值,stack函数会自动过滤出缺失值,也可以通过dropna=False来保留NA值:

s1 = pd.Series([0, 1, 2, 3], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([4, 5, 6], index=['c', 'd', 'e'])
data2 = pd.concat([s1, s2], keys=['one', 'two'])
data2
one  a    0
     b    1
     c    2
     d    3
two  c    4
     d    5
     e    6
dtype: int64
data2.unstack()
	a	b	c	d	e
one	0.0	1.0	2.0	3.0	NaN
two	NaN	NaN	4.0	5.0	6.0
data2.unstack().stack()
one  a    0.0
     b    1.0
     c    2.0
     d    3.0
two  c    4.0
     d    5.0
     e    6.0
dtype: float64
data2.unstack().stack(dropna=False)
one  a    0.0
     b    1.0
     c    2.0
     d    3.0
     e    NaN
two  a    NaN
     b    NaN
     c    4.0
     d    5.0
     e    6.0
dtype: float64

d. 当你在DataFrame中拆堆(unstack)时,被拆堆的层级会变为结果中最低的层级:

df
			side	left	right
state		number		
Ohio		one		0		5
			two		1		6
			three	2		7
Colorado	one		3		8
			two		4		9
			three	5		10
df.unstack('state') # state变为最内的层级
side	left				right
state	Ohio	Colorado	Ohio	Colorado
number				
one		0		3			5		8
two		1		4			6		9
three	2		5			7		10			

在调用stack方法时,可以指定需要堆叠的轴向名称:

df.unstack('state').stack('side') # side变为最内的层级
		state	Colorado	Ohio
number	side		
one		left	3			0
		right	8			5
two		left	4			1
		right	9			6
three	left	5			2
		right	10			7
3.2 将“长”透视为“宽”

在数据库和CSV中存储多时间序列的方法就是所谓的长格式或堆叠格式。
长数据一般是指数据集中的变量没有做明确的细分,即变量中至少有一个变量中的元素存在值严重重复循环的情况(可以归为几类),表格整体的形状为长方形,即 变量少而观察值多。
宽数据是指数据集对所有的变量进行了明确的细分,各变量的值不存在重复循环的情况也无法归类。数据总体的表现为 变量多而观察值少。
长数据与宽数据之间的转换通常为以下两个原因:
时间序列数据想要观察多个种类的变量在一段时间内的变化,如上例,宽数据格式无法利用ggplot做出图形。
当数据清洗完成后,导入某些软件时,例如导入SPSS软件时宽数据格式会更好。

ldata[:5]
							date	item	value
0	1959-03-31 23:59:59.999999999	realgdp	2710.349
1	1959-03-31 23:59:59.999999999	infl	0.000
2	1959-03-31 23:59:59.999999999	unemp	5.800
3	1959-06-30 23:59:59.999999999	realgdp	2778.801
4	1959-06-30 23:59:59.999999999	infl	2.340

以上数据及所谓的多时间序列的长格式,即具有两个或更多个键的其他观测数据,表中的每一行表示一个时间点上的单个观测值。
数据通常以这种方式存储在关系型数据库中,因为固定格式允许item列中不同值的数量随着数据被添加到表中而改变。在某些情况下更想要获得一个按date列时间戳索引的且每个不同的item独立一列的DataFrame。
a. DataFrame的pivot方法可以实现这种转换:

pivoted = ldata.pivot('date', 'item', 'value') # 前两个值分别用作行和列索引,然后是可选的数值以填充数据
pivoted[:5]
item							infl	realgdp	unemp
						date			
1959-03-31 23:59:59.999999999	0.00	2710.349	5.8
1959-06-30 23:59:59.999999999	2.34	2778.801	5.1
1959-09-30 23:59:59.999999999	2.74	2775.488	5.3
1959-12-31 23:59:59.999999999	0.27	2785.204	5.6
1960-03-31 23:59:59.999999999	2.31	2847.699	5.2

b. 如果有两个数值列,可以同时进行重塑:

ldata['value2'] = np.random.randn(len(ldata))
ldata[:5]
							date	item	value		value2
0	1959-03-31 23:59:59.999999999	realgdp	2710.349	1.519298
1	1959-03-31 23:59:59.999999999	infl	0.000		0.291047
2	1959-03-31 23:59:59.999999999	unemp	5.800		-0.814497
3	1959-06-30 23:59:59.999999999	realgdp	2778.801	3.412448
4	1959-06-30 23:59:59.999999999	infl	2.340		2.244378

如果遗漏最后一个参数,会得到一个含有多层列的DataFrame:

pivoted = ldata.pivot('date', 'item') # ldata.pivot('date', 'item', ['value', 'value2'])效果一致
pivoted[:5]
								value						value2
item							infl	realgdp		unemp	infl		realgdp		unemp
date						
1959-03-31 23:59:59.999999999	0.00	2710.349	5.8		0.291047	1.519298	-0.814497
1959-06-30 23:59:59.999999999	2.34	2778.801	5.1		2.244378	3.412448	0.465989
1959-09-30 23:59:59.999999999	2.74	2775.488	5.3		1.817521	-2.337239	1.206609
1959-12-31 23:59:59.999999999	0.27	2785.204	5.6		0.554774	0.927027	-0.347757
1960-03-31 23:59:59.999999999	2.31	2847.699	5.2		-0.488280	-0.246020	1.412534
pivoted['value'][:5]
item							infl	realgdp		unemp
date			
1959-03-31 23:59:59.999999999	0.00	2710.349	5.8
1959-06-30 23:59:59.999999999	2.34	2778.801	5.1
1959-09-30 23:59:59.999999999	2.74	2775.488	5.3
1959-12-31 23:59:59.999999999	0.27	2785.204	5.6
1960-03-31 23:59:59.999999999	2.31	2847.699	5.2

c. pivot 方法等价于使用 set_index 创建分层索引,然后调用 unstack:

ldata.head()
							date	item	value		value2
0	1959-03-31 23:59:59.999999999	realgdp	2710.349	1.519298
1	1959-03-31 23:59:59.999999999	infl	0.000		0.291047
2	1959-03-31 23:59:59.999999999	unemp	5.800		-0.814497
3	1959-06-30 23:59:59.999999999	realgdp	2778.801	3.412448
4	1959-06-30 23:59:59.999999999	infl	2.340		2.244378
ldata.set_index(['date', 'item'])[:10]
										value		value2
						date	item		
1959-03-31 23:59:59.999999999	realgdp	2710.349	1.519298
								infl	0.000		0.291047
								unemp	5.800		-0.814497
1959-06-30 23:59:59.999999999	realgdp	2778.801	3.412448
								infl	2.340		2.244378
								unemp	5.100		0.465989
1959-09-30 23:59:59.999999999	realgdp	2775.488	-2.337239
								infl	2.740		1.817521
								unemp	5.300		1.206609
1959-12-31 23:59:59.999999999	realgdp	2785.204	0.927027
unstacked = ldata.set_index(['date', 'item']).unstack('item')
unstacked[:5]
								value						value2
item							infl	realgdp		unemp	infl		realgdp		unemp
						date						
1959-03-31 23:59:59.999999999	0.00	2710.349	5.8		0.291047	1.519298	-0.814497
1959-06-30 23:59:59.999999999	2.34	2778.801	5.1		2.244378	3.412448	0.465989
1959-09-30 23:59:59.999999999	2.74	2775.488	5.3		1.817521	-2.337239	1.206609
1959-12-31 23:59:59.999999999	0.27	2785.204	5.6		0.554774	0.927027	-0.347757
1960-03-31 23:59:59.999999999	2.31	2847.699	5.2		-0.488280	-0.246020	1.412534
3.3 将“宽”透视为“长”

在DataFrame中,pivot 方法的反操作是panda.melt,pivot 是将一列变换为新的DataFrame中的多列,melt 是将多列合并为一列,产生一个新的DataFrame,将宽度变为长度。

df = pd.DataFrame({
     'key': ['foo', 'bar', 'baz'],
                   'A': [1, 2, 3],
                   'B': [4, 5, 6],
                   'C': [7, 8, 9]})
df
	key	A	B	C
0	foo	1	4	7
1	bar	2	5	8
2	baz	3	6	9

‘key’ 列可以作为分组指标,其他列均为数据值。当使用 pandas.melt 时,必须指明哪些列是分组指标(如果有的话)。

melted = pd.melt(df, ['key'])
melted
	key	variable	value
0	foo		A		1
1	bar		A		2
2	baz		A		3
3	foo		B		4
4	bar		B		5
5	baz		B		6
6	foo		C		7
7	bar		C		8
8	baz		C		9

使用pivot方法可以将数据重塑回原先的布局:

reshaped = melted.pivot('key', 'variable', 'value')
reshaped
variable	A	B	C
	key			
	bar		2	5	8
	baz		3	6	9
	foo		1	4	7

由于pivot的结果将key作为了行索引,可以通过 reset_index来将数据移回一列:

reshaped.reset_index()
variable	key	A	B	C
		0	bar	2	5	8
		1	baz	3	6	9
		2	foo	1	4	7

melt 也可以指定某列作为值列:

pd.melt(df, id_vars=['key'], value_vars=['A', 'B'])
	key		variable	value
0	foo		A			1
1	bar		A			2
2	baz		A			3
3	foo		B			4
4	bar		B			5
5	baz		B			6

melt 也可以无须任何分组指标:

pd.melt(df, value_vars=['A', 'B', 'C'])
	variable	value
0	A			1
1	A			2
2	A			3
3	B			4
4	B			5
5	B			6
6	C			7
7	C			8
8	C			9
pd.melt(df, value_vars=['key', 'A', "B"])
	variable	value
0	key			foo
1	key			bar
2	key			baz
3	A			1
4	A			2
5	A			3
6	B			4
7	B			5
8	B			6

你可能感兴趣的:(Python-数据分析)