Python案例:如何用Pandas做数据分组与重索引——从入门到实战优化
目录导读
- 什么是数据分组与重索引?为什么重要?
- Pandas分组操作核心:groupby详解
- 重索引(reset_index / set_index)实战技巧
- 综合案例:电商销售数据的分组统计与索引重构
- 常见错误与性能优化建议
- 问答环节:解决你关于分组重索引的困惑
什么是数据分组与重索引?为什么重要?
在数据分析中,数据分组(GroupBy)是指根据某个或某几个字段将数据拆分成多个组,然后对每个组独立执行聚合、变换或过滤操作。重索引(Reindexing)则是调整DataFrame的行列索引结构,使数据更易于理解或满足后续分析需求。

为什么要将两者结合?举一个典型场景:你有一份销售数据,需要按“月份”和“商品类别”统计销售额,最终输出一个以“月份”为行、以“商品类别”为列的表格,这本质上就是分组聚合 + 重索引(pivot/unstack) 的组合操作。
Pandas作为Python最核心的数据处理库,其groupby、reset_index、set_index、pivot_table等函数构成了分组重索引的完整生态,掌握这些技巧,能让你的数据处理效率提升数倍。
Pandas分组操作核心:groupby详解
1 基础语法与分组模式
df.groupby(by=grouping_columns)[columns_to_aggregate].agg(aggregation_function)
by:指定分组依据,可以是单个列名、多个列名列表,或自定义函数。- 分组后常用聚合函数:
sum()、mean()、count()、max()、min()、std()等。 agg()支持同时指定多个聚合方式,如{'销售额':['sum','mean']}。
2 案例:按城市和产品分组统计
import pandas as pd
data = {
'城市': ['北京','上海','北京','上海','广州'],
'产品': ['A','A','B','B','A'],
'销售额': [100,200,150,250,180]
}
df = pd.DataFrame(data)
# 单层分组
grouped = df.groupby('城市')['销售额'].sum()
print(grouped)
# 输出:
# 城市
# 上海 450
# 北京 250
# 广州 180
# 多层分组
grouped_multi = df.groupby(['城市','产品'])['销售额'].sum()
print(grouped_multi)
# 城市 产品
# 上海 A 200
# B 250
# 北京 A 100
# B 150
# 广州 A 180
注意:groupby默认会将分组键作为索引(Index),这是后续重索引操作的基础。
重索引(reset_index / set_index)实战技巧
1 reset_index:将索引还原为普通列
当groupby生成的分组结果具有分层索引时(例如按城市+产品分组),使用reset_index()可以将其转换回普通DataFrame:
result = grouped_multi.reset_index() print(result) # 城市 产品 销售额 # 0 上海 A 200 # 1 上海 B 250 # 2 北京 A 100 # 3 北京 B 150 # 4 广州 A 180
2 set_index:将普通列提升为索引
如果你希望将某列设置为索引(例如将“月份”设为行索引),使用set_index():
df_temp = pd.DataFrame({'月份':['1月','2月','3月'], '销量':[300,450,500]})
df_indexed = df_temp.set_index('月份')
print(df_indexed)
# 销量
# 月份
# 1月 300
# 2月 450
# 3月 500
3 高级技巧:unstack() 实现行转列
unstack()是重索引的进阶用法,它可以将多层索引中的内层索引转换为列标签(类似Excel透视表):
grouped_multi.unstack() # 输出: # 产品 A B # 城市 # 上海 200.0 250.0 # 北京 100.0 150.0 # 广州 180.0 NaN
“产品”从行索引变为了列字段,缺失值默认填充NaN,可配合fillna()补零。
4 pivot_table:一步到位的分组+重索引
pivot_table是groupby + unstack的快捷方式,语法更直观:
pd.pivot_table(df, values='销售额', index='城市', columns='产品', aggfunc='sum', fill_value=0) # 输出: # 产品 A B # 城市 # 上海 200 250 # 北京 100 150 # 广州 180 0
综合案例:电商销售数据的分组统计与索引重构
1 数据准备
假设有一份电商订单数据,包含字段:order_date(订单日期)、category(商品类别)、region(区域)、amount(金额)。
import pandas as pd
import numpy as np
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=100, freq='D')
data = {
'order_date': np.random.choice(dates, 500),
'category': np.random.choice(['电子产品','服装','食品','书籍'], 500),
'region': np.random.choice(['华北','华东','华南','西部'], 500),
'amount': np.random.randint(50, 500, 500)
}
df = pd.DataFrame(data)
2 需求1:按月和类别统计总销售额,并以月份为行
# 第一步:提取月份 df['month'] = df['order_date'].dt.month # 分组聚合 monthly_sales = df.groupby(['month','category'])['amount'].sum().reset_index() print(monthly_sales.head()) # month category amount # 0 1 书籍 20556 # 1 1 服装 19704 # 2 1 电子产品 20978 # 3 1 食品 23522 # 4 2 书籍 18111
3 需求2:重构索引,变成“月份”为行,“类别”为列
pivot_sales = monthly_sales.pivot_table(
index='month',
columns='category',
values='amount',
fill_value=0
).reset_index() # 如需将月份也变成普通列
print(pivot_sales)
# category month 书籍 服装 电子产品 食品
# 0 1 20556 19704 20978 23522
# 1 2 18111 20472 22777 21578
# ...(共12个月)
此时索引变成了数字索引(0,1,2…),如果希望“month”作为索引:去掉.reset_index()即可。
4 需求3:按区域和类别分组,计算出平均消费金额,并重索引为矩阵
region_category_mean = df.groupby(['region','category'])['amount'].mean().reset_index()
pivot_region = region_category_mean.pivot_table(
index='region',
columns='category',
values='amount',
fill_value=0
)
print(pivot_region)
# category 书籍 服装 电子产品 食品
# region
# 华北 282.314286 280.956522 277.500000 301.534884
# 华东 293.641026 298.426571 271.105263 267.166667
# 华南 280.276596 296.445783 291.113636 286.582278
# 西部 263.425532 296.638298 282.500000 271.700000
常见错误与性能优化建议
1 常见错误
- 忘记reset_index导致索引混乱:
groupby后直接参与计算,索引可能变为复合索引,后续操作容易出错,建议养成习惯:需要普通列时立即reset_index()。 - pivot_table中缺失值未处理:默认填充NaN,若需补零使用
fill_value=0。 - 数据类型错误:例如日期格式不是datetime类型,导致
dt.month出现异常,用pd.to_datetime()预处理。
2 性能优化
- 对于大数据集(百万行以上),用
groupby的agg比apply快得多。 - 如果分组键是字符串,可先转化为Categorical数据类型以减少内存。
- 避免在
groupby后使用lambda操作,尽量用内置聚合函数。
问答环节:解决你关于分组重索引的困惑
Q1:groupby和pivot_table有什么区别?什么时候用哪个?
A:groupby更灵活,支持复杂的分组后运算(如过滤、自定义变换),但输出通常是长格式。pivot_table专为透视表设计,一步完成分组+行列转换,更直观,我的建议:如果最终需要矩阵形式(行列交叉),用pivot_table;如果需要中间步骤的链式操作,用groupby。
Q2:多层索引如何重设为两层索引?
A:使用reset_index(level=...)或droplevel(),例如df.reset_index(level=0)只会将最外层索引转成列。
Q3:重索引过程中数据丢失怎么办?
A:检查groupby的聚合函数是否选择了正确的列,以及pivot_table的values参数是否匹配,丢失常见于索引不完整(如某些组合缺失数据),用fill_value补0或dropna=False保留空行。
Q4:有什么方法可以将重索引结果保存到Excel?
A:直接使用df.to_excel('output.xlsx'),注意如果索引是复合索引,Excel中会以多列呈现,看起来不美观,建议在输出前先reset_index(),让所有数据变成普通列。
Pandas的分组与重索引是数据分析的“左膀右臂”,通过本文的案例,你应该已经掌握了从groupby聚合到pivot_table透视的完整路径,实际工作中,建议多练习“分组—聚合—重索引—透视”这个经典四步法,当你面对一张凌乱的原始表时,先思考“需要按什么字段分组”,再思考“最终想要什么样的行列结构”,用本文提到的工具就能快速实现。
行动建议:复制案例中的代码,替换成你自己的数据集(如淘宝订单、学生成绩),反复演练三次,你会发现,数据分析从“无从下手”变成了“像搭积木一样简单”。
如果遇到具体的分组重索引难题,欢迎在评论区留言,我会在第一时间回复。