3.6 层级索引
之前介绍了一维数组和二维数组, 用Pandas中的Series和DataFrame对象就可以储存. 我们也经常碰到处理多维数组的需求, Pandas提供了Panel和Panel4D对象解决三维数据与四维数据.
在实践中,更直观的形式是通过层级索引(hierarchical indexing),也被称为多级索引,配合多个有不同等级的一级索引一起使用,这样就可以将高维数组转换成类似一维Series和二维DataFrame对象的形式.
3.6.1 多级索引 Series
笨办法,用元组表示索引
import numpy as np
import pandas as pd
index = [('加利福利亚',2000),('加利福利亚',2010),('纽约',2000),('纽约',2010),('德克萨斯',2000),('德克萨斯',2010)]
population = [1123123,213123,543333,53334,45345,435435]
pop = pd.Series(population,index=index) # index在python中不是关键字
pop
(加利福利亚, 2000) 1123123
(加利福利亚, 2010) 213123
(纽约, 2000) 543333
(纽约, 2010) 53334
(德克萨斯, 2000) 45345
(德克萨斯, 2010) 435435
dtype: int64
pop[('加利福利亚',2010):('德克萨斯',2000)]
(加利福利亚, 2010) 213123
(纽约, 2000) 543333
(纽约, 2010) 53334
(德克萨斯, 2000) 45345
dtype: int64
pop[[i for i in pop.index if i[1]==2010]]
(加利福利亚, 2010) 213123
(纽约, 2010) 53334
(德克萨斯, 2010) 435435
dtype: int64
pop[[i for i in pop.index if index[1]==2010]] #代码错
Series([], dtype: int64)
pop[i for i in pop.index if i[1]==2010] # 少打一个中括号 ,报错
File "<ipython-input-6-fe2fee64d40f>", line 1
pop[i for i in pop.index if i[1]==2010] # 少打一个中括号 ,报错
^
SyntaxError: invalid syntax
好办法: Pandas多级索引
- 使用MultiIndex方法
index = pd.MultiIndex.from_tuples(index) # 为什么是from_tuples, index不是一个序列吗
# 有from-tuples, from_arrays(), from_product()三种用法
index
# levels 表示索引的等级, labels可以认为是哑变量
MultiIndex(levels=[['加利福利亚', '德克萨斯', '纽约'], [2000, 2010]],
labels=[[0, 0, 2, 2, 1, 1], [0, 1, 0, 1, 0, 1]])
pop = pop.reindex(index)
pop
# 布局怎么是这样子的
加利福利亚 2000 1123123
2010 213123
纽约 2000 543333
2010 53334
德克萨斯 2000 45345
2010 435435
dtype: int64
type(pop) # pop的属性还是Series, 只不过有了多级索引
pandas.core.series.Series
pop[:,2000] # 在Series通过多级索引筛选
加利福利亚 1123123
纽约 543333
德克萨斯 45345
dtype: int64
高维数据的多级索引
- DataFrame 就可以看作有行,列索引的数据
- 通过unstack()方法可以快速将一个多级索引的Series转化成普通索引的DataFrame
pop
加利福利亚 2000 1123123
2010 213123
纽约 2000 543333
2010 53334
德克萨斯 2000 45345
2010 435435
dtype: int64
pop_df = pop.unstack() # 程序怎么知道把哪个索引编成列
pop_df
2000 | 2010 | |
---|---|---|
加利福利亚 | 1123123 | 213123 |
德克萨斯 | 45345 | 435435 |
纽约 | 543333 | 53334 |
pop_df.stack() # unstack的逆过程
加利福利亚 2000 1123123
2010 213123
德克萨斯 2000 45345
2010 435435
纽约 2000 543333
2010 53334
dtype: int64
pop_df = pd.DataFrame({'total':pop,'under18':[3232,2323,3435,5656,688,1313]})
pop_df
total | under18 | ||
---|---|---|---|
加利福利亚 | 2000 | 1123123 | 3232 |
2010 | 213123 | 2323 | |
纽约 | 2000 | 543333 | 3435 |
2010 | 53334 | 5656 | |
德克萨斯 | 2000 | 45345 | 688 |
2010 | 435435 | 1313 |
f_u18 = pop_df['under18'] / pop_df['total']
#print(f_u18)
print(f_u18.unstack())
f_u18.unstack() # 为什么两种显示格式不一样呢
2000 2010
加利福利亚 0.002878 0.010900
德克萨斯 0.015173 0.003015
纽约 0.006322 0.106049
2000 | 2010 | |
---|---|---|
加利福利亚 | 0.002878 | 0.010900 |
德克萨斯 | 0.015173 | 0.003015 |
纽约 | 0.006322 | 0.106049 |
3.6.2 多级索引的创建方式
最直接的方法是在Series或DataFrame的index属性使用至少二维的索引数组.
df = pd.DataFrame(np.random.rand(4,2),index=[['a','a','b','b'],[1,2,1,2]],columns=['data1','data2'])
df
# MultiIndex的工作在后台完成
data1 | data2 | ||
---|---|---|---|
a | 1 | 0.425995 | 0.902387 |
2 | 0.051030 | 0.897131 | |
b | 1 | 0.713083 | 0.376870 |
2 | 0.030957 | 0.239390 |
用MultIndex中的方法显式的创建多级索引
# 方法一
pd.MultiIndex.from_arrays([['a','a','b','b'],[1,2,1,2]]) # 双层中括号,中括号中的数组
MultiIndex(levels=[['a', 'b'], [1, 2]],
labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
# 方法二
pd.MultiIndex.from_tuples([('a',1),('a',2),('b',1),('b',2)]) # 中括号中的元组
MultiIndex(levels=[['a', 'b'], [1, 2]],
labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
# 方法三
pd.MultiIndex.from_product([['a','b'],[1,2]]) # 两个索引的笛卡尔积
MultiIndex(levels=[['a', 'b'], [1, 2]],
labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
# 方法四: 在Series或者DataFrame设置index属性. 在MultIndex中设置levels和labels属性
pd.MultiIndex(levels=[['a','b'],[1,2]],
labels=[[0,0,1,1],[0,1,0,1]]) # labels的值应该只能为 0 和 1
MultiIndex(levels=[['a', 'b'], [1, 2]],
labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
多级索引的等级名称, 也就是给索引取个名称
- 用index的names属性
# pop.index.names('state','year') 报错
pop.index.names = ['state','year']
pop
state year
加利福利亚 2000 1123123
2010 213123
纽约 2000 543333
2010 53334
德克萨斯 2000 45345
2010 435435
dtype: int64
多级列索引
index = pd.MultiIndex.from_product([[2013,2014],[1,2]],
names=['year','visit'])
columns = pd.MultiIndex.from_product([['张三','李四','王五'],['HR','Temp']],
names=['subjeck','type'])
data = np.round(np.random.randn(4,6),1)
data[:,::2] *= 10
data += 37
health = pd.DataFrame(data,index=index,columns=columns)
health
subjeck | 张三 | 李四 | 王五 | ||||
---|---|---|---|---|---|---|---|
type | HR | Temp | HR | Temp | HR | Temp | |
year | visit | ||||||
2013 | 1 | 31.0 | 36.8 | 42.0 | 36.1 | 25.0 | 35.8 |
2 | 54.0 | 36.4 | 50.0 | 37.2 | 33.0 | 36.3 | |
2014 | 1 | 49.0 | 36.9 | 54.0 | 36.9 | 49.0 | 35.6 |
2 | 37.0 | 35.0 | 32.0 | 37.7 | 50.0 | 37.2 |
health['张三']
type | HR | Temp | |
---|---|---|---|
year | visit | ||
2013 | 1 | 31.0 | 36.8 |
2 | 54.0 | 36.4 | |
2014 | 1 | 49.0 | 36.9 |
2 | 37.0 | 35.0 |
health[(:,'张三'] # 为什么出错 ,该怎么表示
File "<ipython-input-25-91b10134495e>", line 1
health[(:,'张三'] # 为什么出错 ,该怎么表示
^
SyntaxError: invalid syntax
3.6.3 多级索引的取值与切片
- 把索引看作是额外增加的维度
Series多级索引
pop
state year
加利福利亚 2000 1123123
2010 213123
纽约 2000 543333
2010 53334
德克萨斯 2000 45345
2010 435435
dtype: int64
type(pop)
pandas.core.series.Series
pop[('加利福利亚',2000)] # pop是一个Series对象, 其索引是一个元组
1123123
pop['加利福利亚']
year
2000 1123123
2010 213123
dtype: int64
# pop[(:,2000)] # 报错
pop[:,2000]
state
加利福利亚 1123123
纽约 543333
德克萨斯 45345
dtype: int64
pop[pop>2000]
state year
加利福利亚 2000 1123123
2010 213123
纽约 2000 543333
2010 53334
德克萨斯 2000 45345
2010 435435
dtype: int64
pop[['加利福利亚','纽约']]
state year
加利福利亚 2000 1123123
2010 213123
纽约 2000 543333
2010 53334
dtype: int64
DataFrame多级索引
- DataFrame的基本索引是列索引. DataFrame中列比行重要
health
subjeck | 张三 | 李四 | 王五 | ||||
---|---|---|---|---|---|---|---|
type | HR | Temp | HR | Temp | HR | Temp | |
year | visit | ||||||
2013 | 1 | 31.0 | 36.8 | 42.0 | 36.1 | 25.0 | 35.8 |
2 | 54.0 | 36.4 | 50.0 | 37.2 | 33.0 | 36.3 | |
2014 | 1 | 49.0 | 36.9 | 54.0 | 36.9 | 49.0 | 35.6 |
2 | 37.0 | 35.0 | 32.0 | 37.7 | 50.0 | 37.2 |
health.iloc[:2,:2] # 隐式索引
# health[:2,:2] # 报错
subjeck | 张三 | ||
---|---|---|---|
type | HR | Temp | |
year | visit | ||
2013 | 1 | 31.0 | 36.8 |
2 | 54.0 | 36.4 |
health.loc[:,('张三','HR')] #显式索引
year visit
2013 1 31.0
2 54.0
2014 1 49.0
2 37.0
Name: (张三, HR), dtype: float64
# health.loc[(:,1),(:,'HR')] # 在元组中用切片会报错
# 使用IndexSlice
idx = pd.IndexSlice
health.loc[idx[:,1],idx[:,'HR']]
subjeck | 张三 | 李四 | 王五 | |
---|---|---|---|---|
type | HR | HR | HR | |
year | visit | |||
2013 | 1 | 31.0 | 42.0 | 25.0 |
2014 | 1 | 49.0 | 54.0 | 49.0 |
3.6.4 多级索引的行列转换
- 之前接触过 stack()和unstack()
有序的索引和无序的索引
- 如果MultiIndex不是有序的索引, 那么大多数切片操作都会失败.
index = pd.MultiIndex.from_product([['a','c','b'],[1,2]])
data = pd.Series(np.random.rand(6),index=index)
data.index.names=['char','int']
data
char int
a 1 0.930251
2 0.362329
c 1 0.505852
2 0.544361
b 1 0.683720
2 0.680830
dtype: float64
# data['a':'b'] 报错
局部切片和许多其他相似的操作要求MultiIndex的各级索引是有序的(即按照字典顺序由A至Z).
- Pandas提供了许多其他操作完成排序,sort_index()和sortlevel()方法
data = data.sort_index()
data
char int
a 1 0.930251
2 0.362329
b 1 0.683720
2 0.680830
c 1 0.505852
2 0.544361
dtype: float64
索引排序之后, 局部切片就可以正常使用了
data['a':'b']
char int
a 1 0.930251
2 0.362329
b 1 0.683720
2 0.680830
dtype: float64
索引stack和unstack
- 前文曾提过, 可以将一个多级索引数据集转换成简单的二维形式, 可以通过Level参数设置转换的索引层级.
pop.unstack(level=0)
state | 加利福利亚 | 德克萨斯 | 纽约 |
---|---|---|---|
year | |||
2000 | 1123123 | 45345 | 543333 |
2010 | 213123 | 435435 | 53334 |
pop.unstack(level=1)
year | 2000 | 2010 |
---|---|---|
state | ||
加利福利亚 | 1123123 | 213123 |
德克萨斯 | 45345 | 435435 |
纽约 | 543333 | 53334 |
索引的设置与重置
pop
state year
加利福利亚 2000 1123123
2010 213123
纽约 2000 543333
2010 53334
德克萨斯 2000 45345
2010 435435
dtype: int64
# 用reset_index方法实现. 生成一个列标签包含之前行索引标签. 也可以用数据的name属性为列设置名.
pop_flat = pop.reset_index(name='population')
pop_flat
state | year | population | |
---|---|---|---|
0 | 加利福利亚 | 2000 | 1123123 |
1 | 加利福利亚 | 2010 | 213123 |
2 | 纽约 | 2000 | 543333 |
3 | 纽约 | 2010 | 53334 |
4 | 德克萨斯 | 2000 | 45345 |
5 | 德克萨斯 | 2010 | 435435 |
type(pop_flat)
pandas.core.frame.DataFrame
使用DataFrame的set_index方法, 返回一个带多级索引的DataFrame
pop_flat.set_index(['state','year'])
population | ||
---|---|---|
state | year | |
加利福利亚 | 2000 | 1123123 |
2010 | 213123 | |
纽约 | 2000 | 543333 |
2010 | 53334 | |
德克萨斯 | 2000 | 45345 |
2010 | 435435 |
pop_flat.set_index(['state','population'])
year | ||
---|---|---|
state | population | |
加利福利亚 | 1123123 | 2000 |
213123 | 2010 | |
纽约 | 543333 | 2000 |
53334 | 2010 | |
德克萨斯 | 45345 | 2000 |
435435 | 2010 |
3.6.5 多级索引的数据累计方法
- Pandas自带数据累计方法,比如mean(),sum()和max()
- 对于层级索引数据, 可以设置level实现对数据子集的累计操作
health
subjeck | 张三 | 李四 | 王五 | ||||
---|---|---|---|---|---|---|---|
type | HR | Temp | HR | Temp | HR | Temp | |
year | visit | ||||||
2013 | 1 | 31.0 | 36.8 | 42.0 | 36.1 | 25.0 | 35.8 |
2 | 54.0 | 36.4 | 50.0 | 37.2 | 33.0 | 36.3 | |
2014 | 1 | 49.0 | 36.9 | 54.0 | 36.9 | 49.0 | 35.6 |
2 | 37.0 | 35.0 | 32.0 | 37.7 | 50.0 | 37.2 |
# 计算每一年各项指标的平均值
data_mean= health.mean(level='year')
data_mean
subjeck | 张三 | 李四 | 王五 | |||
---|---|---|---|---|---|---|
type | HR | Temp | HR | Temp | HR | Temp |
year | ||||||
2013 | 42.5 | 36.60 | 46.0 | 36.65 | 29.0 | 36.05 |
2014 | 43.0 | 35.95 | 43.0 | 37.30 | 49.5 | 36.40 |
data_mean.mean(axis=1,level='type')
type | HR | Temp |
---|---|---|
year | ||
2013 | 39.166667 | 36.433333 |
2014 | 45.166667 | 36.550000 |
# heth.mean(axis=1,level=['year','type']) 上面两种方式组合错在哪