Pandas多级索引(MultiIndex)

高维数据的低维表示

学习主题: Pandas MultiIndex 多级索引全面解析

核心内容: MultiIndex创建、访问、排序、stack/unstack、set_index/reset_index、groupby组合分析、面板数据应用

适用场景: 时间序列面板数据、分组统计、多维数据透视、金融数据分析

关键词: Pandas, MultiIndex, 多级索引, xs, stack, unstack, 分层索引

一、MultiIndex 基本概念与用途

在数据分析工作中,我们经常需要处理多维数据——例如按年份和地区统计的销售数据、按产品和类别分组的库存数据、多个股票多个时间点的面板数据。传统的单层索引(Single Index)难以表达这类分层关系,而MultiIndex(多级索引/分层索引)正是Pandas为解决这一问题提供的核心工具。

MultiIndex允许在一个轴上拥有多个索引层级,从而以二维DataFrame的形式表达三维乃至更高维的数据。这种"高维数据的低维表示"是Pandas设计中最优雅的特性之一。

MultiIndex的核心优势:

  • 数据组织: 将多个分组维度组织在索引中,形成清晰的数据层级
  • 高效选择: 通过元组和xs方法实现跨层级的高效数据切片
  • 透视变换: stack/unstack实现DataFrame在宽表和长表之间的自由转换
  • 分组聚合: groupby结合MultiIndex自动生成分层聚合结果
  • 面板数据: 天然适合存储"实体-时间-指标"类型的面板数据结构

直观理解:

可以把MultiIndex理解为一个Excel表格中同时使用"合并单元格"表达的分组关系。例如按"年份-季度-月份"的分层结构,MultiIndex将这三个维度的层级关系明确编码到索引中,使得数据操作既直观又高效。

二、创建 MultiIndex 的多种方式

Pandas提供了丰富的API来创建MultiIndex,理解每种方式的适用场景可以让你在实际工作中灵活应对不同的数据来源。

2.1 使用 pd.MultiIndex.from_tuples

从元组列表创建是最直观的方式。每个元组对应一行数据的完整索引路径。

import pandas as pd import numpy as np # 定义多级索引的元组列表 tuples = [ ('2024年', 'Q1'), ('2024年', 'Q2'), ('2024年', 'Q3'), ('2024年', 'Q4'), ('2025年', 'Q1'), ('2025年', 'Q2'), ] # 创建MultiIndex index = pd.MultiIndex.from_tuples( tuples, names=['年份', '季度'] ) # 使用多级索引创建Series data = pd.Series( [120, 145, 138, 160, 155, 170], index=index, name='销售额' ) print(data)
年份 季度 2024年 Q1 120 Q2 145 Q3 138 Q4 160 2025年 Q1 155 Q2 170 Name: 销售额, dtype: int64

2.2 使用 pd.MultiIndex.from_arrays

当数据以多个独立数组(或多列DataFrame)的形式存在时,from_arrays是最自然的选择。每个数组对应一个层级的所有标签。

# 从多个数组创建 years = ['2024', '2024', '2024', '2025', '2025', '2025'] months = ['1月', '2月', '3月', '1月', '2月', '3月'] products = ['A', 'B', 'A', 'B', 'A', 'B'] arrays = [years, months, products] mi = pd.MultiIndex.from_arrays( arrays, names=['年份', '月份', '产品'] ) df = pd.DataFrame( np.random.randint(50, 100, size=(6, 2)), index=mi, columns=['销量', '库存'] ) print(df)
销量 库存 年份 月份 产品 2024 1月 A 82 67 2月 B 74 91 3月 A 59 73 2025 1月 B 88 64 2月 A 63 85 3月 B 95 78

2.3 使用 pd.MultiIndex.from_product

from_product从多个可迭代对象的笛卡尔积生成MultiIndex。它适合创建所有组合的完整交叉索引,在实验设计和面板数据准备中非常实用。

# 笛卡尔积创建MultiIndex —— 所有年份与所有季度的全组合 all_years = ['2023', '2024', '2025'] all_quarters = ['Q1', 'Q2', 'Q3', 'Q4'] all_regions = ['华北', '华东', '华南'] product_index = pd.MultiIndex.from_product( [all_years, all_quarters, all_regions], names=['年份', '季度', '区域'] ) print(f"共 {len(product_index)} 行数据") print(product_index[:6])
共 36 行数据 MultiIndex([('2023', 'Q1', '华北'), ('2023', 'Q1', '华东'), ('2023', 'Q1', '华南'), ('2023', 'Q2', '华北'), ('2023', 'Q2', '华东'), ('2023', 'Q2', '华南')], names=['年份', '季度', '区域'])

2.4 使用 MultiIndex.from_frame

如果数据已经在一个DataFrame中,可以直接用from_frame将指定列转换为MultiIndex。这个方法在数据清洗阶段尤其方便。

# 从DataFrame的列创建MultiIndex df_source = pd.DataFrame({ '城市': ['北京', '北京', '上海', '上海'], '产品': ['手机', '电脑', '手机', '电脑'], '销量': [1200, 800, 1500, 950], '利润': [240, 320, 300, 380] }) mi_from_df = pd.MultiIndex.from_frame( df_source[['城市', '产品']] ) df_multi = df_source.set_index(mi_from_df) print(df_multi)
销量 利润 城市 产品 北京 手机 1200 240 电脑 800 320 上海 手机 1500 300 电脑 950 380

三、多层索引的访问与选择

MultiIndex的数据访问比单层索引更灵活,同时也需要更小心。Pandas提供了多种方式来实现精确的数据定位。

3.1 使用 loc 进行元组索引

通过传递元组给loc,可以精确定位到指定层级的数据。元组中的元素顺序对应MultiIndex的层级顺序。

# 准备示例数据 tuples = [ ('电子产品', '手机'), ('电子产品', '电脑'), ('电子产品', '平板'), ('家电', '冰箱'), ('家电', '空调'), ('家电', '洗衣机'), ] idx = pd.MultiIndex.from_tuples(tuples, names=['品类', '商品']) sales_df = pd.DataFrame({ '销量': [500, 320, 180, 250, 400, 210], '单价': [3999, 5999, 2999, 2499, 3999, 1999], '毛利': [0.15, 0.20, 0.18, 0.25, 0.30, 0.22] }, index=idx) # 访问完整元组路径 print(sales_df.loc[('电子产品', '手机')]) print('---') # 访问某个一级索引下的所有数据 print(sales_df.loc['电子产品']) print('---') # 同时选择多个一级索引 print(sales_df.loc[('电子产品', '手机'):('家电', '空调')])
销量 500 单价 3999 毛利 0.15 Name: (电子产品, 手机), dtype: object --- 销量 单价 毛利 商品 手机 500 3999 0.15 电脑 320 5999 0.20 平板 180 2999 0.18 --- 销量 单价 毛利 品类 商品 电子产品 手机 500 3999 0.15 电脑 320 5999 0.20 平板 180 2999 0.18 家电 冰箱 250 2499 0.25 空调 400 3999 0.30

3.2 使用 xs 实现跨级选择

xs(cross-section)是MultiIndex中最强大的选择工具之一。它允许你在任意层级上选择数据,而不必遵循索引层级顺序。

# xs 跨级选择 —— 不依赖层级顺序 # 选择所有 '手机' 的数据(不论一级索引是什么) mobile_data = sales_df.xs('手机', level='商品') print("所有手机数据:") print(mobile_data) print('---') # 选择 '家电' 品类下的 '冰箱' fridge = sales_df.xs(('家电', '冰箱')) print("家电-冰箱:") print(fridge) print('---') # 使用 drop_level=False 保留指定层级 elec_keep = sales_df.xs('电子产品', drop_level=False) print("电子产品(保留层级):") print(elec_keep)
所有手机数据: 销量 单价 毛利 品类 电子产品 500 3999 0.15 --- 家电-冰箱: 销量 250 单价 2499 毛利 0.25 Name: (家电, 冰箱), dtype: object --- 电子产品(保留层级): 销量 单价 毛利 品类 商品 电子产品 手机 500 3999 0.15 电脑 320 5999 0.20 平板 180 2999 0.18

xs vs loc 的选择策略:

  • 需要逐级深入选择时使用 loc —— 从外层到内层依次指定
  • 需要跨层级筛选(如从第二级过滤)时使用 xs —— 通过 level 参数指定层级
  • 需要在选择后保留层级结构时,xs 的 drop_level=False 非常有用
  • 需要切片时,loc 的切片语法更自然

3.3 使用 get_level_values 获取层级值

get_level_values 用于提取某个层级的所有标签值,在数据分析和可视化标注时经常用到。

# 获取特定层级的所有值 categories = sales_df.index.get_level_values('品类') products = sales_df.index.get_level_values('商品') print("品类:", categories.tolist()) print("商品:", products.tolist()) # 实际应用:按品类分组计算 sales_df['品类标签'] = sales_df.index.get_level_values('品类') category_summary = sales_df.groupby('品类标签')['销量'].sum() print("\n品类总销量:") print(category_summary)
品类: ['电子产品', '电子产品', '电子产品', '家电', '家电', '家电'] 商品: ['手机', '电脑', '平板', '冰箱', '空调', '洗衣机'] 品类总销量: 品类标签 电子产品 1000 家电 860 Name: 销量, dtype: int64

四、多层索引的排序与重排

当数据量增大时,排序和层级调整成为日常操作。Pandas提供了sort_index(排序)、reorder_levels(层级重排)和swaplevel(层级交换)三个核心方法。

4.1 使用 sort_index 排序

多级索引的排序需要明确指定按哪个层级排序。排序后的数据在切片操作时性能更好。

# 创建一个乱序的MultiIndex DataFrame mi_unsorted = pd.MultiIndex.from_tuples([ ('2025', 'Q3'), ('2024', 'Q1'), ('2025', 'Q1'), ('2024', 'Q2'), ('2025', 'Q2'), ('2024', 'Q3'), ], names=['年份', '季度']) df_unsorted = pd.DataFrame( np.random.randint(100, 200, size=(6, 2)), index=mi_unsorted, columns=['收入', '支出'] ) print("排序前:") print(df_unsorted) print('\n排序后 (按年份和季度):') print(df_unsorted.sort_index()) print('\n仅按季度排序:') print(df_unsorted.sort_index(level='季度'))
排序前: 收入 支出 年份 季度 2025 Q3 156 134 2024 Q1 145 167 2025 Q1 123 155 2024 Q2 189 143 2025 Q2 178 122 2024 Q3 134 156 排序后 (按年份和季度): 收入 支出 年份 季度 2024 Q1 145 167 Q2 189 143 Q3 134 156 2025 Q1 123 155 Q2 178 122 Q3 156 134 仅按季度排序: 收入 支出 年份 季度 2024 Q1 145 167 2025 Q1 123 155 2024 Q2 189 143 2025 Q2 178 122 2024 Q3 134 156 2025 Q3 156 134

4.2 使用 reorder_levels 重排层级

reorder_levels 改变层级的先后顺序,影响loc切片时的外层选择次序。

# 重排索引层级顺序 reordered = df_unsorted.sort_index().reorder_levels(['季度', '年份']) print("重排后 (季度在外层):") print(reordered) print('\n现在可以按季度切片:') print(reordered.loc['Q1'])
重排后 (季度在外层): 收入 支出 季度 年份 Q1 2024 145 167 2025 123 155 Q2 2024 189 143 2025 178 122 Q3 2024 134 156 2025 156 134 现在可以按季度切片: 收入 支出 年份 2024 145 167 2025 123 155

4.3 使用 swaplevel 交换层级

swaplevel 是 reorder_levels 的轻量版本,专门用于交换两个层级的位置。

# 交换两个层级 swapped = df_unsorted.sort_index().swaplevel('年份', '季度') print("交换层级后:") print(swapped) print('\n验证索引名称是否交换:') print(swapped.index.names)
交换层级后: 收入 支出 季度 年份 Q1 2024 145 167 Q2 2024 189 143 Q3 2024 134 156 Q1 2025 123 155 Q2 2025 178 122 Q3 2025 156 134 验证索引名称是否交换: ['季度', '年份']

五、stack/unstack 在 MultiIndex 中的应用

stack 和 unstack 是Pandas中最具"魔法"特性的两个方法。它们在宽表(多列指标)和长表(多行堆叠)之间自由转换,本质上是将列索引和行索引进行相互转换。

5.1 unstack —— 行索引转到列索引

unstack 将行索引中最内层(或指定层级)的索引转换为列索引,从"长表"变为"宽表"。

# 准备数据:各区域各季度销售数据 idx = pd.MultiIndex.from_tuples([ ('华北', 'Q1'), ('华北', 'Q2'), ('华东', 'Q1'), ('华东', 'Q2'), ('华南', 'Q1'), ('华南', 'Q2'), ], names=['区域', '季度']) df_stack = pd.DataFrame({ '销售额': [120, 145, 200, 180, 95, 110], '利润': [24, 29, 40, 36, 19, 22] }, index=idx) print("原始数据(长表):") print(df_stack) print('\nunstack 后(宽表):') print(df_stack.unstack()) print('\n指定 unstack 层级为 区域:') print(df_stack.unstack(level='区域'))
原始数据(长表): 销售额 利润 区域 季度 华北 Q1 120 24 Q2 145 29 华东 Q1 200 40 Q2 180 36 华南 Q1 95 19 Q2 110 22 unstack 后(宽表): 销售额 利润 季度 Q1 Q2 Q1 Q2 区域 华北 120 145 24 29 华东 200 180 40 36 华南 95 110 19 22 指定 unstack 层级为 区域: 销售额 利润 区域 华北 华东 华南 华北 华东 华南 季度 Q1 120 200 95 24 40 19 Q2 145 180 110 29 36 22

5.2 stack —— 列索引转到行索引

stack 是 unstack 的逆操作,将列索引中指定层级转换为行索引。

# stack 操作 —— 将宽表还原为长表 df_wide = df_stack.unstack() df_stacked = df_wide.stack() print("stack 还原后:") print(df_stacked) print('\nstack(future_stack=True) 推荐方式:') df_stacked_v2 = df_wide.stack(future_stack=True) print(df_stacked_v2)
stack 还原后: 销售额 利润 区域 季度 华北 Q1 120 24 Q2 145 29 华东 Q1 200 40 Q2 180 36 华南 Q1 95 19 Q2 110 22 stack(future_stack=True) 推荐方式: 销售额 利润 区域 季度 华北 Q1 120 24 Q2 145 29 华东 Q1 200 40 Q2 180 36 华南 Q1 95 19 Q2 110 22

stack/unstack 的典型应用场景:

  • 数据透视: 将分组聚合后的长表结果转换为易于阅读的交叉表
  • 格式转换: 从数据库导出的"长格式"数据转换为适合可视化的"宽格式"
  • 特征工程: 将分类变量的不同取值展开为不同的列(One-Hot 的替代方案)
  • 多层列索引: unstack 后会生成多层列索引,这是MultiIndex在列上的应用

5.3 MultiIndex 列的高级操作

当unstack生成多层列索引后,DataFrame的列也变成了MultiIndex,我们可以用同样的方法操作列。

# 操作多层列索引 df_unstacked = df_stack.unstack() print("列索引:", df_unstacked.columns) print('\n选择所有区域的销售额:') print(df_unstacked['销售额']) print('\n选择华北的所有指标:') print(df_unstacked.xs('华北', axis=1, level='区域'))
列索引: MultiIndex([('销售额', 'Q1'), ('销售额', 'Q2'), ( '利润', 'Q1'), ( '利润', 'Q2')], names=[None, '季度']) 选择所有区域的销售额: 季度 Q1 Q2 区域 华北 120 145 华东 200 180 华南 95 110 选择华北的所有指标: 季度 Q1 Q2 销售额 120 145 利润 24 29

六、set_index / reset_index 操作

set_index 和 reset_index 是DataFrane中与索引相关的两个最常用的方法,在与MultiIndex配合时展现出强大的数据重塑能力。

6.1 使用 set_index 创建多级索引

将DataFrame的多个列同时设置为索引,一步创建MultiIndex。

# 从DataFrame列创建多级索引 df_flat = pd.DataFrame({ '年份': ['2024', '2024', '2024', '2025', '2025'], '季度': ['Q1', 'Q2', 'Q3', 'Q1', 'Q2'], '产品': ['A', 'B', 'A', 'B', 'A'], '收入': [100, 150, 120, 180, 160], '成本': [60, 90, 70, 100, 95] }) df_mi = df_flat.set_index(['年份', '季度', '产品']) print(df_mi) print('\n索引层级:', df_mi.index.names)
收入 成本 年份 季度 产品 2024 Q1 A 100 60 Q2 B 150 90 Q3 A 120 70 2025 Q1 B 180 100 Q2 A 160 95 索引层级: ['年份', '季度', '产品']

6.2 使用 reset_index 重置多级索引

reset_index 将索引层级恢复为普通列,可以针对特定层级或全部层级进行操作。

# 重置所有索引 df_reset_all = df_mi.reset_index() print("重置所有索引:") print(df_reset_all) print('\n---') # 仅重置最内层(产品) df_reset_inner = df_mi.reset_index(level='产品') print("仅重置最内层 (产品):") print(df_reset_inner) print('\n---') # 重置并丢弃索引(不保留为列) df_reset_drop = df_mi.reset_index(drop=True) print("重置并丢弃:") print(df_reset_drop)
重置所有索引: 年份 季度 产品 收入 成本 0 2024 Q1 A 100 60 1 2024 Q2 B 150 90 2 2024 Q3 A 120 70 3 2025 Q1 B 180 100 4 2025 Q2 A 160 95 仅重置最内层 (产品): 收入 成本 产品 年份 季度 2024 Q1 100 60 A Q2 150 90 B Q3 120 70 A 2025 Q1 180 100 B Q2 160 95 A 重置并丢弃: 收入 成本 0 100 60 1 150 90 2 120 70 3 180 100 4 160 95

七、groupby 与 MultiIndex 的组合分析

groupby 分组聚合后,默认会产生一个MultiIndex作为结果的行索引,这正是MultiIndex最自然的应用场景之一。

7.1 多列分组自动生成 MultiIndex

# 创建销售数据 np.random.seed(42) n = 100 df_sales = pd.DataFrame({ '年份': np.random.choice(['2023', '2024', '2025'], n), '季度': np.random.choice(['Q1', 'Q2', 'Q3', 'Q4'], n), '区域': np.random.choice(['华北', '华东', '华南', '西南'], n), '销售额': np.random.randint(10, 100, n), '利润': np.random.randint(1, 30, n) }) # 多列分组 —— 结果自动使用MultiIndex grouped = df_sales.groupby(['年份', '季度', '区域'])['销售额'].agg(['sum', 'mean', 'count']) print("分组聚合结果 (MultiIndex):") print(grouped.head(12)) print('\n索引类型:', type(grouped.index)) print('索引名称:', grouped.index.names)
分组聚合结果 (MultiIndex): sum mean count 年份 季度 区域 2023 Q1 华北 102 34.0 3 华东 100 50.0 2 华南 93 46.5 2 西南 99 33.0 3 Q2 华北 34 34.0 1 华东 27 27.0 1 华南 138 46.0 3 西南 46 46.0 1 Q3 华北 151 37.8 4 华东 63 31.5 2 华南 68 34.0 2 西南 42 42.0 1 索引类型: 索引名称: ['年份', '季度', '区域']

7.2 使用 unstack 美化聚合结果

将分组结果中的内层索引展开为列,生成更易读的交叉表。

# 将聚合结果unstack为更美观的宽表 print("按年份和区域透视:") pivot = df_sales.groupby(['年份', '区域'])['销售额'].sum().unstack() print(pivot) print('\n按年份和季度透视:') pivot_q = df_sales.groupby(['年份', '季度'])['销售额'].mean().unstack() print(pivot_q)
按年份和区域透视: 区域 华北 华东 华南 西南 年份 2023 359 237 348 228 2024 388 461 380 315 2025 305 408 427 332 按年份和季度透视: 季度 Q1 Q2 Q3 Q4 年份 2023 394.0 245.0 324.0 209.0 2024 300.0 440.0 392.0 412.0 2025 328.0 372.0 350.0 472.0

7.3 多个聚合函数与MultiIndex列

当groupby同时使用多个聚合函数且对多列聚合时,结果会同时拥有MultiIndex行和MultiIndex列。

# 多列多函数聚合 —— 行和列都是MultiIndex result = df_sales.groupby(['年份', '区域'])[['销售额', '利润']].agg(['sum', 'mean', 'std']) print("多列多函数聚合:") print(result.head(8)) print('\n列索引:', result.columns) print('行索引:', result.index)
多列多函数聚合: 销售额 利润 sum mean std sum mean std 年份 区域 2023 华北 359 51.3 27.913 86 12.3 7.929 华东 237 47.4 28.190 53 10.6 5.770 华南 348 49.7 28.790 77 11.0 7.549 西南 228 38.0 19.078 53 8.8 6.653 2024 华北 388 48.5 26.510 93 11.6 6.957 华东 461 51.2 20.272 91 10.1 4.987 华南 380 47.5 26.438 92 11.5 8.367 西南 315 39.4 25.541 75 9.4 6.676

八、MultiIndex 在面板数据中的应用

面板数据(Panel Data)是计量经济学和金融分析中最常见的数据结构之一,它包含多个实体在多个时间点的观测值。Pandas的MultiIndex天然适合表达这种"实体-时间"结构。

8.1 构建股票面板数据

# 构建多只股票的时间序列面板数据 dates = pd.date_range('2025-01-01', periods=5, freq='B') stocks = ['AAPL', 'GOOGL', 'MSFT'] # 使用 from_product 创建实体 × 时间的完整索引 panel_index = pd.MultiIndex.from_product( [stocks, dates], names=['股票', '日期'] ) np.random.seed(42) panel_data = pd.DataFrame({ '开盘': np.random.uniform(150, 200, 15).round(2), '收盘': np.random.uniform(150, 200, 15).round(2), '成交量': np.random.randint(1000000, 5000000, 15), '涨跌幅': np.random.uniform(-3, 3, 15).round(2) }, index=panel_index) print("面板数据 (前10行):") print(panel_data.head(10))
面板数据 (前10行): 开盘 收盘 成交量 涨跌幅 股票 日期 AAPL 2025-01-01 173.62 162.16 3250247 2.67 2025-01-02 175.31 185.77 3126754 -2.63 2025-01-03 153.40 159.65 2076257 1.28 2025-01-06 158.27 180.34 4556637 2.65 2025-01-07 152.43 162.24 2300612 -1.11 GOOGL 2025-01-01 181.30 161.29 3281550 2.74 2025-01-02 156.21 199.57 2402772 -1.32 2025-01-03 188.98 156.48 1098129 2.22 2025-01-06 163.43 166.49 1999659 1.38 2025-01-07 152.11 163.10 4995293 -2.11

8.2 面板数据的典型操作

# 按股票分组计算统计量 stock_stats = panel_data.groupby(level='股票').agg({ '开盘': ['mean', 'std'], '涨跌幅': ['mean', 'min', 'max'] }) print("各股票统计:") print(stock_stats) print('\n---') # 将面板数据转为"股票为列、日期为行"的宽表 panel_wide = panel_data['收盘'].unstack(level='股票') print("收盘价宽表:") print(panel_wide) print('\n---') # 计算各股的日收益率 returns = panel_wide.pct_change().round(4) * 100 print("日收益率 (%):") print(returns)
各股票统计: 开盘 涨跌幅 mean std mean min max 股票 AAPL 162.606 10.838 0.572 -2.63 2.67 GOOGL 168.406 16.308 0.542 -2.11 2.74 MSFT 175.230 14.734 1.260 -1.72 2.50 收盘价宽表: 股票 AAPL GOOGL MSFT 日期 2025-01-01 162.16 161.29 175.38 2025-01-02 185.77 199.57 181.94 2025-01-03 159.65 156.48 170.00 2025-01-06 180.34 166.49 177.68 2025-01-07 162.24 163.10 153.97 日收益率 (%): 股票 AAPL GOOGL MSFT 日期 2025-01-01 NaN NaN NaN 2025-01-02 14.56 23.72 3.74 2025-01-03 -14.06 -21.58 -6.56 2025-01-06 12.96 6.40 4.52 2025-01-07 -10.04 -2.04 -13.34

面板数据操作技巧:

  • 使用 groupby(level=0) 按最外层实体分组,groupby(level=-1) 按最内层分组
  • 使用 unstack(level='实体') 可以将面板转为"时间×实体"的宽表
  • 使用 pct_change() 结合unstack可以轻松计算各实体的收益率/增长率
  • 使用 shift() 可以创建滞后变量,用于时间序列回归分析
  • MultiIndex面板数据可以直接输入到statsmodels的面板数据模型中

九、核心要点总结

十、进一步思考与实践

进阶探索方向:

  • MultiIndex vs pivot_table: 理解pivot_table的底层实现就是MultiIndex + unstack,掌握底层原理后可以更灵活地定制透视结果
  • 交叉表(crosstab): pd.crosstab 的返回值本质就是MultiIndex DataFrame,学会用索引操作来定制crosstab的输出
  • 时间序列+MultiIndex: 对每个实体分别进行时间序列操作(如滚动窗口、差分、滞后),可以先用groupby再apply自定义函数
  • 数据入库: 使用 df.to_sql 时,reset_index 将MultiIndex转为普通列后再存入数据库是标准做法
  • 与xarray的对比: 对于超过3维的数据,考虑使用xarray库,它专为多维数组设计

实战练习建议:

  1. 股票数据分析: 从Yahoo Finance下载多只股票历史数据,构建"股票×日期"的面板,计算每只股票的滚动20日波动率
  2. 销售数据透视: 对销售数据按"年份-季度-区域-产品"四层分组,用unstack生成多维透视表,再用xs提取特定产品的跨区域对比
  3. 实验结果分析: 在A/B测试中,构建"实验组-用户-时间"的三层索引,使用groupby+agg计算各组在各时间点的均值与置信区间
  4. 多指标报表: 用MultiIndex列来组织报表的指标层级,如"利润表"中"收入/成本/费用"大类下再分子类,生成专业的财务报表

MultiIndex的精髓在于:它让你用二维表格的思维处理多维问题,把复杂的分组关系编码到索引中,从而实现简洁而高效的代码表达。掌握MultiIndex,是Pandas从入门到精通的关键一步。