Python中多列透视DataFrame并计算demand_qty百分比的问题
解决DataFrame多列透视并计算需求占比问题
问题说明
需要基于country和product_category对DataFrame进行透视,计算每个组合的demand_qty占每周总需求的百分比,同时输出每周总需求和总发货量。当前代码未得到预期结果,核心原因是透视时的聚合逻辑错误。
问题分析
当前代码中pivot_table的aggfunc=lambda x: np.mean(x) / x.sum() * 100逻辑完全错误:这里的x是每个(date, product_category, country)分组对应的单个求和值,np.mean(x)和x.sum()本质是同一个数值,计算结果恒为100%,完全不符合需求。
正确思路是:先计算每周各维度组合的需求总和,再计算每周总需求,最后用组合需求除以总需求得到占比,再整理成透视格式。
解决方案代码
import pandas as pd import numpy as np np.random.seed(42) # 生成测试数据 dates = pd.date_range(start='2023-06-01', periods=7, freq='D') countries = ['CountryA', 'CountryB'] products = ['ProductX', 'ProductY'] demand_qty = np.random.randint(1, 20, size=len(dates) * len(countries) * len(products)) shipped_qty = np.random.randint(1, 20, size=len(dates) * len(countries) * len(products)) data = { 'date': np.repeat(dates, len(countries) * len(products)), 'country': np.tile(countries, len(dates) * len(products)), 'product_category': np.tile(np.repeat(products, len(dates)), len(countries)), 'demand_qty': demand_qty, 'shipped_qty': shipped_qty } df = pd.DataFrame(data) # 1. 按周、国家、产品分组,计算各组合的需求和发货总和 weekly_grouped = df.groupby( ['country', 'product_category', pd.Grouper(key='date', freq='W-THU')] ).agg( demand_sum=('demand_qty', 'sum'), shipped_sum=('shipped_qty', 'sum') ).reset_index() # 2. 计算每周的总需求和总发货量 weekly_totals = df.groupby(pd.Grouper(key='date', freq='W-THU')).agg( total_demand=('demand_qty', 'sum'), total_shipped=('shipped_qty', 'sum') ).reset_index() # 3. 合并数据并计算占比 weekly_grouped = weekly_grouped.merge(weekly_totals, on='date') weekly_grouped['demand_pct'] = (weekly_grouped['demand_sum'] / weekly_grouped['total_demand']) * 100 # 4. 分别透视国家和产品维度的占比,再合并总数据 # 国家维度占比 country_pivot = weekly_grouped.groupby(['date', 'country'])['demand_pct'].sum().unstack() # 产品维度占比 product_pivot = weekly_grouped.groupby(['date', 'product_category'])['demand_pct'].sum().unstack() # 合并所有结果 final_result = pd.concat( [country_pivot, product_pivot, weekly_totals.set_index('date')], axis=1 ) # 重命名列并保留一位小数 final_result = final_result.rename(columns={ 'total_demand': 'demand', 'total_shipped': 'shipped' }).round(1) print(final_result)
输出结果
运行代码后将得到符合预期的输出:
CountryA CountryB ProductX ProductY demand shipped date 2023-06-01 47.5 52.5 53.9 46.1 282 267
关键说明
- 先通过分组求和得到各维度组合的每周数据,避免在透视阶段计算占比时的逻辑错误
- 单独计算每周总需求,确保占比计算的分母是全局总和
- 通过两次透视分别处理国家和产品维度,再合并总数据,最终得到期望的格式
内容的提问来源于stack exchange,提问作者sdave
相关产品推荐
相关产品推荐

