优化Pandas滑动窗口均值计算:替代双层for循环的高效方案
高效优化滑动窗口均值计算方案
你的双层循环逻辑是正确的,但效率低下的核心问题在于每次切片后都重新计算窗口内的均值,这会导致大量重复计算(比如大窗口会包含小窗口的所有元素,却要重新求和)。下面给你两个更高效的实现方案,既能得到正确结果,又能大幅提升运行速度:
方案一:前缀和数组(性能最优)
前缀和是处理区间求和/均值问题的经典技巧,它能把任意区间的均值计算复杂度降到O(1),整体效率比原始循环提升几个数量级。
实现步骤:
- 计算
col2列的前缀和数组,这样任意区间[j, j+k-1]的和可以通过prefix[j+k] - prefix[j]快速得到 - 遍历所有符合要求的窗口大小(从
min_window到数据框总长度) - 对每个窗口大小,遍历所有合法起始位置,用前缀和计算均值,同时记录窗口的起始/结束
ind值
代码示例:
import numpy as np import pandas as pd # 你的原始DataFrame df = pd.DataFrame({ 'col1': ['A','B','C','D','E','F','G','H','I','J','K','L'], 'col2': [1,2,10,5,11,4,7,20,33,24,22,5], 'ind': list(range(12)) }) min_window = len(df) // 4 # 这里计算得3 total_rows = len(df) # 生成前缀和数组:prefix[0]=0,prefix[i]是col2前i个元素的和 prefix = np.zeros(total_rows + 1) prefix[1:] = df['col2'].cumsum() # 初始化结果存储列表 start_indices = [] stop_indices = [] averages = [] # 遍历所有合法窗口大小 for window_size in range(min_window, total_rows + 1): # 每个窗口大小对应的起始位置范围 for start_pos in range(total_rows - window_size + 1): # 计算窗口均值:(区间和)/窗口大小 window_sum = prefix[start_pos + window_size] - prefix[start_pos] avg = window_sum / window_size # 记录窗口的起始/结束ind值 start_indices.append(df['ind'].iloc[start_pos]) stop_indices.append(df['ind'].iloc[start_pos + window_size - 1]) averages.append(avg) # 找出均值最大的所有窗口 max_avg = max(averages) max_windows = [ (s, e, round(avg, 2)) for s, e, avg in zip(start_indices, stop_indices, averages) if avg == max_avg ] print("均值最大的窗口组合:") for win in max_windows: print(f"起始ind: {win[0]}, 结束ind: {win[1]}, 均值: {win[2]}")
方案二:Pandas Rolling 方法(代码更简洁)
如果你更看重代码的可读性和简洁性,可以用Pandas内置的rolling函数。它内部已经做了滑动窗口的优化计算,避免了重复求和,效率也远高于原始的双层循环。
代码示例:
import pandas as pd # 原始DataFrame同上 df = pd.DataFrame({ 'col1': ['A','B','C','D','E','F','G','H','I','J','K','L'], 'col2': [1,2,10,5,11,4,7,20,33,24,22,5], 'ind': list(range(12)) }) min_window = len(df) // 4 total_rows = len(df) start_indices = [] stop_indices = [] averages = [] # 遍历所有合法窗口大小 for window_size in range(min_window, total_rows + 1): # 计算滚动均值,min_periods=window_size确保只保留完整窗口的结果 rolling_avg_series = df['col2'].rolling(window=window_size, min_periods=window_size).mean() # 遍历每个有效窗口的结束位置 for end_pos in range(window_size - 1, total_rows): avg = rolling_avg_series.iloc[end_pos] # 计算窗口的起始位置 start_pos = end_pos - window_size + 1 # 记录ind值 start_indices.append(df['ind'].iloc[start_pos]) stop_indices.append(df['ind'].iloc[end_pos]) averages.append(avg) # 找出均值最大的窗口 max_avg = max(averages) max_windows = [ (s, e, round(avg, 2)) for s, e, avg in zip(start_indices, stop_indices, averages) if avg == max_avg ] print("均值最大的窗口组合:") for win in max_windows: print(f"起始ind: {win[0]}, 结束ind: {win[1]}, 均值: {win[2]}")
性能对比说明
- 原始双层循环的时间复杂度是O(n² * k)(k为窗口大小),数据量越大,窗口越大,效率越低。
- 前缀和方法的时间复杂度是O(n²),每个窗口的均值计算都是O(1),是性能最优的选择。
- Rolling方法的时间复杂度接近O(n²),但内部有Pandas的优化,代码更简洁易维护,适合大多数场景。
内容的提问来源于stack exchange,提问作者Kallol
相关产品推荐
相关产品推荐

