悠悠楠杉
网站页面
正文:
在实际数据分析中,标准的平均值计算往往无法满足需求。例如,电商需要按品类计算销售额的加权平均(权重为销量),或金融领域需要按时间窗口计算波动率的加权值。Pandas的groupby().agg()虽然强大,但原生不支持直接传入权重列。这时,闭包(Closure)就能大显身手。
假设有一份销售数据:
import pandas as pd
data = {
'category': ['电子产品', '电子产品', '服饰', '服饰'],
'price': [5000, 4000, 300, 500],
'sales_volume': [10, 20, 50, 30]
}
df = pd.DataFrame(data)若想按category分组计算价格加权平均(权重为sales_volume),常规做法需预计算:
df['weighted_price'] = df['price'] * df['sales_volume']
result = df.groupby('category').agg(
weighted_avg=('weighted_price', 'sum'),
total_weight=('sales_volume', 'sum')
)
result['final_avg'] = result['weighted_avg'] / result['total_weight']这种方式不仅冗长,且无法在单次agg()中完成。闭包的登场让一切变得优雅。
通过构造一个返回函数的闭包,可以将权重列动态绑定到计算逻辑中:
def weighted_avg(weight_col):
def inner(x):
return (x * df.loc[x.index, weight_col]).sum() / df.loc[x.index, weight_col].sum()
return inner
result = df.groupby('category').agg(
avg_price=('price', 'mean'), # 普通平均
weighted_avg_price=('price', weighted_avg('sales_volume')) # 加权平均
)关键点解析:
1. weighted_avg('sales_volume')返回一个函数inner,该函数能访问外部参数weight_col;
2. 在分组计算时,inner(x)中的x是每组的price序列,通过df.loc[x.index]精准定位对应的权重值。
闭包的灵活性还能扩展。例如,针对不同指标使用不同权重列:
def multi_weighted_avg(weight_dict):
def inner(x):
col_name = x.name # 获取当前列名
weight = weight_dict.get(col_name)
if weight:
return (x * df.loc[x.index, weight]).sum() / df.loc[x.index, weight].sum()
return x.mean()
return inner
weights = {'price': 'sales_volume', 'rating': 'user_count'}
df.groupby('category').agg(multi_weighted_avg(weights))这种设计尤其适合需要同时计算多个加权指标的场景。
df.loc[x.index]能正确匹配权重列,建议在调用前检查索引是否对齐;lambda x: np.average(x, weights=df.loc[x.index, 'weight'])。闭包在Pandas中的应用远不止于此,它为解决复杂分组计算提供了“函数工厂”的能力,让数据聚合既保持简洁又充满弹性。