You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何向量化Pandas双层循环以提升大DataFrame运行效率?

Pandas双层循环性能优化求助

问题描述

我写了一段Pandas双层for循环代码,功能是遍历目标DataFrame(df)的所有列与时间戳索引,匹配另一数据集(dat)中满足start < 时间戳、end > 时间戳且plant_type与df列名一致的记录,计算这些记录的Difference总和后赋值给df对应位置。代码能正常运行,但因df数据量较大,单次运行耗时长达11分钟。我尝试用向量化方式优化,但多次尝试均报错无法执行,恳请提供优化方案。

数据结构

df结构

{'Fossil Gas': {Timestamp('2022-01-01 00:00:00', freq='H'): 0, Timestamp('2022-01-01 01:00:00', freq='H'): 0, Timestamp('2022-01-01 02:00:00', freq='H'): 0, Timestamp('2022-01-01 03:00:00', freq='H'): 0, Timestamp('2022-01-01 04:00:00', freq='H'): 0}, 'Fossil Brown coal/Lignite': {Timestamp('2022-01-01 00:00:00', freq='H'): 0, Timestamp('2022-01-01 01:00:00', freq='H'): 0, Timestamp('2022-01-01 02:00:00', freq='H'): 0, Timestamp('2022-01-01 03:00:00', freq='H'): 0, Timestamp('2022-01-01 04:00:00', freq='H'): 0}, 'Biomass': {Timestamp('2022-01-01 00:00:00', freq='H'): 0, Timestamp('2022-01-01 01:00:00', freq='H'): 0, Timestamp('2022-01-01 02:00:00', freq='H'): 0, Timestamp('2022-01-01 03:00:00', freq='H'): 0, Timestamp('2022-01-01 04:00:00', freq='H'): 0}, 'Nuclear': {Timestamp('2022-01-01 00:00:00', freq='H'): 0, Timestamp('2022-01-01 01:00:00', freq='H'): 0, Timestamp('2022-01-01 02:00:00', freq='H'): 0, Timestamp('2022-01-01 03:00:00', freq='H'): 0, Timestamp('2022-01-01 04:00:00', freq='H'): 0}, 'Fossil Oil': {Timestamp('2022-01-01 00:00:00', freq='H'): 0, Timestamp('2022-01-01 01:00:00', freq='H'): 0, Timestamp('2022-01-01 02:00:00', freq='H'): 0, Timestamp('2022-01-01 03:00:00', freq='H'): 0, Timestamp('2022-01-01 04:00:00', freq='H'): 0}, 'Wind Onshore': {Timestamp('2022-01-01 00:00:00', freq='H'): 0, Timestamp('2022-01-01 01:00:00', freq='H'): 0, Timestamp('2022-01-01 02:00:00', freq='H'): 0, Timestamp('2022-01-01 03:00:00', freq='H'): 0, Timestamp('2022-01-01 04:00:00', freq='H'): 0}, 'Solar': {Timestamp('2022-01-01 00:00:00', freq='H'): 0, Timestamp('2022-01-01 01:00:00', freq='H'): 0, Timestamp('2022-01-01 02:00:00', freq='H'): 0, Timestamp('2022-01-01 03:00:00', freq='H'): 0, Timestamp('2022-01-01 04:00:00', freq='H'): 0}, 'Fossil Hard coal': {Timestamp('2022-01-01 00:00:00', freq='H'): 0, Timestamp('2022-01-01 01:00:00', freq='H'): 0, Timestamp('2022-01-01 02:00:00', freq='H'): 0, Timestamp('2022-01-01 03:00:00', freq='H'): 0, Timestamp('2022-01-01 04:00:00', freq='H'): 0}, 'Other renewable': {Timestamp('2022-01-01 00:00:00', freq='H'): 0, Timestamp('2022-01-01 01:00:00', freq='H'): 0, Timestamp('2022-01-01 02:00:00', freq='H'): 0, Timestamp('2022-01-01 03:00:00', freq='H'): 0, Timestamp('2022-01-01 04:00:00', freq='H'): 0}}

dat结构

{'Unnamed: 0': {Timestamp('2016-07-19 12:00:00'): 0, Timestamp('2018-01-01 00:00:00'): 243}, 'avail_qty': {Timestamp('2016-07-19 12:00:00'): 0.0, Timestamp('2018-01-01 00:00:00'): 0.0}, 'biddingzone_domain': {Timestamp('2016-07-19 12:00:00'): 'HU', Timestamp('2018-01-01 00:00:00'): 'HU'}, 'businesstype': {Timestamp('2016-07-19 12:00:00'): 'Unplanned outage', Timestamp('2018-01-01 00:00:00'): 'Planned maintenance'}, 'curvetype': {Timestamp('2016-07-19 12:00:00'): 'A03', Timestamp('2018-01-01 00:00:00'): 'A03'}, 'docstatus': {Timestamp('2016-07-19 12:00:00'): nan, Timestamp('2018-01-01 00:00:00'): nan}, 'end': {Timestamp('2016-07-19 12:00:00'): Timestamp('2018-01-25 00:00:00'), Timestamp('2018-01-01 00:00:00'): Timestamp('2019-01-01 00:00:00')}, 'mrid': {Timestamp('2016-07-19 12:00:00'): 'doEAXAnlBP-GXBLvSXLrww', Timestamp('2018-01-01 00:00:00'): 'm7N8fbZN3jM4LLrCTgu1zw'}, 'nominal_power': {Timestamp('2016-07-19 12:00:00'): 25.0, Timestamp('2018-01-01 00:00:00'): 95.0}, 'plant_type': {Timestamp('2016-07-19 12:00:00'): 'Fossil Gas', Timestamp('2018-01-01 00:00:00'): 'Fossil Gas'}, 'production_resource_id': {Timestamp('2016-07-19 12:00:00'): '15WDUME------PPI', Timestamp('2018-01-01 00:00:00'): '15WDKCE------PPQ'}, 'production_resource_location': {Timestamp('2016-07-19 12:00:00'): 'Százhalombatta', Timestamp('2018-01-01 00:00:00'): 'Debrecen'}, 'production_resource_name': {Timestamp('2016-07-19 12:00:00'): 'Dunamenti Erőmű', Timestamp('2018-01-01 00:00:00'): 'Debreceni Kombináltciklusú Erőmű'}, 'pstn': {Timestamp('2016-07-19 12:00:00'): 1, Timestamp('2018-01-01 00:00:00'): 1}, 'qty_uom': {Timestamp('2016-07-19 12:00:00'): 'MAW', Timestamp('2018-01-01 00:00:00'): 'MAW'}, 'resolution': {Timestamp('2016-07-19 12:00:00'): 'PT60M', Timestamp('2018-01-01 00:00:00'): 'PT15M'}, 'revision': {Timestamp('2016-07-19 12:00:00'): 1, Timestamp('2018-01-01 00:00:00'): 944}, 'start': {Timestamp('2016-07-19 12:00:00'): Timestamp('2016-07-19 12:00:00'), Timestamp('2018-01-01 00:00:00'): Timestamp('2018-01-01 00:00:00')}, 'created_doc_time': {Timestamp('2016-07-19 12:00:00'): '2018-01-25 06:00:54+01:00', Timestamp('2018-01-01 00:00:00'): '2018-12-30 15:36:00+01:00'}, 'Deleted': {Timestamp('2016-07-19 12:00:00'): 25.0, Timestamp('2018-01-01 00:00:00'): 95.0}}

优化方案建议

注:以下方案中假设Difference对应dat中的Deleted列,若实际为其他列可自行替换。

方法1:长格式合并+分组求和

将df转为长格式后与dat合并,筛选时间匹配的记录再分组求和,最后转回宽格式更新原df,全程避免循环:

import pandas as pd

# 将df转为长格式(时间戳+plant_type为行维度)
df_long = df.stack().reset_index()
df_long.columns = ['timestamp', 'plant_type', 'value']

# 重置dat索引(原索引无业务意义)
dat_reset = dat.reset_index(drop=True)

# 按plant_type合并两个数据集
merged = pd.merge(df_long, dat_reset, on='plant_type', how='left')

# 筛选时间戳在[start, end)区间内的记录
mask = (merged['timestamp'] > merged['start']) & (merged['timestamp'] < merged['end'])
filtered = merged[mask]

# 按时间戳和plant_type分组求和
summed = filtered.groupby(['timestamp', 'plant_type'])['Deleted'].sum().reset_index()

# 转回宽格式并更新原df
result = summed.pivot(index='timestamp', columns='plant_type', values='Deleted').fillna(0)
df.update(result)

方法2:时间区间匹配

利用Pandas的IntervalIndex快速判断时间戳是否在区间内,减少循环次数:

# 为dat创建时间区间列(左开右闭,匹配需求中的start<时间戳<end)
dat['time_interval'] = pd.IntervalIndex.from_arrays(dat['start'], dat['end'], closed='neither')

# 仅按plant_type循环,内部用矢量化判断
for plant in df.columns:
    plant_records = dat[dat['plant_type'] == plant]
    if plant_records.empty:
        continue
    # 对每个时间戳,计算符合条件的Deleted总和
    df[plant] = df.index.to_series().apply(
        lambda ts: plant_records[plant_records['time_interval'].contains(ts)]['Deleted'].sum()
    )

方法3:NumPy广播矢量化

将时间戳和区间转为NumPy数组,用广播机制批量计算匹配情况,性能最优:

import numpy as np

# 提取df的时间戳数组和plant类型列表
timestamps = df.index.values
plants = df.columns.values

for plant in plants:
    plant_data = dat[dat['plant_type'] == plant]
    if plant_data.empty:
        continue
    # 提取dat中的关键数组
    starts = plant_data['start'].values
    ends = plant_data['end'].values
    deletes = plant_data['Deleted'].values
    
    # 广播判断每个时间戳是否在每个区间内
    match_mask = (timestamps[:, np.newaxis] > starts) & (timestamps[:, np.newaxis] < ends)
    # 按时间轴求和
    total_deletes = np.sum(match_mask * deletes, axis=1)
    # 赋值给df对应列
    df[plant] = total_deletes

内容的提问来源于stack exchange,提问作者user21320342

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 19:44:55