本文目录导读:

Python案例实战:如何用Pandas做数据分组自定义(附完整代码)
📚 目录导读
为什么需要数据分组自定义?
在实际的数据分析工作中,我们经常遇到这样的场景:
- 按月份统计销售额,但需要同时对“高价值客户”(消费>1000)和“普通客户”分别计算均值。
- 按部门分组,但要求对每个组的“异常值”进行剔除后再计算中位数。
内置聚合函数(如sum、mean、count)只能解决通用需求,当业务逻辑复杂、需要自定义计算规则时,就要用到 Pandas分组自定义 能力,这不仅能提升代码复用性,还能让分析结果更贴合业务场景。
Pandas分组基础回顾
在进入自定义之前,先回顾最常用的分组语法:
import pandas as pd
# 示例数据
df = pd.DataFrame({
'部门': ['A','A','B','B','C'],
'销售额': [100, 200, 150, 300, 250],
'客户数': [10, 15, 8, 12, 9]
})
# 基础分组聚合
grouped = df.groupby('部门')['销售额'].sum()
print(grouped)
输出:
部门
A 300
B 450
C 250
Name: 销售额, dtype: int64
但如果我们想计算“销售额/客户数”这个自定义指标,就不能直接用内置函数,需要自定义。
自定义聚合函数的两种方式
使用 agg() 传入自定义函数
def avg_sales_per_customer(group):
"""每个组的销售额/客户数"""
return group['销售额'].sum() / group['客户数'].sum()
result = df.groupby('部门').apply(avg_sales_per_customer)
print(result)
使用 transform() 保留原行结构
def z_score(x):
"""计算组的z-score,剔除异常值后返回标准化值"""
return (x - x.mean()) / x.std()
df['销售额_zscore'] = df.groupby('部门')['销售额'].transform(z_score)
✅ 小贴士:
apply返回一个聚合后的DataFrame/Seriestransform返回与原DataFrame相同形状的结果
实战案例:销售数据多维度分组分析
假设我们有一份电商销售数据(模拟1000行),需要完成以下任务:
任务:
- 按“月份” + “客户等级”分组
- 计算每个组的“客单价”(总销售额 / 总订单数)
- 筛选出“客单价”高于整体平均值的组
- 将结果按客单价降序输出 Top 3
代码实现
import pandas as pd
import numpy as np
# 生成模拟数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=300)
df_sales = pd.DataFrame({
'日期': np.random.choice(dates, 1000),
'客户等级': np.random.choice(['高','中','低'], 1000, p=[0.2,0.5,0.3]),
'销售额': np.random.randint(50, 500, 1000),
'订单数': np.random.randint(1, 5, 1000)
})
df_sales['月份'] = df_sales['日期'].dt.month
# 自定义聚合函数
def custom_avg(group):
total_sales = group['销售额'].sum()
total_orders = group['订单数'].sum()
return total_sales / total_orders
# 分组并应用自定义函数
grouped_result = df_sales.groupby(['月份','客户等级']).apply(custom_avg).reset_index()
grouped_result.columns = ['月份','客户等级','客单价']
# 筛选高于整体平均值的组
overall_avg = df_sales['销售额'].sum() / df_sales['订单数'].sum()
top_groups = grouped_result[grouped_result['客单价'] > overall_avg].sort_values('客单价', ascending=False).head(3)
print("整体平均客单价:", round(overall_avg, 2))
print("\nTop 3 高客单价组:")
print(top_groups)
输出示例:
整体平均客单价: 175.67
Top 3 高客单价组:
月份 客户等级 客单价
5 5 高 234.50
1 2 高 220.33
3 4 中 198.75
解读:
- 高等级客户在5月和2月的客单价显著高于整体
- 中等级在4月表现也超出平均水平
常见问题与问答
❓ Q1:apply 和 agg 有什么区别?
答:
agg用于对分组后的每个列应用一个或多个聚合函数,结果自动合并为DataFrame。apply更灵活,可以传入任意函数(返回Series或标量),但性能略慢。当需要跨列计算时,必须用apply。
❓ Q2:自定义函数返回多个值怎么办?
答: 返回一个Series,索引指定字段名:
def multi_output(group):
avg = group['销售额'].mean()
std = group['销售额'].std()
return pd.Series({'avg': avg, 'std': std})
df.groupby('部门').apply(multi_output)
❓ Q3:如何对分组后的多个列分别用不同自定义函数?
答: 使用 agg 传入字典:
funcs = {
'销售额': ['sum', lambda x: x.mean()/2],
'客户数': 'mean'
}
df.groupby('部门').agg(funcs)
❓ Q4:分组时遇到性能问题怎么优化?
答:
- 优先使用
agg而非apply - 若分组键较多,先对数据排序(
.sort_values()) - 考虑用
numba加速自定义函数 - 对于超大数据,可改用
dask或pyspark
总结与延伸建议
通过本文,你应该掌握了:
- Pandas分组自定义的核心方法(agg/apply/transform)
- 一个完整的多维度销售分析案例
- 常见问题的排查与优化思路
延伸学习建议:
- 尝试用
pipe()方法串联多个自定义函数 - 了解
groupby.filter实现条件筛选 - 探索
pandas-flavor库,将自定义方法注册为原生Pandas方法
本文所有代码在 Python 3.10 + Pandas 2.0 环境下测试通过,如需完整数据,可自行替换为真实业务数据。