5. 层级索引¶
处理多维数据的时候,虽然Pandas提供了Panel和Panel4D, 但更直观的是使用层级索引(Hierarchical Indeing,也叫多级索引 multi-indexing), 通过层级索引, 可以将高维度数据转换成类似一维Series或者二维DataFrame对象的形式。
5.1. 层级索引的创建¶
5.1.1. 直接创建¶
import numpy as np
import pandas as pd
# 通过输入层级索引,直接创建Series
# 我们创建一组翻译数据,分别包含1-5的汉语,英语,德语
# 用元组信息作为index
idx = [("yi", "en"), ("yi", "de"),
("er", "en"), ("er", "de"),
("san", "en"), ("san", "de"),
("si", "en"), ("si", "de"),
("wu", "en"), ("wu", "de")]
# 内容是元组对应的每一个英语或者德育的翻译
digits = ["ONE", "EINS", "TWO", "ZWEI", "THREE", "DREI", "FOUR", "VIER",
"FIVE", "FUNF"]
# 创建一维的数组
trans = pd.Series(digits, index=idx)
# 查看打印的结果
print("trans = \n", trans)
# 这个一维数组可以支持常常的比如取值,切片操作
print("\n trans[('san', 'en')] = ", trans[('san', 'en')] )
print("\n trans[('san', 'en'):('wu','en')] = \n", trans[('san', 'en'):('wu','en')])
trans =
(yi, en) ONE
(yi, de) EINS
(er, en) TWO
(er, de) ZWEI
(san, en) THREE
(san, de) DREI
(si, en) FOUR
(si, de) VIER
(wu, en) FIVE
(wu, de) FUNF
dtype: object
trans[('san', 'en')] = THREE
trans[('san', 'en'):('wu','en')] =
(san, en) THREE
(san, de) DREI
(si, en) FOUR
(si, de) VIER
(wu, en) FIVE
dtype: object
5.1.2. 使用多级索引创建¶
可以先创建多级MultiIndex,然后利用多级索引来创建内容。
MultiIndex是一个包含levels的结构,levels表示的是索引的层级,每个层级分别有哪些内容等等。
# 创建多级索引
# 使用上面的数据idx
midx = pd.MultiIndex.from_tuples(idx)
print( "多级索引midx=\n", midx)
多级索引midx=
MultiIndex(levels=[['er', 'san', 'si', 'wu', 'yi'], ['de', 'en']],
labels=[[4, 4, 0, 0, 1, 1, 2, 2, 3, 3], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]])
# 然后利用创建的多级索引创建数据
trans = pd.Series(digits, index=midx)
print("\n trans = \n", trans)
# 然后可以使用切片等功能获取相应内容
# 注意:因为索引不是按照字典排序,所以不能使用切片,即切片的前提是,索引是有序索引 !!!
print("\n trans['san', 'de'] = ", trans['san', 'de'])
trans =
yi en ONE
de EINS
er en TWO
de ZWEI
san en THREE
de DREI
si en FOUR
de VIER
wu en FIVE
de FUNF
dtype: object
trans['san', 'de'] = DREI
5.1.3. 利用Series创建二维DataFrame¶
pandas给我们提供了一对函数,stack和unstack,可以让Series和DataFrame相互转换。
#数据直接使用一维的trans
trans_df = trans.unstack()
print("折叠后的二维数据是:")
print("trans_df = \n", trans_df)
折叠后的二维数据是:
trans_df =
de en
er ZWEI TWO
san DREI THREE
si VIER FOUR
wu FUNF FIVE
yi EINS ONE
5.1.4. 创建数据的时候直接时候用二维索引¶
trans_df = pd.DataFrame(digits, index=[["YI", "YI", "ER", "ER", "SAN", "SAN","SI", "SI", "WU", "WU"],
["EN", "DE","EN", "DE","EN", "DE","EN", "DE","EN", "DE"]],
columns=["TRANS"])
print(trans_df)
TRANS
YI EN ONE
DE EINS
ER EN TWO
DE ZWEI
SAN EN THREE
DE DREI
SI EN FOUR
DE VIER
WU EN FIVE
DE FUNF
5.2. 多级索引¶
5.2.1. 显式创建多级索引¶
# 通过数组创建
midx = pd.MultiIndex.from_arrays([list("AAABBBCCC"), [1,2,3,1,2,3,1,2,3]])
print("通过数组创建:midx = \n", midx)
通过数组创建:midx =
MultiIndex(levels=[['A', 'B', 'C'], [1, 2, 3]],
labels=[[0, 0, 0, 1, 1, 1, 2, 2, 2], [0, 1, 2, 0, 1, 2, 0, 1, 2]])
# 通过tuple创建
midx = pd.MultiIndex.from_tuples([("A", 1),("A", 2), ("A", 3),
("B", 1),("B", 2), ("B", 3),
("C", 1),("C", 2), ("C", 3),])
print("通过元组创建:midx = \n", midx)
通过元组创建:midx =
MultiIndex(levels=[['A', 'B', 'C'], [1, 2, 3]],
labels=[[0, 0, 0, 1, 1, 1, 2, 2, 2], [0, 1, 2, 0, 1, 2, 0, 1, 2]])
# 通过笛卡尔积创建(CartesianProduct)
midx = pd.MultiIndex.from_product([list("ABC"), [1,2,3]])
print("通过笛卡尔积创建:midx = \n", midx)
通过笛卡尔积创建:midx =
MultiIndex(levels=[['A', 'B', 'C'], [1, 2, 3]],
labels=[[0, 0, 0, 1, 1, 1, 2, 2, 2], [0, 1, 2, 0, 1, 2, 0, 1, 2]])
5.2.2. 多级索引的等级名称¶
对多级索引的每个等级进行命名可以方便以后的操作,一般可以直接通过对所含的属性names进行赋值得到。
midx.names =["NAME", "LEVEL"]
print("带名字的多级索引 midx = \n", midx)
带名字的多级索引 midx =
MultiIndex(levels=[['A', 'B', 'C'], [1, 2, 3]],
labels=[[0, 0, 0, 1, 1, 1, 2, 2, 2], [0, 1, 2, 0, 1, 2, 0, 1, 2]],
names=['NAME', 'LEVEL'])
5.2.3. 多级列索引¶
列索引也可以包含多级,通过多级行索引,列所含能实现多维数据的创建和操作。
# 行多级索引
midx_row = pd.MultiIndex.from_product([list("ABC"), [1,2,3]])
# 列多级索引
midx_col = pd.MultiIndex.from_product([["I", "II", "III"], [1,2]])
#准备数据
data = np.arange(54).reshape(9,6)
df = pd.DataFrame(data, index=midx_row, columns=midx_col)
print(df)
I II III
1 2 1 2 1 2
A 1 0 1 2 3 4 5
2 6 7 8 9 10 11
3 12 13 14 15 16 17
B 1 18 19 20 21 22 23
2 24 25 26 27 28 29
3 30 31 32 33 34 35
C 1 36 37 38 39 40 41
2 42 43 44 45 46 47
3 48 49 50 51 52 53
5.3. 多级索引的的取值和切片¶
多级索引的取值和切片和和简单的数组的取值和切片很类似,我们这里先介绍Series的多级索引的取值和切片,再介绍DataFrame的取值和切片用法。
对多级索引使用切片的前提是:索引为有序索引!!!
5.3.1. Series的多级索引取值操作¶
# 创建数据
trans = pd.Series(digits, index=[["YI", "YI", "ER", "ER", "SAN", "SAN","SI", "SI", "WU", "WU"],
["EN", "DE","EN", "DE","EN", "DE","EN", "DE","EN", "DE"]],
)
print("Series的数据: \n", trans_df)
Series的数据:
YI EN ONE
DE EINS
ER EN TWO
DE ZWEI
SAN EN THREE
DE DREI
SI EN FOUR
DE VIER
WU EN FIVE
DE FUNF
dtype: object
# 对trans的取值操作
# 提取单个一级索引的值
print("trans['YI', 'DE'] = \n", trans['YI'])
# 使用二级索引提取单个的值
print("\n trans['YI', 'DE'] = ", trans['YI', 'DE'])
trans['YI', 'DE'] =
EN ONE
DE EINS
dtype: object
trans['YI', 'DE'] = EINS
# 切片的前提是index必须有序
# 准备有序的index
data = np.arange(100, 700, 100)
midx = pd.MultiIndex.from_product([list("ABC"), [1,2]])
df = pd.Series(data, index=midx)
print("df = \n", df)
df =
A 1 100
2 200
B 1 300
2 400
C 1 500
2 600
dtype: int64
# 对一级索引进行切片
print("一级索引进行切片 df[:'B'] = \n", df[:'B'])
一级索引进行切片 df[:'B'] =
A 1 100
2 200
B 1 300
2 400
dtype: int64
# 对二级索引选取
print("\n 二级索引进行切片 df.loc[:'B', :2] =\n", df.loc[:'B', :2])
二级索引进行切片 df.loc[:'B', :2] =
A 1 100
2 200
B 1 300
2 400
dtype: int64
# 掩码选取
print("y掩码选取数据 df[df < 400] = \n", df[df < 400])
y掩码选取数据 df[df < 400] =
A 1 100
2 200
B 1 300
dtype: int64
# 花哨索引
print(df[['A','C']].loc[:, 1])
A 100
C 500
dtype: int64
5.3.2. DataFrame取值操作¶
DataFrame的切片操作比较麻烦,对元组的切片还容易引起错误,pandas给提供了专门的IndexSlice来专门帮助处理切片。
## 准备数据
# 行多级索引
midx_row = pd.MultiIndex.from_product([list("ABC"), [1,2,3]])
# 列多级索引
midx_col = pd.MultiIndex.from_product([["I", "II", "III"], [1,2]])
#准备数据
data = np.arange(54).reshape(9,6)
df = pd.DataFrame(data, index=midx_row, columns=midx_col)
print(df)
I II III
1 2 1 2 1 2
A 1 0 1 2 3 4 5
2 6 7 8 9 10 11
3 12 13 14 15 16 17
B 1 18 19 20 21 22 23
2 24 25 26 27 28 29
3 30 31 32 33 34 35
C 1 36 37 38 39 40 41
2 42 43 44 45 46 47
3 48 49 50 51 52 53
# 使用列索引获取数据
print("获取DF的一列 df['I',2] = \n", df['I', 2])
获取DF的一列 df['I',2] =
A 1 1
2 7
3 13
B 1 19
2 25
3 31
C 1 37
2 43
3 49
Name: (I, 2), dtype: int64
# 最好使用索引器
print("获取DF的一列 df['I',2] = \n", df.loc['A'])
获取DF的一列 df['I',2] =
I II III
1 2 1 2 1 2
1 0 1 2 3 4 5
2 6 7 8 9 10 11
3 12 13 14 15 16 17
# 使用IndexSlice
idx = pd.IndexSlice
df.loc[idx[:, 2], idx[:'II', :]]
5.4. 多级索引的行列转换¶
Pandas提供了很多方法,可以让数据在内容保持不变的情况下,按照需要进行行列转换。
5.4.1. 有序和无序索引¶
MultiIndex如果不是有序索引,则大多数切片可能失败。
为了让Index有序,我们一般使用sort_index方法对齐进行处理,使之有序后在进行操作。
# 准备无序MultiIndex的数据
trans = pd.Series(digits, index=[["YI", "YI", "ER", "ER", "SAN", "SAN","SI", "SI", "WU", "WU"],
["EN", "DE","EN", "DE","EN", "DE","EN", "DE","EN", "DE"]],
)
print("Series的数据: \n", trans)
Series的数据:
YI EN ONE
DE EINS
ER EN TWO
DE ZWEI
SAN EN THREE
DE DREI
SI EN FOUR
DE VIER
WU EN FIVE
DE FUNF
dtype: object
# 上面trans是无序index,在对其进行切片操作的时候,会报错
# 使用sort_index进行排序后,进行切片操作就正常
trans2 = trans.sort_index()
print("排序后的 trans2[:'WU'] = \n", trans2[:'WU'] )
排序后的 trans2[:'WU'] =
ER DE ZWEI
EN TWO
SAN DE DREI
EN THREE
SI DE VIER
EN FOUR
WU DE FUNF
EN FIVE
dtype: object
5.4.2. stack和unstack¶
这两个互为逆操作。
# unstack把低维度数据扩展成高维度数据
trans4 = trans.unstack()
print("二维数据是: trans.unstack = \n", trans4)
二维数据是: trans.unstack =
DE EN
ER ZWEI TWO
SAN DREI THREE
SI VIER FOUR
WU FUNF FIVE
YI EINS ONE
# unstack可以按照指定级别进行扩展
trans4 = trans.unstack(level=0)
print("二维数据是: trans.unstack = \n", trans4)
二维数据是: trans.unstack =
ER SAN SI WU YI
DE ZWEI DREI VIER FUNF EINS
EN TWO THREE FOUR FIVE ONE
5.4.3. 索引的设置和重置¶
set_index和reset_index可以通过重新设置index来改变数据的表现形式。
# 吧索引当做数据插入到二维数组中
trans6 = trans.reset_index()
print("重置索引后数据 trans.reset_index = \n", trans6)
print(type(trans6))
重置索引后数据 trans.reset_index =
level_0 level_1 0
0 YI EN ONE
1 YI DE EINS
2 ER EN TWO
3 ER DE ZWEI
4 SAN EN THREE
5 SAN DE DREI
6 SI EN FOUR
7 SI DE VIER
8 WU EN FIVE
9 WU DE FUNF
<class 'pandas.core.frame.DataFrame'>
# 可以使用drop参数来表明是否丢弃index
# 默认drop=False
trans7 = trans.reset_index(drop=True)
print("重置索引并丢弃后 trans.reset_index(drop=True) = \n", trans7)
重置索引并丢弃后 trans.reset_index(drop=True) =
0 ONE
1 EINS
2 TWO
3 ZWEI
4 THREE
5 DREI
6 FOUR
7 VIER
8 FIVE
9 FUNF
dtype: object
# set_index是reset_index的逆操作
# 参数为必填项,必须明确指明哪个columns呗当做index
trans8 = trans6.set_index(['level_0'])
print("重置索引后的数据 trans6.set_index('level_0') = \n", trans8)
重置索引后的数据 trans6.set_index('level_0') =
level_1 0
level_0
YI EN ONE
YI DE EINS
ER EN TWO
ER DE ZWEI
SAN EN THREE
SAN DE DREI
SI EN FOUR
SI DE VIER
WU EN FIVE
WU DE FUNF
5.5. 多级索引的数据累计方法¶
对于多级索引,我们可能需要对某一个索引进行数据处理,比如求mean,sum等操作,此时需要用到参数level,通过对参数level的设置,pandas会选择相应的数据进行处理。
import numpy as np
import pandas as pd
## 准备数据
# 行多级索引
midx_row = pd.MultiIndex.from_product([list("ABC"), [1,2,3]], names=["Upper", "row_digit"])
# 列多级索引
midx_col = pd.MultiIndex.from_product([["I", "II", "III"], [1,2]], names=["ROM", "col_digit"])
#准备数据
data = np.arange(54).reshape(9,6)
df = pd.DataFrame(data, index=midx_row, columns=midx_col)
print(df)
ROM I II III
col_digit 1 2 1 2 1 2
Upper row_digit
A 1 0 1 2 3 4 5
2 6 7 8 9 10 11
3 12 13 14 15 16 17
B 1 18 19 20 21 22 23
2 24 25 26 27 28 29
3 30 31 32 33 34 35
C 1 36 37 38 39 40 41
2 42 43 44 45 46 47
3 48 49 50 51 52 53
5.5.1. level的使用¶
level参数可以对某一索引进行聚合操作
## 上面例子,对A,B,C进行求和
data_sum = df.sum(level="Upper")
print(data_sum)
ROM I II III
col_digit 1 2 1 2 1 2
Upper
A 18 21 24 27 30 33
B 72 75 78 81 84 87
C 126 129 132 135 138 141
5.6. level配合axis¶
level和axis可以配合一起使用,这样就可以死对任意维度进行操作,还可以连续操作,最终得到想要的结果。
# 配合axis使用
data_sum = data_sum.sum(axis=1, level="ROM")
print(data_sum)
ROM I II III
Upper
A 39 51 63
B 147 159 171
C 255 267 279