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

如何加速Python中计算小时损失的嵌套双循环?

优化小时级损失计算的高效方案

原嵌套双循环的时间复杂度为O(N*M)(N=8760小时数据,M为故障记录数),当M较大时执行效率极低。以下是几种按效率排序的优化方案:

方法一:事件流累加(最优,O(M+N)复杂度)

核心思路是将每个故障的开始/结束转换为"增减事件",通过重采样累加得到小时级损失,避免逐行匹配:

步骤与代码:

  1. 统一转换时间列为datetime类型(处理字符串格式的时间)
  2. 生成事件数据:故障开始时加对应损失,结束时减损失(结束时间向上取整到下一小时,确保当前小时的损失被正确统计)
  3. 合并事件并计算累计损失,再重采样到小时粒度
  4. 与原始小时时间数据对齐得到结果
import pandas as pd
import numpy as np

# 1. 转换时间列类型(注意Forced的日期格式是日/月/年)
Forced['Start'] = pd.to_datetime(Forced['Start'], format='%d/%m/%Y %H:%M')
Forced['End'] = pd.to_datetime(Forced['End'], format='%d/%m/%Y %H:%M')
DF_time['datetime'] = pd.to_datetime(DF_time['datetime'])

# 2. 生成增减事件:开始事件加损失,结束事件减损失
start_events = Forced[['Start', 'Lost']].rename(columns={'Start': 'datetime', 'Lost': 'delta'})
end_events = Forced[['End', 'Lost']].rename(columns={'End': 'datetime', 'Lost': 'delta'})
end_events['delta'] *= -1
# 结束时间向上取整到下一小时,保证当前小时的损失被统计
end_events['datetime'] = end_events['datetime'].dt.ceil('H')

# 3. 合并事件并计算累计损失
events = pd.concat([start_events, end_events]).sort_values('datetime')
events['cum_loss'] = events['delta'].cumsum()

# 4. 重采样到小时粒度,填充缺失值
hourly_loss = events.set_index('datetime').resample('H').ffill().fillna(0)

# 5. 与DF_time对齐,得到最终结果
LOST = DF_time.merge(hourly_loss[['cum_loss']], on='datetime', how='left').rename(columns={'cum_loss': 'perd'})
LOST['perd'] = LOST['perd'].fillna(0)

方法二:向量化区间匹配(O(N*logM)复杂度)

利用numpy广播或pandas区间索引,替代显式循环:

方案A:numpy广播计算

# 先转换时间类型(同方法一)
Forced['Start'] = pd.to_datetime(Forced['Start'], format='%d/%m/%Y %H:%M')
Forced['End'] = pd.to_datetime(Forced['End'], format='%d/%m/%Y %H:%M')
DF_time['datetime'] = pd.to_datetime(DF_time['datetime'])

# 转换为时间戳数组,实现广播比较
hour_stamps = DF_time['datetime'].values[:, np.newaxis]
start_stamps = Forced['Start'].values
end_stamps = Forced['End'].dt.ceil('H').values

# 生成匹配掩码,计算每个小时的总损失
mask = (hour_stamps >= start_stamps) & (hour_stamps < end_stamps)
LOST = DF_time.copy()
LOST['perd'] = np.dot(mask, Forced['Lost'].values)

方案B:pandas区间索引

# 转换时间类型(同方法一)
Forced['Start'] = pd.to_datetime(Forced['Start'], format='%d/%m/%Y %H:%M')
Forced['End'] = pd.to_datetime(Forced['End'], format='%d/%m/%Y %H:%M')
DF_time['datetime'] = pd.to_datetime(DF_time['datetime'])

# 创建区间索引
intervals = pd.IntervalIndex.from_arrays(Forced['Start'], Forced['End'].dt.ceil('H'), closed='left')

# 计算每个小时对应的损失和
LOST = DF_time.copy()
LOST['perd'] = LOST['datetime'].apply(lambda x: Forced.loc[intervals.contains(x), 'Lost'].sum())

原逻辑的最小优化(适合快速调整)

如果要保留逐小时匹配的思路,可通过提前提取数组减少索引开销:

import pandas as pd
import numpy as np

# 转换时间类型(同方法一)
Forced['Start'] = pd.to_datetime(Forced['Start'], format='%d/%m/%Y %H:%M')
Forced['End'] = pd.to_datetime(Forced['End'], format='%d/%m/%Y %H:%M')
DF_time['datetime'] = pd.to_datetime(DF_time['datetime'])

LOST = DF_time.copy()
# 提前提取数组,避免循环内重复索引
hours = DF_time['datetime'].values
starts = Forced['Start'].values
ends = Forced['End'].values
losts = Forced['Lost'].values

# 用列表推导+向量化求和替代双循环
LOST['perd'] = [losts[(h >= starts) & (h <= ends)].sum() for h in hours]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 14:32:41