到目前为止,我们接触到的都是一位数据和二维数据,分别可以用Pandas的Series和DataFrame对象储存
但我们也有可能遇到存储多维数据的情况,对此Pandas分别提供了Panel和Panel4D对象来解决三维数据和思维数据
我们通过之前的讲解,可以发现Pandas的DataFrame对象很像Excel表格,每一个数据都有Index和Column,对应Excel表格中的行名和列名
而层级索引则对应Excel中的合并行与合并列,这样,我们通过Pandas所展现的数据就更加直观
此外,我们可以使用层级索引来让高维数组降维,从而使用DataFrame对象来直观的表示高维数据.
这一节将主要讲解创建层级索引(MultiIndex对象)的方法,多级索引数据的取值和切片,统计值的计算以及普通索引与层级索引的转换方法
首先讲解为什么要引入层级索引,即如何用DataFrame对象表示高维数据
假如我们需要用Series对象来储存不同省份在不同年份的人口数,我们可以使用如下一个抖机灵的方法
我们把索引改变为省份与年份的元组,来实现存储
Index=[('PlaceA',2020),('PlaceB',2021),\
('PlaceB',2020),('PlaceB',2021),\
('PlaceC',2020),('PlaceC',2021)]
index_multi=pd.MultiIndex.from_tuples(Index)
print(pd.Series([100,200,300,400,500,600],index=Index))
>>>
(PlaceA, 2020) 100
(PlaceB, 2021) 200
(PlaceB, 2020) 300
(PlaceB, 2021) 400
(PlaceC, 2020) 500
(PlaceC, 2021) 600
dtype: int64
但是这样却比较笨,因为我们想要通过索引来取值的时候,无法做到取出某个年份所有的人口数据,因为我们单独的年份或者省份都不是索引
下面我们就用多级索引来对比一下
Index=[('PlaceA',2020),('PlaceB',2021),\
('PlaceB',2020),('PlaceB',2021),\
('PlaceC',2020),('PlaceC',2021)]
index_multi=pd.MultiIndex.from_tuples(Index)
print(pd.Series([100,200,300,400,500,600],index=Index))
print(pd.Series([100,200,300,400,500,600],index=Index).reindex(index_multi))
>>>
(PlaceA, 2020) 100
(PlaceB, 2021) 200
(PlaceB, 2020) 300
(PlaceB, 2021) 400
(PlaceC, 2020) 500
(PlaceC, 2021) 600
dtype: int64
PlaceA 2020 100
PlaceB 2021 200
2020 300
2021 400
PlaceC 2020 500
2021 600
dtype: int64
注意,这个时候地区和年份都是有效的索引我们可以直接使用来索取各地的数据
Index=[('PlaceA',2020),('PlaceA',2021),\
('PlaceB',2020),('PlaceB',2021),\
('PlaceC',2020),('PlaceC',2021)]
index_multi=pd.MultiIndex.from_tuples(Index)
Series_1=pd.Series([100,200,300,400,500,600],index=Index)
Series_1=Series_1.reindex(index_multi)
print(Series_1)
print(Series_1['PlaceA'])
print(Series_1[:,2020])
>>>
PlaceA 2020 100
2021 200
PlaceB 2020 300
2021 400
PlaceC 2020 500
2021 600
dtype: int64
2020 100
2021 200
dtype: int64
PlaceA 100
PlaceB 300
PlaceC 500
dtype: int64
事实上,这本应是一个三维数组,即(地区,年份,人口),但是我们通过多级索引就成功地让本该储存一维数据的Series对象储存了用二维数组才能储存的数据,我们也可以再加一级索引,从而做到更高维度的压缩
在正式开始讲解创建MultiIndex对象之前,补充讲解一个内容,上面讲到我们通过创建多级索引的方式实现了用Series对象储存用DataFrame对象储存的数据
换而言之,可以理解为将二维DataFrame对象压堆为一维Series对象,对应的,我们可以使用解压堆来让被压堆的一维对象扩展为二维数组
Index=[('PlaceA',2020),('PlaceA',2021),\
('PlaceB',2020),('PlaceB',2021),\
('PlaceC',2020),('PlaceC',2021)]
index_multi=pd.MultiIndex.from_tuples(Index)
Series_1=pd.Series([100,200,300,400,500,600],index=Index)
Series_1=Series_1.reindex(index_multi)
Series_1_unstack=Series_1.unstack()
print(Series_1)
print(type(Series_1))
print('')
print(Series_1_unstack)
print(type(Series_1_unstack))
print('')
print(Series_1_unstack.stack())
print(type(Series_1_unstack.stack()))
>>>
PlaceA 2020 100
2021 200
PlaceB 2020 300
2021 400
PlaceC 2020 500
2021 600
dtype: int64
<class 'pandas.core.series.Series'>
2020 2021
PlaceA 100 200
PlaceB 300 400
PlaceC 500 600
<class 'pandas.core.frame.DataFrame'>
PlaceA 2020 100
2021 200
PlaceB 2020 300
2021 400
PlaceC 2020 500
2021 600
dtype: int64
<class 'pandas.core.series.Series'>
我们创建具有多级索引的Series或DataFrame对象,注意是具有多级索引的Series对象总的来说有两种方法
直接创建具有多级索引的Series或DataFrame对象有如下的两种途径
我们只需要在创建DataFrame或者Series对象时候指定index参数为二维数组即可
Series_1=pd.Series([1,2,3,4],index=[['a','a','b','b'],['a_1','a_2','b_1','b_2']])
DataFrame_1=pd.DataFrame(np.arange(0,16,1).reshape(4,4),\
index=[['a','a','b','b'],['a_1','a_2','b_1','b_2']],\
columns=list('abcd'))
print(Series_1)
print(DataFrame_1)
>>>
a a_1 1
a_2 2
b b_1 3
b_2 4
dtype: int64
a b c d
a a_1 0 1 2 3
a_2 4 5 6 7
b b_1 8 9 10 11
b_2 12 13 14 15
指定二维index对象的关键就在于属于高级索引的低级索引与高级索引的位置对应,即上例中的a_1与a_2,他们都属于高级索引a,所以在高级索引中对应为a,a
实际上可以指定多维数组来创建多级索引
前面讲过,Series对象和DataFrame对象的创建都可以通过字典来创建
只不过Series对象的键名将作为索引名,DataFrame对象的键名将作为列名
例如
dict_1={
'a':1,'b':2,'c':3,'d':4}
Series_1=pd.Series(dict_1)
DataFrame_1=pd.DataFrame({
'a':Series_1,'b':Series_1,'c':Series_1})
print(Series_1)
print(DataFrame_1)
>>>
a 1
b 2
c 3
d 4
dtype: int64
a b c
a 1 1 1
b 2 2 2
c 3 3 3
d 4 4 4
这里,我们只需要把键名改为元组即可
tuple_1=('a','a_1')
tuple_2=('a','a_2')
tuple_3=('b','b_1')
tuple_4=('b','b_2')
dict_1={
tuple_1:1,tuple_2:2,tuple_3:3,tuple_4:4}
Series_1=pd.Series(dict_1)
DataFrame_1=pd.DataFrame({
tuple_1:Series_1,tuple_2:Series_1,tuple_3:Series_1,tuple_4:Series_1})
print(Series_1)
print(DataFrame_1)
>>>
a a_1 1
a_2 2
b b_1 3
b_2 4
dtype: int64
a b
a_1 a_2 b_1 b_2
a a_1 1 1 1 1
a_2 2 2 2 2
b b_1 3 3 3 3
b_2 4 4 4 4
就像前面所说,我们可以创建MultiIndex对象,然后调用DataFrame对象和Series对象的reindex方法来让MultiIndex对象替换掉原有的Index对象,或者直接在创建对象的到时候指定Index参数
下面就将讲解MultiIndex对象的创建方式,主要是调用MultiIndex这个类下的方法
from_array方法类似于直接创建具有多级索引的Series和DataFrame对象一样,通过给定二维数组或者多维数组来创建具有多级索引
MultiIndex_1=pd.MultiIndex.from_arrays([['a','a','a','b','b','b'],['a_1','a_1','a_2','b_1','b_1','b_2'],[1,2,1,1,2,1]])
print(MultiIndex_1)
print(pd.Series(np.arange(10,16,1),index=MultiIndex_1))
>>>
MultiIndex([('a', 'a_1', 1),
('a', 'a_1', 2),
('a', 'a_2', 1),
('b', 'b_1', 1),
('b', 'b_1', 2),
('b', 'b_2', 1)],
)
a a_1 1 10
2 11
a_2 1 12
b b_1 1 13
2 14
b_2 1 15
dtype: int64
from_tuple方法用于从元组中生成MultiIndex对象,就像前面的不同地方不同年份的人口信息的例子一样,通过给定元组,调用from_tuple方法来创建
Index=[('PlaceA',2020),('PlaceA',2021),\
('PlaceB',2020),('PlaceB',2021),\
('PlaceC',2020),('PlaceC',2021)]
MultiIndex_1=pd.MultiIndex.from_tuples(Index)
print(MultiIndex_1)
print(pd.Series([100,200,300,400,500,600],index=MultiIndex_1))
>>>
MultiIndex([('PlaceA', 2020),
('PlaceA', 2021),
('PlaceB', 2020),
('PlaceB', 2021),
('PlaceC', 2020),
('PlaceC', 2021)],
)
PlaceA 2020 100
2021 200
PlaceB 2020 300
2021 400
PlaceC 2020 500
2021 600
dtype: int64
from_product方法用于生成两个索引的笛卡尔内积,这个是最常用的
MultiIndex_1=pd.MultiIndex.from_product([['a','b'],[1,2]])
print(MultiIndex_1)
print(pd.Series([2,4,6,8],index=MultiIndex_1))
>>>
MultiIndex([('a', 1),
('a', 2),
('b', 1),
('b', 2)],
)
a 1 2
2 4
b 1 6
2 8
dtype: int64
前面我们成功地创建了具有多级索引的DataFrame对象和Series对象,如果在多级索引的基础上,我们能够对于多级索引的每一级索引设置名称,那么无疑将会使数据结构更加的清晰
设置多级索引的等级名称主要是对MultiIndex对象的names属性进行设置,因此可以在创建MuitiIndex对象的时候指定names属性,也可以在完成整个DataFrame或Index对象后调用Index对象的names属性进行设置
MultiIndex_1=pd.MultiIndex.from_product([['a','b'],[1,2]],names=['l_1','l_2'])
Series_1=pd.Series(np.arange(1,5,1),index=list('abcd'))
DataFrame_1=pd.DataFrame(np.random.randint(0,10,(4,4)),index=MultiIndex_1)
print(DataFrame_1)
print(Series_1)
Series_1.index.names=['l_1']
print(Series_1)
>>>
0 1 2 3
l_1 l_2
a 1 9 5 9 7
2 2 5 5 9
b 1 1 6 3 1
2 5 1 6 4
a 1
b 2
c 3
d 4
dtype: int64
l_1
a 1
b 2
c 3
d 4
dtype: int64
通过前面的讲述,我们其实不难发现Pandas中各种对象的关系.
Pandas自带Index和MultiIndex对象,以Numpy的数组对象为底层,根据数组对象的维度不同,添加不同数量的Index或者MultiIndex对象,最终构成Series,DataFrame,Panel,Panel4D对象
所以本质上,DataFrame对象的列索引和前面讲解的行索引其实是一样的,都是Index或者MultiIndex对象,所以我们实际上也能够创建多级列索引,并且为多级列索引命名
为DataFrame对象指定多级列索引很简单,只需要指定columns参数为MultiIndex对象即可
Columns=pd.MultiIndex.from_product([['a','b'],[1,2]],names=['le_1','le_2'])
DataFrame_1=pd.DataFrame(np.random.randint(0,10,(4,4)),index=list('abcd'),columns=Columns)
print(DataFrame_1)
>>>
le_1 a b
le_2 1 2 1 2
a 0 4 2 6
b 5 5 1 9
c 3 4 9 3
d 8 8 1 5
接下来我们利用多级索引来模拟一份医学报告
ndex=pd.MultiIndex.from_product([[2019,2020],[1,2]],names=['YEAR','VISIT'])
Columns=pd.MultiIndex.from_product([['Jack','Sarah','Sue'],['HR','TEMP']],names=['NAME','TYPE'])
data=np.round(np.random.randn(4,6),1)
data[:,::2]*=10
data+=37
HealthData=pd.DataFrame(data,index=Index,columns=Columns)
print(HealthData)
>>>
NAME Jack Sarah Sue
TYPE HR TEMP HR TEMP HR TEMP
YEAR VISIT
2019 1 43.0 37.0 23.0 36.6 19.0 38.1
2 37.0 37.6 33.0 35.1 28.0 37.2
2020 1 35.0 35.7 47.0 38.9 28.0 38.8
2 44.0 38.5 37.0 34.3 47.0 38.4
实际上,上面这个数据表具有四个维度,名字,年份,访问次数,体检项目,但是我们通过增加了两个多级索引,实现了降维,将一个四维数据实现了压堆
由于Series对象和DataFrame对象的多级索引的取值与切片本质上是一样的,只不过默认状态下DataFrame对象是对列索引进行索引,所以将放在一起讲,
我们可以通过逐个指定多级索引每一级的值,来获取单个元素
MultiIndex_1=pd.MultiIndex.from_product([['a','b'],[1,2]],names=['Le_1','Le_2'])
Series_1=pd.Series(np.random.randint(0,10,4),index=MultiIndex_1)
print(Series_1)
print(Series_1['b',2])
>>>
Le_1 Le_2
a 1 6
2 7
b 1 4
2 2
dtype: int64
2
我们可以不指定低级的索引,只指定高级索引,即支取索引的某一个层次,这样将会返回一个新的Series对象
MultiIndex_1=pd.MultiIndex.from_product([['a','b','c'],[1,2]],names=['Le_1','Le_2'])
Series_1=pd.Series(np.arange(1,7,1),index=MultiIndex_1)
print(Series_1)
print(Series_1['a'])
print(Series_1['a':'b'])
>>>
Le_1 Le_2
a 1 1
2 2
b 1 3
2 4
c 1 5
2 6
dtype: int64
Le_2
1 1
2 2
dtype: int64
Le_1 Le_2
a 1 1
2 2
b 1 3
2 4
dtype: int64
我们也可以只指定低级索引而不指定高级索引来获得一个新的Series对象.注意,高级索引要用切片
MultiIndex_1=pd.MultiIndex.from_product([['a','b','c'],[1,2]],names=['Le_1','Le_2'])
Series_1=pd.Series(np.arange(1,7,1),index=MultiIndex_1)
print(Series_1)
print(Series_1[:,1])
>>>
Le_1 Le_2
a 1 1
2 2
b 1 3
2 4
c 1 5
2 6
dtype: int64
Le_1
a 1
b 3
c 5
dtype: int64
对于DataFrame或者Series对象,我们可以使用花哨的索引来获取值
Index=pd.MultiIndex.from_product([['a','b','c'],[1,2,3]],names=['Le_1','Le_2'])
Columns=pd.MultiIndex.from_product([['A','B','C'],[1,2,3]],names=['Le_1','Le_2'])
Series_1=pd.Series(np.random.randint(0,10,9),index=Index)
DataFrame_1=pd.DataFrame(np.random.randint(0,10,(9,9)),index=Index,columns=Columns)
print(Series_1)
print(Series_1[Series_1 >= 3])
print(DataFrame_1)
print(DataFrame_1[DataFrame_1 >=3])
>>>
Le_1 Le_2
a 1 5
2 7
3 5
b 1 9
2 3
3 0
c 1 7
2 3
3 8
dtype: int64
Le_1 Le_2
a 1 5
2 7
3 5
b 1 9
2 3
c 1 7
2 3
3 8
dtype: int64
Le_1 A B C
Le_2 1 2 3 1 2 3 1 2 3
Le_1 Le_2
a 1 2 5 9 8 1 5 4 2 6
2 6 0 0 2 0 6 7 3 7
3 0 8 5 9 7 9 4 3 0
b 1 2 9 4 0 0 1 3 3 7
2 9 1 7 6 7 2 0 9 1
3 1 0 9 1 9 9 6 2 9
c 1 8 6 1 3 5 1 6 8 7
2 0 3 3 3 2 6 7 5 3
3 3 5 2 4 7 2 1 9 2
Le_1 A B C
Le_2 1 2 3 1 2 3 1 2 3
Le_1 Le_2
a 1 NaN 5.0 9.0 8.0 NaN 5.0 4.0 NaN 6.0
2 6.0 NaN NaN NaN NaN 6.0 7.0 3.0 7.0
3 NaN 8.0 5.0 9.0 7.0 9.0 4.0 3.0 NaN
b 1 NaN 9.0 4.0 NaN NaN NaN 3.0 3.0 7.0
2 9.0 NaN 7.0 6.0 7.0 NaN NaN 9.0 NaN
3 NaN NaN 9.0 NaN 9.0 9.0 6.0 NaN 9.0
c 1 8.0 6.0 NaN 3.0 5.0 NaN 6.0 8.0 7.0
2 NaN 3.0 3.0 3.0 NaN 6.0 7.0 5.0 3.0
3 3.0 5.0 NaN 4.0 7.0 NaN NaN 9.0 NaN
具有层级索引的Series和DataFrame对象底层依旧是Numpy数组,因此我们依旧可以使用索引器来暴露底层的Numpy数组接口属性来获取值
iloc索引器索引的依旧是隐式索引,所以没有什么变化
Index=pd.MultiIndex.from_product([['a','b','c'],[1,2,3]],names=['Le_1','Le_2'])
Columns=pd.MultiIndex.from_product([['A','B','C'],[1,2,3]],names=['Le_1','Le_2'])
Series_1=pd.Series(np.random.randint(0,10,9),index=Index)
DataFrame_1=pd.DataFrame(np.random.randint(0,10,(9,9)),index=Index,columns=Columns)
print(Series_1)
print(Series_1.iloc[1:4])
print(DataFrame_1)
print(DataFrame_1.iloc[:,1:3])
>>>
Le_1 Le_2
a 1 1
2 9
3 2
b 1 5
2 8
3 8
c 1 7
2 6
3 2
dtype: int64
Le_1 Le_2
a 2 9
3 2
b 1 5
dtype: int64
Le_1 A B C
Le_2 1 2 3 1 2 3 1 2 3
Le_1 Le_2
a 1 6 4 9 7 3 7 1 9 6
2 5 1 4 7 3 1 6 3 6
3 5 6 5 8 3 7 6 0 0
b 1 1 3 4 8 4 2 4 4 4
2 5 7 4 8 1 0 1 7 6
3 1 8 9 1 1 2 8 3 0
c 1 7 1 3 0 9 9 0 8 8
2 0 3 2 8 9 5 9 2 9
3 8 1 9 8 9 0 6 6 3
Le_1 A
Le_2 2 3
Le_1 Le_2
a 1 4 9
2 1 4
3 6 5
b 1 3 4
2 7 4
3 8 9
c 1 1 3
2 3 2
3 1 9
loc索引器索引的显示索引,因此由于原始的Index对象被替换为MultiIndex对象,所以在显示索引的索引上会出现变化
和普通的索引不同,对于具有多级索引的Series或者DataFrame对象索引我们需要传递多级索引构成的索引元组
Index=pd.MultiIndex.from_product([['a','b','c'],[1,2,3]],names=['Le_1','Le_2'])
Series_1=pd.Series(np.random.randint(0,10,9),index=Index)
print(Series_1)
print(Series_1.loc[('a',2):('c',1)])
>>>
Le_1 Le_2
a 1 4
2 0
3 0
b 1 3
2 9
3 8
c 1 7
2 1
3 0
dtype: int64
Le_1 Le_2
a 2 0
3 0
b 1 3
2 9
3 8
c 1 7
dtype: int64
但实际上,使用loc索引器非常少见,第一个原因就是传递的元组很容易搞混,在具有多个层级的索引时,其次对元组时候用切片操作会报错,再者,loc索引器对DataFrame对象的支持不是很好,因此一般使用下面的方法
为了解决我们使用loc索引器索引显示索引时的不便,Pandas内置了另外一个对象IndexSlice,来帮助我们索引显示索引
我们只需要对创建的IndexSlice对象使用切片,并将获取的切片传递给loc索引器即可
Index=pd.MultiIndex.from_product([['a','b','c'],[1,2,3]],names=['Le_1','Le_2'])
Columns=pd.MultiIndex.from_product([['A','B','C'],[1,2,3]],names=['Le_1','Le_2'])
Idx=pd.IndexSlice
DataFrame_1=pd.DataFrame(np.random.randint(0,10,(9,9)),index=Index,columns=Columns)
print(DataFrame_1)
print(DataFrame_1.loc[Idx[:,1],Idx[:,2:3]])
>>>
Le_1 A B C
Le_2 1 2 3 1 2 3 1 2 3
Le_1 Le_2
a 1 8 7 4 8 4 4 5 0 4
2 6 4 1 6 2 9 0 9 3
3 9 8 8 8 7 5 2 7 2
b 1 5 2 8 3 0 5 3 5 1
2 8 8 4 4 2 5 5 4 1
3 9 8 3 0 8 0 7 0 7
c 1 9 5 2 5 9 6 4 7 2
2 3 4 7 1 7 5 2 2 2
3 3 3 5 7 7 6 7 3 3
Le_1 A B C
Le_2 2 3 2 3 2 3
Le_1 Le_2
a 1 7 4 4 4 0 4
b 1 2 8 0 5 5 1
c 1 5 2 9 6 7 2
如上面的代码,我们只需要给IndexSlice对象进行切片即可
使用多级索引的关键就是掌握有效的数据转换方法,为此Pandas提供了许多操作,可以让数据在内容保持不变的同时按照需求进行行列转换
前面讲过的stack和unstack就是可以用于行列索引转换的
前面我们讲解过对MultiIndex对象进行切片这类取值操作,但是对MultiIndex对象进行切片操作的基础是MultiIndex中的索引是有序的,即符合字典序
如果MultiIndex对象不满足字典序,那么对MultiIndex对象的大多数切片操作都会失效
MultiIndex_1=pd.MultiIndex.from_product([['a','b','c'],[1,2]],names=['Le_1','Le_2'])
MultiIndex_2=pd.MultiIndex.from_product([['a','c','b'],[1,2]],names=['Le_1','Le_2'])
Series_1=pd.Series([1,2,3,4,5,6],index=MultiIndex_1)
Series_2=pd.Series([1,2,3,4,5,6],index=MultiIndex_2)
print(Series_1)
print(Series_1['a':'b'])
print(Series_2['a':'b'])
>>>
Le_1 Le_2
a 1 1
2 2
b 1 3
2 4
c 1 5
2 6
dtype: int64
Le_1 Le_2
a 1 1
2 2
b 1 3
2 4
dtype: int64
Traceback (most recent call last):
File "TryPandas.py", line 410, in <module>
print(Series_2['a':'b'])
File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/site-packages/pandas/core/series.py", line 910, in __getitem__
return self._get_with(key)
File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/site-packages/pandas/core/series.py", line 915, in _get_with
return self._slice(key)
File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/site-packages/pandas/core/series.py", line 865, in _slice
slobj = self.index._convert_slice_indexer(slobj, kind=kind or "getitem")
File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/site-packages/pandas/core/indexes/base.py", line 2963, in _convert_slice_indexer
indexer = self.slice_indexer(start, stop, step, kind=kind)
File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/site-packages/pandas/core/indexes/base.py", line 4713, in slice_indexer
start_slice, end_slice = self.slice_locs(start, end, step=step, kind=kind)
File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/site-packages/pandas/core/indexes/multi.py", line 2543, in slice_locs
return super().slice_locs(start, end, step, kind=kind)
File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/site-packages/pandas/core/indexes/base.py", line 4926, in slice_locs
start_slice = self.get_slice_bound(start, "left", kind)
File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/site-packages/pandas/core/indexes/multi.py", line 2487, in get_slice_bound
return self._partial_tup_index(label, side=side)
File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/site-packages/pandas/core/indexes/multi.py", line 2548, in _partial_tup_index
f"Key length ({len(tup)}) was greater than MultiIndex lexsort depth"
pandas.errors.UnsortedIndexError: 'Key length (1) was greater than MultiIndex lexsort depth (0)'
虽然报错信息非常不明显,但是我们前面讲过了这是因为我们的MultiIndex对象是无序的
为此,Pandas提供了许多便捷的操作来帮助我们完成排序,例如sort_index()和sortlevel()
这里只讲解用于索引排序的sort_index()方法
sort_index()方法是Series对象和DataFrame对象内置的方法,直接调用即可
MultiIndex_1=pd.MultiIndex.from_product([['a','c','b'],[1,2]],names=['Le_1','Le_2'])
MultiIndex_2=pd.MultiIndex.from_product([['A','C','B'],[1,2]],names=['Le_1','Le_2'])
Series_1=pd.Series([1,2,3,4,5,6],index=MultiIndex_1)
DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,6)),index=MultiIndex_1,columns=MultiIndex_2)
print(Series_1)
print(Series_1.sort_index())
print(DataFrame_1)
print(DataFrame_1.sort_index())
>>>
Le_1 Le_2
a 1 1
2 2
c 1 3
2 4
b 1 5
2 6
dtype: int64
Le_1 Le_2
a 1 1
2 2
b 1 5
2 6
c 1 3
2 4
dtype: int64
Le_1 A C B
Le_2 1 2 1 2 1 2
Le_1 Le_2
a 1 9 7 8 3 3 4
2 5 0 5 5 8 5
c 1 1 8 8 8 1 1
2 0 4 8 0 7 1
b 1 9 1 4 8 8 6
2 6 8 7 6 8 7
Le_1 A C B
Le_2 1 2 1 2 1 2
Le_1 Le_2
a 1 9 7 8 3 3 4
2 5 0 5 5 8 5
b 1 9 1 4 8 8 6
2 6 8 7 6 8 7
c 1 1 8 8 8 1 1
2 0 4 8 0 7 1
前面简单的讲解过了stack与unstack方法,这里补充讲解一下,我们实际上可以通过制定level参数设置转换的索引层级
索引层级从高到低对应从0递增
MultiIndex_1=pd.MultiIndex.from_product([['PlaceA','PlaceB','PlaceC'],[2020,2021]],names=['PLACE','YEAR'])
Series_1=pd.Series([100,200,300,400,500,600],index=MultiIndex_1)
print(Series_1)
print(Series_1.unstack(0))
print(Series_1.unstack(1))
>>>
PLACE YEAR
PlaceA 2020 100
2021 200
PlaceB 2020 300
2021 400
PlaceC 2020 500
2021 600
dtype: int64
PLACE PlaceA PlaceB PlaceC
YEAR
2020 100 300 500
2021 200 400 600
YEAR 2020 2021
PLACE
PlaceA 100 200
PlaceB 300 400
PlaceC 500 600
我们对索引数据维度转换的另一种方法是行列标签转换为数据内容,可以通过reset_index方法实现
reset_index方法实际上是重新设置行列索引,不过一个额外的作用就是将原有的行列索引转换为数据内容,同时原有的索引名将作为列索引名
我们可以指定name属性来为列设置名称
MultiIndex_1=pd.MultiIndex.from_product([['PlaceA','PlaceB','PlaceC'],[2020,2021]],names=['PLACE','YEAR'])
Series_1=pd.Series([100,200,300,400,500,600],index=MultiIndex_1)
print(Series_1)
print(Series_1.reset_index())
print(Series_1.reset_index(name='population'))
>>>
PLACE YEAR
PlaceA 2020 100
2021 200
PlaceB 2020 300
2021 400
PlaceC 2020 500
2021 600
dtype: int64
PLACE YEAR 0
0 PlaceA 2020 100
1 PlaceA 2021 200
2 PlaceB 2020 300
3 PlaceB 2021 400
4 PlaceC 2020 500
5 PlaceC 2021 600
PLACE YEAR population
0 PlaceA 2020 100
1 PlaceA 2021 200
2 PlaceB 2020 300
3 PlaceB 2021 400
4 PlaceC 2020 500
5 PlaceC 2021 600
在解决实际问题时,我们通常是将原始输入的列转换成MultiIndex对象,然后使用Pandas的方法来处理处理数据
将列转换为MultiIndex对象,通常使用reset_index()方法的逆方法set_index()方法
MultiIndex_1=pd.MultiIndex.from_product([['PlaceA','PlaceB','PlaceC'],[2020,2021]],names=['PLACE','YEAR'])
Series_1=pd.Series([100,200,300,400,500,600],index=MultiIndex_1)
Series_2=Series_1.reset_index()
print(Series_2)
print(Series_2.set_index(['PLACE','YEAR'])
>>>
PLACE YEAR 0
0 PlaceA 2020 100
1 PlaceA 2021 200
2 PlaceB 2020 300
3 PlaceB 2021 400
4 PlaceC 2020 500
5 PlaceC 2021 600
0
PLACE YEAR
PlaceA 2020 100
2021 200
PlaceB 2020 300
2021 400
PlaceC 2020 500
2021 600
对于Series对象和DataFrame对象,Pandas内置了对数据进行统计的方法,,结合多级索引,我们可以给这些方法指定level参数来指定统计的行
如果需要统计列的话指定不仅需要指定列索引,还要指定axis参数
MultiIndex_1=pd.MultiIndex.from_product([['a','b','c'],[1,2]],names=['Le_1','Le_2'])
MultiIndex_2=pd.MultiIndex.from_product([['A','B','C'],[1,2]],names=['Row_1','Row_2'])
DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,6)),index=MultiIndex_1,columns=MultiIndex_2)
print(DataFrame_1)
print(DataFrame_1.sum(level='Le_1',axis=0))
print(DataFrame_1.sum(level='Row_2',axis=1))
>>>
Row_1 A B C
Row_2 1 2 1 2 1 2
Le_1 Le_2
a 1 5 2 1 0 1 2
2 0 4 4 2 1 1
b 1 3 8 6 5 3 2
2 3 8 0 0 9 6
c 1 5 5 7 7 4 3
2 6 6 2 8 3 4
Row_1 A B C
Row_2 1 2 1 2 1 2
Le_1
a 5 6 5 2 2 3
b 6 16 6 5 12 8
c 11 11 9 15 7 7
Row_2 1 2
Le_1 Le_2
a 1 7 4
2 5 7
b 1 12 15
2 12 14
c 1 16 15
2 11 18