首页
学习
活动
专区
圈层
工具
发布
50 篇文章
1
【原创佳作】介绍Pandas实战中一些高端玩法
2
pandas 如何实现 excel 中的汇总行?
3
pandas多级索引的骚操作!
4
40000字 Matplotlib 实操干货,真的全!
5
利用Python搞定女朋友的小情绪~
6
Python 绘制惊艳的瀑布图
7
6种方式创建多层索引
8
Python 进阶指南(编程轻松进阶):三、使用 Black 工具来格式化代码
9
数据科学 IPython 笔记本 9.6 聚合:最小、最大和之间的任何东西
10
精通 Pandas 探索性分析:1~4 全
11
高手系列!数据科学家私藏pandas高阶用法大全 ⛵
12
总结了67个pandas函数,完美解决数据处理,拿来即用!
13
PyAutoGUI,一个Python办公自动化利器!
14
解放双手|利用 PyAutoGUI 快速构建自动化操作脚本
15
Python中内置数据库!SQLite使用指南! ⛵
16
数据分析索引总结(中)Pandas多级索引
17
数据分析索引总结(下)Pandas索引技巧
18
数据分析索引总结(上)Pandas单级索引
19
网友需求系列01-Python-matplotlib定制化刻度(主副)绘制
20
用Python自动生成数据分析报告
21
手把手教你用Python操纵Word自动编写离职报告
22
pandas transform 数据转换的 4 个常用技巧!
23
30段极简Python代码:这些小技巧你都Get了么
24
数据处理遇到麻烦不要慌,5个优雅的Numpy函数助你走出困境
25
数据分析最有用的Top 50 Matplotlib图(带有完整的Python代码)(上)
26
数据分析最有用的Top 50 Matplotlib图(带有完整的Python代码)(下)
27
数据分析之Pandas变形操作总结
28
数据分析之Pandas缺失数据处理
29
数据分析之Pandas合并操作总结
30
数据分析之Pandas分组操作总结
31
学习用Pandas处理分类数据!
32
如何用Pandas处理文本数据?
33
Pandas处理时序数据(初学者必会)!
34
Python高阶函数使用总结!
35
机器学习在金融风控的经验总结!
36
你知道怎么用Pandas绘制带交互的可视化图表吗?
37
6个提升效率的pandas小技巧
38
Python数据分析库pandas高级接口dt和str的使用
39
pandas 拼接 concat 5 个常用技巧!
40
pandas分组8个常用技巧!
41
pandas 文本处理大全
42
pandas 筛选数据的 8 个骚操作
43
pandas 分类数据处理大全(附代码)
44
68 个Python内置函数,你用过几个?
45
太秀了!用 pandas 搞定 24 张 Excel 报表
46
用 Python 的 Template 类生成文件报告
47
码如其人,同学你能写一手漂亮的Python函数吗
48
Python处理图片九宫格,炫酷朋友圈
49
Python排序傻傻分不清?一文看透sorted与sort用法
50
python-docx操作word文件(
清单首页python文章详情

数据分析索引总结(中)Pandas多级索引

作者:闫钟峰,Datawhale优秀学习者

寄语:本文介绍了创建多级索引、多层索引切片、多层索引中的slice对象、索引层的交换等内容。

创建多级索引

1. 通过from_tuple或from_arrays

① 直接从元组列表创建多重索引

代码语言:javascript
复制
tuples = [('A','a'),('A','b'),('B','a'),('B','b')]
mul_index = pd.MultiIndex.from_tuples(tuples, names=('Upper', 'Lower'))
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=mul_index

② 利用zip创建元组

多重索引本质上的结构是一个由元组构成的list

代码语言:javascript
复制
L1 = list('AABB')
L2 = list('abab')
tuples = list(zip(L1,L2))
mul_index = pd.MultiIndex.from_tuples(tuples, names=('Upper', 'Lower'))
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=mul_index)

注意,如果用于创建多重索引的由tuple组成的list本身是未排序的, 那么创建的df也未排序。

代码语言:javascript
复制
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=pd.MultiIndex.from_tuples(list(zip(L2,L1)), names=('Lower', 'Upper')))

为了便于使用, 可以使用sort_index()进行排序

代码语言:javascript
复制
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=pd.MultiIndex.from_tuples(list(zip(L2,L1)), names=('Lower', 'Upper'))).sort_index()

③ 通过Array(或列表构成的列表)创建

内层的list会自动转成元组

代码语言:javascript
复制
arrays = [['A','a'],['A','b'],['B','a'],['B','b']]
mul_index = pd.MultiIndex.from_tuples(arrays, names=('Upper', 'Lower'))
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=mul_index)
代码语言:javascript
复制
arrays = [['A','a'],['B','a'],['A','b'],['B','b']]
mul_index = pd.MultiIndex.from_tuples(arrays, names=('Upper', 'Lower'))
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=mul_index)

尽管多重索引内部是个由tuple组成的list, 但由于顺序不同, 并不能视为相等的多重索引。但直接比较两个顺序不同的多重索引, 返回值是一个布尔值array, 并不如预期的那样。

代码语言:javascript
复制
sorted_multi_index=pd.DataFrame({'Score':['perfect','good','fair','bad']},index=mul_index).sort_index().index
sorted_multi_index==mul_index

如果是两个list, 改变顺序后与原始list相比较, 返回值只有一个 False。

代码语言:javascript
复制
[('A', 'a'),  ('B', 'a'), ('A', 'b'), ('B', 'b')]==[('A', 'a'),  ('A', 'b'), ('B', 'a'), ('B', 'b')]

创建一个由二元list构成的list

代码语言:javascript
复制
arr=np.random.randint(1,5,20).reshape(-1,2)

必须把array转化为list才能用pd.MultiIndex.from_tuples 函数创建层次化索引。

代码语言:javascript
复制
pd.MultiIndex.from_tuples(list(arr),names=('left','right'))

使用上述多重索引创建df后,要记得多加一个sort_index(), 以使得df的结果看起来更整齐。

代码语言:javascript
复制
dftemp=pd.DataFrame(np.random.randn(20).reshape(10,2), index=pd.MultiIndex.from_tuples(list(arr),names=('left','right'))).sort_index()

多重索引构造器

代码语言:javascript
复制
pd.MultiIndex.from_tuples??
# tuples: list / sequence of tuple-likes Each tuple is the index of one row/column.

2. 通过from_product

笛卡尔乘积---可能很多时候并不需要用笛卡儿积的所有结果作为索引。

代码语言:javascript
复制
L1 = ['A','B']
L2 = ['a','b']
pd.MultiIndex.from_product([L1,L2],names=('Upper', 'Lower'))
代码语言:javascript
复制
Make a MultiIndex from the cartesian product of multiple iterables.
# iterables : list / sequence of iterables  Each iterable has unique labels for each level of the index.
pd.MultiIndex.from_product??

3. 指定df中的列创建(set_index方法)

传入两个以上的列名时,必须以list的形式传入(tuple不行)。注意原来的索引'ID'已经被丢弃了--这是因为set_index的 drop 参数默认值 drop=True。

代码语言:javascript
复制
df_using_mul = df.set_index(['Class','Address']) 
df_using_mul.head()

由于drop参数默认值是True,上述语法并不等价于分别将两列设置为索引。

代码语言:javascript
复制
df1= df.set_index('Class')

第二次将某列设置为索引时,会丢弃原来的索引

代码语言:javascript
复制
df2 = df1.set_index('Address')

第二次指定索引时,必须指定参数 append=True 才会保留原来的索引---这个参数默认是False(丢弃原始索引)。

代码语言:javascript
复制
df3= df1.set_index('Address',append=True)
代码语言:javascript
复制
df.set_index??

多层索引切片

使用第一层的索引,会把该索引下的所有行都选中,除非该索引的二级索引只有一个,否则返回行数不会等于一行。

代码语言:javascript
复制
df_using_mul.loc['C_1']

如何获取次级索引为指定值的行??

方法1:交换索引层级

代码语言:javascript
复制
df_using_mul.swaplevel('Class','Address').loc['street_1']

方法2: 使用针对索引的 get_level_values 函数,指定索引层级为第二层

代码语言:javascript
复制
df_using_mul.loc[df_using_mul.index.get_level_values(1) == 'street_1']
代码语言:javascript
复制
df_using_mul.index.names
df_using_mul.index.get_level_values??

方法3:使用query方法,传入 次级索引名称等于指定值--需要使用引号

代码语言:javascript
复制
df_using_mul.query('Address == "street_1"')

相当于将内层索引当作列,等价于

代码语言:javascript
复制
select * from df_using_mul where Address = 'street_1'

对原始df使用query可以获取同样的行

代码语言:javascript
复制
df.query('Address=="street_1"')

方法4:使用pd.IndexSlice对层次索引按次级索引的值进行切片

代码语言:javascript
复制
df_using_mul.loc(axis=0)[pd.IndexSlice[:, 'street_1']]
代码语言:javascript
复制
pd.IndexSlice??

1. 一般切片

当索引不排序时,单个索引会报出性能警告

代码语言:javascript
复制
df_using_mul.loc['C_2','street_5']

该函数检查索引是否排序

代码语言:javascript
复制
df_using_mul.index.is_lexsorted()

根据索引排序后不再报出性能警告

代码语言:javascript
复制
df_using_mul.sort_index().loc['C_2','street_5']

当不排序时,不能使用多层切片

代码语言:javascript
复制
df_using_mul.loc[('C_2','street_5'):] 报错

故先要进行排序,注意此处由于使用了loc,因此仍然包含右端点

代码语言:javascript
复制
df_using_mul.sort_index().loc[('C_2','street_6'):('C_3','street_4')]

使用索引标签进行切片, 是个闭区间非元组也是合法的,表示选中该层所有元素

代码语言:javascript
复制
df_using_mul.sort_index().loc[('C_2','street_7'):'C_3'].head()
df_using_mul.sort_index().loc['C_1':'C_2']#.head(10)

2. 第一类特殊情况:由元组构成列表

选出某几个元素,每个元组的第一个元素是第一层索引的可能取值,元组的第二个元素是第二层索引的可能取值...精确到最内层索引

代码语言:javascript
复制
df_using_mul.sort_index().loc[[('C_2','street_7'),('C_3','street_2')]]

如果第一个本应该是元组的地方传入了一个元素, 不会报错, 但结果与预期不一样。

代码语言:javascript
复制
df_using_mul.sort_index().loc[['C_2',('C_3','street_2')]]

但是反过来, 第一个传入元组, 第二个传入一个元素, 就会报错TypeError: Expected tuple, got str。

所以这里大概是有一个自动推断的过程:如果第一个位置是元组,那就默认是按照元组的相应位置去对应相应层级的索引的值;如果第一个位置是元素, 那就默认直接对应第一层索引的相应取值。

代码语言:javascript
复制
df_using_mul.sort_index().loc[[('C_2','street_7'),'C_3']]

上边不会报错,但结果与预期不一样,是由于第一层索引没有以元组('C_3','street_2')为索引的。

使用元素和元组组成的切片时, 就不会报错了, 但这时候需注意传入的切片不应该再包含在[]内。

代码语言:javascript
复制
df_using_mul.sort_index().loc['C_2':('C_3','street_2')]

3. 第二类特殊情况:由列表构成元组

选出第一层在‘C_2’和'C_3'中且第二层在'street_4'和'street_7'中的行。

代码语言:javascript
复制
df_using_mul.sort_index().loc[(['C_2','C_3'],['street_4','street_7']),:]

注意,loc方法必须是先选行再选列,因此列表构成的元组后的逗号和冒号不能省略。

代码语言:javascript
复制
df_using_mul.sort_index().loc[(['C_2','C_3'],['street_4','street_7'])]
# KeyError: "None of [Index(['street_4', 'street_7'], dtype='object')] are in the [columns]"

下面语句不等价于使用zip将两个list绑定,而是等价于将两个list做笛卡尔乘积。

代码语言:javascript
复制
df_using_mul.sort_index().loc[zip(['C_2','C_3'],['street_4','street_7']),:]

定义一个为两个list做笛卡尔乘积的函数

代码语言:javascript
复制
def list_product(list1,list2):
    lst=[]
    for a in list1:
        for b in list2:
            lst.append((a,b))
    return lst

实际上, 传入两个列表,相当于将两个list做笛卡尔乘积--这说明两个列表并不需要长度相等。

代码语言:javascript
复制
df_using_mul.sort_index().loc[list_product(['C_2','C_3'], ['street_4','street_7']),:]

传入两个不等长的list的笛卡尔乘积

代码语言:javascript
复制
df_using_mul.sort_index().loc[list_product(['C_2','C_3'], ['street_1','street_4','street_7']),:]

和传入两个list组成的元组效果是一致的。

代码语言:javascript
复制
df_using_mul.sort_index().loc[(['C_2','C_3'], ['street_1','street_4','street_7']),:]

多层索引中的slice对象

行索引和列索引均有两个层级

代码语言:javascript
复制
L1,L2 = ['A','B','C'],['a','b','c']
mul_index1 = pd.MultiIndex.from_product([L1,L2],names=('Upper', 'Lower'))
L3,L4 = ['D','E','F'],['d','e','f']
mul_index2 = pd.MultiIndex.from_product([L3,L4],names=('Big', 'Small'))
df_s = pd.DataFrame(np.random.rand(9,9),index=mul_index1,columns=mul_index2)
df_s
代码语言:javascript
复制
# Create an object to more easily perform multi-index slicing.
pd.IndexSlice??

索引Slice的使用非常灵活

df_s.sum()默认为对列求和,因此返回一个长度为9的数值列表。

代码语言:javascript
复制
df_s.loc[pd.IndexSlice['B':,df_s['D']['d']>0.3],pd.IndexSlice[df_s.sum()>4]]
# 对行的筛选等价于 select * from (select * from df_s where (Upper>'B' or D_d>0.3) )
# 如果不使用连接等手段, sql无法实现类似的对列名的筛选---特别地,sql中没有层级索引

接下来使用pd.IndexSlice函数找出那些列的和大于4的列,分解开来看--列的筛选。

代码语言:javascript
复制
pd.IndexSlice[df_s.sum()>4]

分解开来看--行的筛选,注意观察发现,最终结果没有第一次行索引为A的, 但下边的结果中第一层索引为A的有等于True的--这是因为前边还有个slice,表示的是前边的从B开始的切片。

代码语言:javascript
复制
pd.IndexSlice['B':,df_s['D']['d']>0.3]

去掉切片

代码语言:javascript
复制
pd.IndexSlice[df_s['D']['d']>0.3]

最终,被传递给loc的是两个布尔值的Series,和原始的Series对齐,然后根据布尔值是否为真筛选出最终的结果。

代码语言:javascript
复制
df_s.loc[pd.IndexSlice['B':,df_s['D']['d']>0.3],pd.IndexSlice[df_s.sum()>4]]

索引层的交换

1. swaplevel方法(两层交换)

代码语言:javascript
复制
df_using_mul.head()

交换索引后, 使用sort_index使得显示结果更加整齐。

代码语言:javascript
复制
df_using_mul.swaplevel(i=1,j=0,axis=0).sort_index().head()
代码语言:javascript
复制
# Swap levels i and j in a MultiIndex on a particular axis.
# 有必要增加一个sort_index=True的参数, 使得可以通过该参数设置交换索引后是否按索引重新排序
df_using_mul.swaplevel??

2. reorder_levels方法(多层交换)

代码语言:javascript
复制
df_muls = df.set_index(['School','Class','Address'])
df_muls.head(10)

传入的第一个参数是一个类list的对象, 是原来索引层级(用默认整数表示)的一个排列。第二个参数指定需要重排索引的轴,0表示行轴,也就是重排行索引的层级。

代码语言:javascript
复制
df_muls.reorder_levels([2,0,1],axis=0).sort_index().head()

如果索引有name,可以直接使用name的一个排列

代码语言:javascript
复制
df_muls.reorder_levels(['Address','School','Class'],axis=0).sort_index().head()
代码语言:javascript
复制
# Rearrange index levels using input order. May not drop or duplicate levels.
# 注意后一句话--这使得该函数和df的类似函数 reindex 相比功能更弱
df_muls.reorder_levels??
下一篇
举报
领券