蒙特卡洛模拟中如何高效为Pandas所有列应用滚动窗口?
高效替代方案:基于Numpy的批量滑动窗口计算
你的核心问题是逐列循环调用rolling.apply的效率极低——这是因为rolling.apply本质上是逐行调用Python函数,加上10000列的循环,双重低效叠加。我们可以通过Numpy的批量数组操作直接完成所有列的滑动窗口计算,完全避免循环。
步骤原理
- 先将原数据转换为布尔数组(标记每个元素是否>0)
- 对所有列批量计算滑动窗口内
True的数量 - 判断数量是否>20,再补全前29个位置的
NaN(和原rolling行为一致)
实现代码
import pandas as pd import numpy as np from scipy.stats import norm trials = 10000 df1 = pd.DataFrame(norm.rvs(size=(500, trials))) # 1. 转换为布尔数组:元素>0为True,否则False bool_arr = df1.values > 0 window_size = 30 n_rows, n_cols = bool_arr.shape # 2. 批量计算滑动窗口内True的数量(用Numpy as_strided实现高效滑动窗口) from numpy.lib.stride_tricks import as_strided # 构造滑动窗口视图:形状为(n_rows - window_size + 1, window_size, n_cols) strides = (bool_arr.strides[0], bool_arr.strides[0], bool_arr.strides[1]) windowed_arr = as_strided( bool_arr, shape=(n_rows - window_size + 1, window_size, n_cols), strides=strides ) # 对每个窗口求和(axis=1是窗口维度) window_sums = windowed_arr.sum(axis=1) # 3. 生成最终结果:前29个位置补NaN,后面是是否>20的布尔值 result = np.full((n_rows, n_cols), np.nan) result[window_size-1:] = window_sums > 20 # 转换为DataFrame df2 = pd.DataFrame(result, columns=df1.columns)
为什么这个方法快?
- 完全避免了Python层面的循环,所有计算都是Numpy的C级批量操作
as_strided只是创建数组视图,不复制数据,内存开销极小- 求和与布尔判断都是向量化操作,效率比逐列调用
rolling.apply高几个数量级
另一种可选方法:用卷积实现求和
如果担心as_strided的内存视图操作不够直观,可以用卷积来计算窗口和:
from scipy.signal import convolve2d # 构造卷积核:30行1列的全1数组,对应沿列方向的滑动窗口 kernel = np.ones((window_size, 1), dtype=int) # 用"valid"模式计算,只保留有完整窗口的结果 window_sums = convolve2d(bool_arr, kernel, mode='valid') # 同样补NaN result = np.full((n_rows, n_cols), np.nan) result[window_size-1:] = window_sums > 20 df2 = pd.DataFrame(result, columns=df1.columns)
这个方法的效率和as_strided接近,代码更直观,适合对内存视图操作不熟悉的场景。
内容的提问来源于stack exchange,提问作者Charles0349
相关产品推荐
相关产品推荐

