本章主要内容为对存在于不同文件的数据或者以不易于进行分析的格式存储的数据进行数据联合、连接以及重新排列。
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. 创建多层索引的方法有三种:元组列表 / 特定结构 / 笛卡尔积,具体内容参见收藏博客内容。
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
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
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
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
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
指数据拼接、绑定或堆叠。
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
想象这样一个场景,有两个存在重叠索引的数据集,想要用第二个数据集中相同所以的值来修补第一个数据集中同一个索引下的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
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
在数据库和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
在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