11. 多层索引(MultiIndex)
1. 概述
多层索引(MultiIndex)是 Pandas 中处理层次化数据的重要特性。它允许一个 DataFrame 拥有多个层级的行索引或列索引,适用于分组数据、时间序列、面板数据等复杂场景。
importpandasaspdimportnumpyasnp# 创建示例数据:多层级数据data={'年份':[2023,2023,2023,2023,2024,2024,2024,2024],'季度':['Q1','Q2','Q3','Q4','Q1','Q2','Q3','Q4'],'销售额':[100,120,110,130,150,160,140,170],'利润':[20,25,22,28,35,38,32,40]}df=pd.DataFrame(data)print("原始数据:")print(df)2. 创建多层索引
2.1 从 DataFrame 创建
# 使用 set_index 创建多层索引multi_df=df.set_index(['年份','季度'])print("多层索引 DataFrame:")print(multi_df)print(f"\n索引结构:{multi_df.index}")print(f"索引层级:{multi_df.index.names}")2.2 从数组创建
# 使用 pd.MultiIndex 创建arrays=[['A','A','B','B'],[1,2,1,2]]index=pd.MultiIndex.from_arrays(arrays,names=['字母','数字'])df_multi=pd.DataFrame({'值':[10,20,30,40]},index=index)print("从数组创建:")print(df_multi)2.3 从元组列表创建
# 使用 from_tuplestuples=[('A',1),('A',2),('B',1),('B',2)]index=pd.MultiIndex.from_tuples(tuples,names=['字母','数字'])df_multi=pd.DataFrame({'值':[10,20,30,40]},index=index)print("从元组创建:")print(df_multi)2.4 从笛卡尔积创建
# 使用 from_productletters=['A','B']numbers=[1,2,3]index=pd.MultiIndex.from_product([letters,numbers],names=['字母','数字'])df_multi=pd.DataFrame({'值':range(6)},index=index)print("从笛卡尔积创建:")print(df_multi)3. 多层索引的访问
3.1 使用 loc 访问
multi_df=df.set_index(['年份','季度'])print("原始数据:")print(multi_df)# 访问第一层print("\n访问2023年数据:")print(multi_df.loc[2023])# 访问完整层级print("\n访问2023年Q1:")print(multi_df.loc[(2023,'Q1')])# 访问多个print("\n访问2023年Q1和Q2:")print(multi_df.loc[(2023,['Q1','Q2'])])3.2 使用 xs 方法交叉选择
# xs 方法用于交叉选择multi_df=df.set_index(['年份','季度'])# 选择所有年份的 Q1print("所有年份的 Q1:")print(multi_df.xs('Q1',level='季度'))# 选择2023年的所有季度print("\n2023年所有季度:")print(multi_df.xs(2023,level='年份'))# 保持层级print("\n保持层级结构:")print(multi_df.xs('Q1',level='季度',drop_level=False))4. 多层索引的切片
multi_df=df.set_index(['年份','季度'])# 按第一层切片print("2023-2024年数据:")print(multi_df.loc[2023:2024])# 按完整层级切片print("\n2023年Q1到2024年Q2:")print(multi_df.loc[(2023,'Q1'):(2024,'Q2')])# 使用 pd.IndexSliceidx=pd.IndexSliceprint("\n使用 IndexSlice:")print(multi_df.loc[idx[2023,'Q1':'Q3'],:])5. 多层索引的层级操作
5.1 交换层级
multi_df=df.set_index(['年份','季度'])print("原始:")print(multi_df)# 交换层级swapped=multi_df.swaplevel()print("\n交换层级后:")print(swapped)# 指定交换的层级swapped2=multi_df.swaplevel(0,1)print("\n指定交换层级:")print(swapped2)5.2 排序层级
# 创建乱序的多层索引arrays=[['B','A','B','A'],[2,1,1,2]]index=pd.MultiIndex.from_arrays(arrays,names=['字母','数字'])df_unsorted=pd.DataFrame({'值':[10,20,30,40]},index=index)print("乱序索引:")print(df_unsorted)# 排序索引sorted_df=df_unsorted.sort_index()print("\n排序后:")print(sorted_df)5.3 重设层级
multi_df=df.set_index(['年份','季度'])# 重置所有层级reset_all=multi_df.reset_index()print("重置所有层级:")print(reset_all)# 重置指定层级reset_level=multi_df.reset_index(level='季度')print("\n重置季度层级:")print(reset_level)5.4 删除层级
multi_df=df.set_index(['年份','季度'])# 删除层级(将索引转为普通列后删除)df_no_quarter=multi_df.reset_index(level='季度',drop=True)print("删除季度层级:")print(df_no_quarter)6. 多层索引的聚合
6.1 按层级分组
multi_df=df.set_index(['年份','季度'])# 按年份分组求和print("按年份求和:")print(multi_df.groupby(level='年份').sum())# 按季度分组(跨年份)print("\n按季度求和:")print(multi_df.groupby(level='季度').sum())# 按多个层级分组print("\n按年份和季度分组:")print(multi_df.groupby(level=['年份','季度']).sum())6.2 层级聚合函数
# 创建更多数据np.random.seed(42)index=pd.MultiIndex.from_product([['A','B','C'],['X','Y'],[1,2]],names=['组','子组','编号'])df_hier=pd.DataFrame({'数值1':np.random.randn(12),'数值2':np.random.randn(12)},index=index)print("层次化数据:")print(df_hier)# 按第一层聚合print("\n按组聚合:")print(df_hier.groupby(level='组').mean())# 按前两层聚合print("\n按组和子组聚合:")print(df_hier.groupby(level=['组','子组']).std())7. 多层索引的堆叠与展开
7.1 stack() - 将列索引转为行索引
# 创建宽格式数据wide_df=pd.DataFrame({('2023','Q1'):[100,200],('2023','Q2'):[120,220],('2024','Q1'):[150,250],('2024','Q2'):[160,260]},index=['产品A','产品B'])wide_df.columns=pd.MultiIndex.from_tuples(wide_df.columns)print("宽格式数据:")print(wide_df)# stack() 将列转为行stacked=wide_df.stack()print("\nstack 后:")print(stacked)7.2 unstack() - 将行索引转为列索引
multi_df=df.set_index(['年份','季度'])print("原始多层索引:")print(multi_df)# unstack 将季度转为列unstacked=multi_df.unstack(level='季度')print("\nunstack 后:")print(unstacked)# 指定层级unstacked2=multi_df.unstack(level=0)print("\n将年份转为列:")print(unstacked2)8. 多层列索引
# 创建多层列索引columns=pd.MultiIndex.from_tuples([('销售额','2023'),('销售额','2024'),('利润','2023'),('利润','2024')])df_cols=pd.DataFrame([[100,150,20,35],[120,160,25,38]],columns=columns,index=['产品A','产品B'])print("多层列索引:")print(df_cols)# 访问列print("\n销售额:")print(df_cols['销售额'])# 访问特定年份print("\n2023年销售额:")print(df_cols[('销售额','2023')])9. 完整示例:销售数据多维分析
# 创建销售数据np.random.seed(42)sales_data=[]foryearin[2023,2024]:forquarterin['Q1','Q2','Q3','Q4']:forproductin['产品A','产品B','产品C']:forregionin['华东','华南','华北']:sales_data.append({'年份':year,'季度':quarter,'产品':product,'地区':region,'销售额':np.random.randint(1000,5000),'销量':np.random.randint(50,200)})df_sales=pd.DataFrame(sales_data)print("="*60)print("销售数据多维分析")print("="*60)# 1. 创建多层索引sales_indexed=df_sales.set_index(['年份','季度','产品','地区'])print("\n1. 多层索引结构:")print(sales_indexed.head())print(f"索引层级:{sales_indexed.index.names}")# 2. 按年份汇总print("\n2. 按年份销售额汇总:")print(sales_indexed.groupby(level='年份')['销售额'].sum())# 3. 按产品和地区汇总print("\n3. 按产品和地区销售额:")product_region=sales_indexed.groupby(level=['产品','地区'])['销售额'].sum()print(product_region.unstack())# 4. 2024年各季度销售趋势print("\n4. 2024年各季度销售趋势:")q4_trend=sales_indexed.xs(2024,level='年份').groupby(level='季度')['销售额'].sum()print(q4_trend)# 5. 产品A在各地区的表现print("\n5. 产品A在各地区的销售:")product_a=sales_indexed.xs('产品A',level='产品')print(product_a.groupby(level='地区')['销售额'].sum())# 6. 透视分析print("\n6. 产品 × 地区 销售额透视:")pivot=sales_indexed.groupby(level=['产品','地区'])['销售额'].sum().unstack()print(pivot)10. 总结
| 操作 | 方法 | 说明 |
|---|---|---|
| 创建多层索引 | df.set_index(['col1','col2']) | 从列创建 |
| 从数组创建 | pd.MultiIndex.from_arrays() | 从数组列表创建 |
| 从元组创建 | pd.MultiIndex.from_tuples() | 从元组列表创建 |
| 从笛卡尔积创建 | pd.MultiIndex.from_product() | 创建所有组合 |
| 访问数据 | df.loc[(level1, level2)] | 按层级访问 |
| 交叉选择 | df.xs('value', level='name') | 按层级值选择 |
| 交换层级 | df.swaplevel() | 交换索引层级 |
| 重置层级 | df.reset_index(level='name') | 将索引转为列 |
| 删除层级 | df.reset_index(level='name', drop=True) | 丢弃索引 |
| 堆叠 | df.stack() | 列→行 |
| 展开 | df.unstack() | 行→列 |
| 分组聚合 | df.groupby(level='name') | 按层级分组 |
11. 模块二总结
恭喜你完成了模块二:数据选择与索引!
你已掌握:
- ✅ 列选择与操作
- ✅ 行选择与切片
- ✅ 条件筛选
- ✅ query 方法
- ✅ 索引设置与重置
- ✅ 多层索引