3.1 使用多层索引进行重塑
多层索引在 dataframe 中提供了一种一致性方式用于重排列数据:
- stack(堆叠):该操作会旋转或将列中的数据透视到行。
- unstack(拆堆):将行中的数据透视到列。
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.unstack() # 就是拆成原来的
# 默认情况下,最内层是以拆堆的。你可以传入一个层级序号或名称来拆分一个不同的层级。
result.unstack(0)
state Ohio Colorado
number
one 0 3
two 1 4
three 2 5
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
# 默认情况下,stack()可以过滤缺失值,所有这个操作时可逆的
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
df = pd.DataFrame({'left': result, 'right': result + 5},
columns=pd.Index(['left', 'right'], name='side'))
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(0)
side left right
state Ohio Colorado Ohio Colorado
number
one 0 3 5 8
two 1 4 6 9
three 2 5 7 10
df.unstack(0).stack('side') # 指名需要堆叠的轴向名称。等价于 .stack(0)
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 中存储多时间序列的方式就是所谓的长格式或堆叠格式。
data=pd.read_table('macrodata.csv',sep=r'\t')
data.head()
year quarter realgdp realcons realinv realgovt realdpi cpi m1 tbilrate unemp pop infl realint
0 1959.0 1.0 2710.349 1707.4 286.898 470.045 1886.9 28.98 139.7 2.82 5.8 177.146 0.00 0.00
1 1959.0 2.0 2778.801 1733.7 310.859 481.301 1919.7 29.15 141.7 3.08 5.1 177.830 2.34 0.74
2 1959.0 3.0 2775.488 1751.8 289.226 491.260 1916.4 29.35 140.5 3.82 5.3 178.657 2.74 1.09
3 1959.0 4.0 2785.204 1753.7 299.356 484.052 1931.3 29.37 140.0 4.33 5.6 179.386 0.27 4.06
4 1960.0 1.0 2847.699 1770.5 331.722 462.199 1955.5 29.54 139.6 3.50 5.2 180.007 2.31 1.19
# PeriodIndex 以后再说,,就是将year 和 quarter 等列联合生成一种时间间隔类型。
periods=pd.PeriodIndex(year=data.year,quarter=data.quarter,name='data')
periods
PeriodIndex(['1959Q1', '1959Q2', '1959Q3', '1959Q4', '1960Q1', '1960Q2',
'1960Q3', '1960Q4', '1961Q1', '1961Q2',
...
'2007Q2', '2007Q3', '2007Q4', '2008Q1', '2008Q2', '2008Q3',
'2008Q4', '2009Q1', '2009Q2', '2009Q3'],
dtype='period[Q-DEC]', name='date', length=203, freq='Q-DEC')
columns=pd.Index(['realgdp','infl','unemp'],name='item')
data=data.reindex(columns=columns) # 这里就选出三列来
data.index=periods.to_timestamp('D','end')
# ['1959-03-31 23:59:59.999999999','1959-06-30 23:59:59.999999999'........
# data.index 就是这样的,,,我也看不懂为啥后面有那么多9999999,以后学到在说吧,,,我怀疑买到了假书。。。
ldata=data.stack().reset_index().rename(columns={0:'value'}) # 看不懂的话可以拆开一步一步看结果。。。
ldata[:5]
date item value
0 1959-03-31 23:59:59.999999999 realgdp 2710.349 # 这9999999999999是撒啊。。。
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
这种数据就是所谓的多时间序列的长格式。或称为具有两个或更多个键的其他观测数据(这里的键就是data and item)。表中的每一行表示一个时间点上的单个观测值。
数据通常以这种方式存储在关系型数据库中,如MySQL,因为固定模式(列名称和数据类型)允许item列中不同值的数量随这数据被添加到表中而改变。但是处理这种格式的数据更为困难,你可能更喜欢获取一个按data列时间索引的且每个不同 item独立一列的 dataframe。 使用pivote 方法。就是将一列变换成新的datafraem 的多列。
pivoted=ldata.pivot('date','item','value') # 传递的前两个值分别用作行和列索,然后是可选的数值列来填充dataframe 。
pivoted
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
... ... ... ...
2008-09-30 23:59:59.999999999 -3.16 13324.600 6.0
2008-12-31 23:59:59.999999999 -8.79 13141.920 6.9
2009-03-31 23:59:59.999999999 0.94 12925.410 8.1
2009-06-30 23:59:59.999999999 3.37 12901.504 9.2
2009-09-30 23:59:59.999999999 3.56 12990.341 9.6
203 rows × 3 columns
# 如果你同时想处理两个数值列
ldata['value2']=np.random.randn(len(ldata))
pivoted=ldata.pivot('date','item')
pivoted[:2]
value value2
item infl realgdp unemp infl realgdp unemp
date
1959-03-31 23:59:59.999999999 0.00 2710.349 5.8 -1.875672 0.374631 0.219582
1959-06-30 23:59:59.999999999 2.34 2778.801 5.1 1.722825 1.106186 1.187564
pivoted['value'][:2]
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
# pivot 方法等价于使用 set_index 创建分层索引,然后调用 unstack
unstacked=ldata.set_index(['date','item'])
unstacked # 返回值 等于 pivoted
3.3 将 ‘宽’ 透视为 ‘长’
在 dataframe 中,pivot 方法的反操作就是pandas.melt。与将一列变换成新的dataframe 中的多列不同,他将多列合并成一列, 产生一个新的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
melted=pd.melt(df,['key']) # 使用 melt 时,必须要指名那些列是分组坐标。这里我们使用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
reshaped=melted.pivot('key','variable','value')
reshaped # 使用pivote 将数据重塑到原先的布局。
variable A B C
key
bar 2 5 8
baz 3 6 9
foo 1 4 7
reshaped.reset_index() # pivot 的结果根据行标签的列生成了索引(就是key那一列)
variable key A B
0 bar 2 5 8 # 使用 reset_index 回移一列,就是多加一列索引。。
1 baz 3 6 9
2 foo 1 4 7
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
pd.melt(df,value_vars=['A','B','key']) # 也可以无需任何分组组标。
variable value
0 A 1
1 A 2
2 A 3
3 B 4
4 B 5
5 B 6
6 key foo
7 key bar
8 key baz
在家憋久了感觉一切都索然无味了。。。😔。。。。