求Pandas支持索引/列感知的单元格级apply函数及数据清洗优化方案
更高效的Pandas风格实现方案
你的核心痛点是双重循环的低效,我们可以通过向量化操作、广播机制和Pandas原生分组变换完全替代循环,同时保持逻辑清晰。以下是分步优化方案:
一、优化辅助计算步骤
1. 计算州级产品年度总量与季节性
替代嵌套apply,用groupby+reindex直接生成州级季节性占比:
# 州级产品年度总量(按Product+Year分组求和) tot_annual = df.groupby(['Product', df.columns.str[:2]], axis=1).sum() # 州级季节性:每个季度占当年州级总量的比例 tot_season = df / tot_annual.reindex(columns=df.columns, level=1).ffill(axis=1)
通过reindex+ffill把年度总量广播到每个季度列,直接做除法得到季节性,逻辑更简洁。
2. 验证县-产品年度数据有效性
用groupby+transform替代嵌套apply,批量验证数据有效性:
# 州级每个产品-年份的非零季度数 tot_nonzero = (df != 0).groupby(['Product', df.columns.str[:2]], axis=1).sum() # 县级每个产品-年份的非零季度数(自动广播回原维度) cty_nonzero = (df != 0).groupby(['Product', df.columns.str[:2]], axis=1).transform('sum') # 标记有效年份:县级非零数等于州级非零数 cty_valid = cty_nonzero == tot_nonzero.reindex(columns=df.columns, level=1).ffill(axis=1)
3. 计算县-产品年度总量(广播到季度)
用transform自动生成与原表同维度的年度总量,无需手动重命名列:
cty_annual = df.groupby(['Product', df.columns.str[:2]], axis=1).transform('sum')
二、核心:向量化生成县级季节性矩阵
完全替代双重循环,用np.where结合广播机制一键生成cty_season:
# 计算县级实际季节性(仅有效年份可用) actual_season = df / cty_annual # 将州级季节性广播到县级维度(匹配每个县对应的Product) state_season = tot_season.loc[df.index.get_level_values('Product')].values # 按有效性选择使用实际或州级季节性 cty_season = pd.DataFrame( np.where(cty_valid, actual_season, state_season), index=df.index, columns=df.columns )
通过df.index.get_level_values('Product')匹配每个县对应的产品,再用数组广播实现维度对齐,np.where完成条件选择。
三、最终生成调整后的数据
和原逻辑一致:
cty_adj = cty_season * cty_annual
完整优化代码
import pandas as pd import numpy as np data = [[73, 0, 0, 22, 0, 34, 5, 46], [51, 12, 77, 0, 19, 3, 0, 34], [73, 44, 1, 72, 0, 56, 21, 3], [ 3, 74, 2, 24, 4, 60, 8, 39], [70, 0, 36, 50, 3, 1, 59, 1], [14, 37, 26, 27, 87, 58, 95, 2], [ 4, 1, 17, 34, 25, 1, 1, 2], [ 0, 0, 0, 4, 18, 1, 8, 0], [42, 27, 41, 15, 67, 2, 25, 6]] df = pd.DataFrame(data, index=pd.MultiIndex.from_product([['County 1','County 2','County 3'],['A','B','C']],names=['County','Product']), columns=pd.Series(['Y1Q1','Y1Q2','Y1Q3','Y1Q4','Y2Q1','Y2Q2','Y2Q3','Y2Q4'],name='Quarter')) # 1. 计算州级产品年度总量与季节性 tot_annual = df.groupby(['Product', df.columns.str[:2]], axis=1).sum() tot_season = df / tot_annual.reindex(columns=df.columns, level=1).ffill(axis=1) # 2. 验证县-产品年度数据有效性 tot_nonzero = (df != 0).groupby(['Product', df.columns.str[:2]], axis=1).sum() cty_nonzero = (df != 0).groupby(['Product', df.columns.str[:2]], axis=1).transform('sum') cty_valid = cty_nonzero == tot_nonzero.reindex(columns=df.columns, level=1).ffill(axis=1) # 3. 计算县-产品年度总量(广播到季度) cty_annual = df.groupby(['Product', df.columns.str[:2]], axis=1).transform('sum') # 4. 向量化生成县级季节性矩阵 actual_season = df / cty_annual state_season = tot_season.loc[df.index.get_level_values('Product')].values cty_season = pd.DataFrame( np.where(cty_valid, actual_season, state_season), index=df.index, columns=df.columns ) # 5. 生成调整后的数据 cty_adj = cty_season * cty_annual
为什么这更Pandas化?
- 无循环:完全用向量化操作替代逐元素循环,大数据量下性能提升显著
- 原生API:依赖
groupby、transform等核心功能,逻辑易读易维护 - 广播机制:自动对齐维度,避免手动处理索引/列名
- 可复用性:只需调整列名切片规则,就能扩展到月度数据或其他层级的行政区划
内容的提问来源于stack exchange,提问作者Carl Peterson
相关产品推荐
相关产品推荐

