Pandas多级层索引
官方一点的说法:Pandas中的多级索引(MultiIndex)是一种允许在单个轴上拥有多个索引级别的索引对象。这种索引结构在处理具有复杂层次结构的数据时非常有用,因为它能够提供更丰富的数据访问和操作能力。
我自己的理解是在excel表格中一个大类里面还有其他子类,子类存储数据。
今天出这样一个notebook主要是想到之前在使用pandas中的groupby传递多列作为一个唯一分组再使用agg或apply时会返回一个多级索引的dataframe或series,那时并不会看也不知道该怎么操作,就只会用transform()函数运算。
最后算完了只要一组数据后还得去重哈哈哈哈。所以现在出一篇可能会给大家有点参考顺带给自己巩固一下。
多层级索引的概念
多层级索引(MultiIndex)是Pandas库中一种非常重要的数据结构,它允许在DataFrame或Series的轴(行或列)上拥有多个(两个以上)索引级别。这种结构能够增强数据集的维度管理,便于组织复杂结构数据,为数据集增加更多的维度,使得我们能够更好地组织和表示具有复杂结构的数据。
模块导入
| import pandas as pd |
| import numpy as np |
| from random import choice |
使用groupby和agg返回的多层索引dataframe
创建一个基础dataframe
创建的dataframe内容有三列分别是:产品、月份、数量
| |
| values = np.random.randint(1,50,size=12) |
| month = ['一月','二月','三月'] |
| product = ['A','B'] |
| temp_1,temp_2 = [] , [] |
| for i in range(0,12): |
| temp_1.append(choice(month)) |
| temp_2.append(choice(product)) |
| data = [temp_1,temp_2,values] |
| data_transposed = [list(x) for x in zip(*data)] |
| data_transposed |
| [['三月', 'B', np.int32(23)], |
| ['一月', 'B', np.int32(26)], |
| ['二月', 'A', np.int32(48)], |
| ['三月', 'B', np.int32(3)], |
| ['一月', 'B', np.int32(25)], |
| ['二月', 'A', np.int32(20)], |
| ['三月', 'A', np.int32(14)], |
| ['二月', 'A', np.int32(25)], |
| ['一月', 'A', np.int32(21)], |
| ['三月', 'A', np.int32(14)], |
| ['二月', 'A', np.int32(46)], |
| ['二月', 'B', np.int32(23)]] |
| |
| df = pd.DataFrame(data=data_transposed,columns=['月份','产品','数量']) |
| df , df.index |
| ( 月份 产品 数量 |
| 0 三月 B 23 |
| 1 一月 B 26 |
| 2 二月 A 48 |
| 3 三月 B 3 |
| 4 一月 B 25 |
| 5 二月 A 20 |
| 6 三月 A 14 |
| 7 二月 A 25 |
| 8 一月 A 21 |
| 9 三月 A 14 |
| 10 二月 A 46 |
| 11 二月 B 23, |
| RangeIndex(start=0, stop=12, step=1)) |
上面这个dataframe就是模拟了一下平时做数据的时候可能会读取到这种样式的数据,但这样看密密麻麻不美观也不容易看出每个产品每月的总数量。那就使用groupby来进行分组再聚合运算
| |
| df_group = df.groupby(['产品','月份'])['数量'].agg('sum') |
| df_group |
| 产品 月份 |
| A 一月 21 |
| 三月 28 |
| 二月 139 |
| B 一月 51 |
| 三月 26 |
| 二月 23 |
| Name: 数量, dtype: int32 |
这里因为分组已经拿走了两列作为唯一值索引,剩下数量这一列数据所以返回的是一个多层级索引的series,可以使用pd.dataframe转换
| df_group = pd.DataFrame(df_group) |
| df_group |
|
|
数量 |
产品 |
月份 |
|
A |
一月 |
21 |
三月 |
28 |
二月 |
139 |
B |
一月 |
51 |
三月 |
26 |
二月 |
23 |
| MultiIndex([('A', '一月'), |
| ('A', '三月'), |
| ('A', '二月'), |
| ('B', '一月'), |
| ('B', '三月'), |
| ('B', '二月')], |
| names=['产品', '月份']) |
可以看到分组聚合运算之后原来df的索引是整数索引,而分组聚合后索引变成了每行一个唯一分组的索引
用AI解释的原话就是:在Pandas中,当你使用groupby方法并传入两个或更多列名作为分组键时,Pandas会创建一个多级索引(也称为层次化索引或复合索引)来标识每个唯一的分组。这是因为每个分组现在由多个列的值共同定义,所以需要一个能够反映这种多维分组结构的索引。
我的学习小插曲
这是一个比较方便快捷的做到分组汇总的方法,如果是我以前做的话是这样的:
| |
| df_sum = df.copy() |
| df_sum['汇总'] = df.groupby(['产品','月份'])['数量'].transform('sum') |
| df_sum |
|
月份 |
产品 |
数量 |
汇总 |
0 |
三月 |
B |
23 |
26 |
1 |
一月 |
B |
26 |
51 |
2 |
二月 |
A |
48 |
139 |
3 |
三月 |
B |
3 |
26 |
4 |
一月 |
B |
25 |
51 |
5 |
二月 |
A |
20 |
139 |
6 |
三月 |
A |
14 |
28 |
7 |
二月 |
A |
25 |
139 |
8 |
一月 |
A |
21 |
21 |
9 |
三月 |
A |
14 |
28 |
10 |
二月 |
A |
46 |
139 |
11 |
二月 |
B |
23 |
23 |
| df_sum.drop_duplicates(subset=['产品','月份'],inplace=True) |
| df_sum.drop(columns='数量',inplace=True) |
| df_sum |
|
月份 |
产品 |
汇总 |
0 |
三月 |
B |
26 |
1 |
一月 |
B |
51 |
2 |
二月 |
A |
139 |
6 |
三月 |
A |
28 |
8 |
一月 |
A |
21 |
11 |
二月 |
B |
23 |
这就是我以前做的步骤,这种步骤做完了并不能让你直观的看到A、B每个月份的总数量是多少,还是达不到想要的效果也不美观。
当时只会傻傻的这样用,然后当自己要做成多层级索引的时候再打开excel表格自己手工做。
后来看别人写的代码了解到,agg和transform返回的结果是不一样的。agg返回的是一个新的以分组作为索引的dataframe或series,而transform返回的是一个与原dataframe等长的数组
它们的区别就简单带过一下,主要是多层索引
多层索引的好处就是可以很直观的看到分组的整体数据,不用看的那么密密麻麻。当然也可以自己创建多层级索引的dataframe。
创建多层级索引dataframe
pandas中有个MultiIndex函数提供了三种方法创建多层级索引,分别是:.from_tuples()、.from_arrays()和.from_product(),
按照很多人推荐的且我自己也用过,第三种.from_product()是最好用的
| |
| index = pd.MultiIndex.from_product([['A', 'B'], ['一月', '二月','三月']], names=['产品', '月份']) |
| values_2 = np.random.randint(1,50,size=6) |
| df_2 = pd.DataFrame(data=values_2,index=index,columns=['数量']) |
| df_2 |
|
|
数量 |
产品 |
月份 |
|
A |
一月 |
12 |
二月 |
28 |
三月 |
47 |
B |
一月 |
35 |
二月 |
6 |
三月 |
23 |
|
|
数量 |
产品 |
月份 |
|
A |
一月 |
21 |
三月 |
28 |
二月 |
139 |
B |
一月 |
51 |
三月 |
26 |
二月 |
23 |
可以看到不管是groupby加agg返回的series还是自己创建的多层索引dataframe,他们的索引都是一样的(但顺序不完全一样哈哈哈)。
说到这可能有些时候又需要把产品放到列索引中,那pandas也留了两个函数stack()和unstack(),可以对索引进行转换
索引转换
stack()函数可以把列索引压入到行索引当中,unstack()把行索引展开到列索引中。默认展开的顺序是从里往外,展开后也是在索引的最里层,最里面一层的序号是-1。
我这里想把产品这行索引转换成列索引所以使用unstack()函数。
DataFrame.unstack(level=-1, fill_value=None)
● level:要展开的级别编号或名称。默认为-1,即最内层的级别。
● fill_value:用于填充缺失值的值。默认为None,即不填充。
|
数量 |
产品 |
A |
B |
月份 |
|
|
一月 |
21 |
51 |
三月 |
28 |
26 |
二月 |
139 |
23 |
产品的行索引在最外层从-1往外数是-2,可以看到转换之后产品这行索引从最外层跑到了列索引的最里层。
能讲的就这么多了主要还是想分享一个在学习的过程中遇到的事情。
上面用到的一些函数不会的可以找找AI或菜鸟教程。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章