新手求助:优化DataFrame滚动求和计算月度最大半小时电力负荷代码
优化电力负荷数据的月度滚动半小时最大需求计算
我完全懂你现在的困扰——100个用户、每个超10万行的数据集,原代码单用户就要跑36分钟,这效率确实没法接受。咱们先拆解原代码里拖慢速度的核心问题,再一步步给出高效的优化方案。
原代码的性能瓶颈
- 重复的数据查询开销:最内层循环里反复执行
df_input[filename].loc[df_input[filename]['Date_conv']==m_o_y],每次都要重新筛选数据,这是巨大的性能浪费 - Python原生循环的低效:用
for循环逐行计算窗口求和,完全没利用Pandas的向量化运算能力,这是处理大数据时的典型低效操作 - 错误的结果拼接方式:直接给
df_m_hh_max的列赋值会导致数据覆盖,最后可能只保留最后一条记录,而且这种方式本身也不高效
优化方案:用Pandas向量化+窗口函数重构
下面是重构后的代码,完全基于Pandas的内置优化函数,效率能提升几个数量级:
import pandas as pd def monthly_hh_maximum(df_input, filenames): # 提前初始化结果DataFrame,指定列类型更高效 df_m_hh_max = pd.DataFrame(columns=['filename', 'm_o_y', 'm_max'], dtype='float64') for filename in filenames: print(f"Processing {filename}...") # 一次性取出当前用户的所有数据,避免重复查询 user_df = df_input[filename].copy() # 1. 用滚动窗口计算所有连续6条记录的求和(30分钟负荷) # rolling是Pandas底层优化的向量化操作,比Python循环快几十倍 user_df['30min_sum'] = user_df['load power (kW)'].rolling(window=6, min_periods=6).sum() # 2. 按月份分组,直接提取每个月的滚动求和最大值 monthly_max = user_df.groupby('Date_conv')['30min_sum'].max().reset_index() # 3. 添加用户标识,合并到结果集 monthly_max['filename'] = filename df_m_hh_max = pd.concat( [df_m_hh_max, monthly_max.rename(columns={'Date_conv': 'm_o_y', '30min_sum': 'm_max'})], ignore_index=True ) return df_m_hh_max
优化点详解
- 避免重复数据筛选:每个用户的数据只取出一次,后续所有操作都基于这个本地DataFrame,省去了反复
loc查询的巨大开销 - 向量化滚动计算:
rolling().sum()是Pandas底层用C实现的向量化操作,一次性计算所有窗口的求和值,比Python原生循环效率提升百倍以上 - 分组聚合取最大值:
groupby().max()直接按月份分组提取最大值,替代了内层循环的逐次比较逻辑,代码更简洁且效率更高 - 正确的结果拼接:用
pd.concat批量合并每个用户的月度结果,避免了原代码中列赋值导致的数据覆盖问题
额外性能升级建议
- 日期格式优化:如果
Date_conv还不是标准日期类型,提前转换为datetime后提取月份(比如user_df['Date_conv'] = user_df['datetime_col'].dt.to_period('M')),分组会更稳定 - 内存优化:如果精度允许,把
load power (kW)列转换为float32类型,减少内存占用,能进一步提升运算速度 - 并行处理:如果你的机器有多核心CPU,可以用多进程并行处理不同用户的数据,100个用户的处理时间能压缩到几十分钟内。举个简单示例:
from multiprocessing import Pool def process_single_user(filename): user_df = df_input[filename].copy() user_df['30min_sum'] = user_df['load power (kW)'].rolling(window=6, min_periods=6).sum() monthly_max = user_df.groupby('Date_conv')['30min_sum'].max().reset_index() monthly_max['filename'] = filename return monthly_max.rename(columns={'Date_conv': 'm_o_y', '30min_sum': 'm_max'}) if __name__ == '__main__': # 根据你的CPU核心数调整processes参数 with Pool(processes=4) as pool: results = pool.map(process_single_user, filenames) df_m_hh_max = pd.concat(results, ignore_index=True)
这样调整后,原本需要几天的计算任务,应该能在几十分钟内完成。
内容的提问来源于stack exchange,提问作者Zeba Anjum
相关产品推荐
相关产品推荐

