pandas.Series.ewm在千万级数据上变慢,因其默认保留完整窗口历史、隐式拷贝、类型推断及每次调用都重算全部权重;而numba.jit单次遍历仅维护一个状态变量,O(1)空间、O(n)时间,支持dtype控制与cache复用。

直接用 numpy 手写循环或调用 pandas.Series.ewm 都可能在千万级数据上卡住——关键不在“怎么算”,而在“不把中间结果全存下来”和“避免 Python 层循环”。
为什么 pandas.Series.ewm 在大数据上变慢?
它默认保留完整窗口历史,且底层会做隐式拷贝和类型推断;当 span 或 alpha 动态变化、或你只需要最终值而非整个序列时,开销完全不必要。
- 每调用一次
.mean(),pandas实际执行了完整指数衰减权重生成 + 点积,即使你只要最后一个值 -
min_periods设得小,但内部仍为每个位置检查前序有效计数,带来额外分支判断 - 若原始数据是
float32,pandas默认升格为float64,内存翻倍、缓存不友好
用 numba.jit 写单次遍历的指数平滑
核心是只维护一个状态变量 smoothed,边读边更新,O(1) 空间、O(n) 时间,且可指定 dtype 严格控制精度与内存。
@njit(fastmath=True)
def ewma_inplace(arr, alpha, out):
smoothed = arr[0]
out[0] = smoothed
for i in range(1, len(arr)):
smoothed = alpha * arr[i] + (1 - alpha) * smoothed
out[i] = smoothed
-
alpha必须是标量(不支持数组变化),对应公式y[t] = α·x[t] + (1−α)·y[t−1] - 输入
arr和输出out均需是numpy.ndarray且 dtype 一致(推荐np.float32节省 50% 内存) - 首次运行有编译开销,但后续调用比纯 Python 快 80 倍以上;加
cache=True可复用编译结果
移动加权平均:用 numpy.convolve 还是手写滑动窗口?
如果权重固定(如三角窗、自定义衰减向量),numpy.convolve 简洁安全;但如果窗口超大(>10k)、或需在线流式处理,则必须手写环形缓冲区,否则内存爆炸。
立即学习“Python免费学习笔记(深入)”;
- 用
convolve:权重向量w长度为L,则np.convolve(arr, w, 'valid')输出长度为len(arr)−L+1,且自动补零——注意首尾无填充时结果偏移 - 手写滑动窗口:用
collections.deque(maxlen=L)存最近L个值,每次 pop+append+点积,适合实时场景,但 Python 层循环在L > 100时明显拖慢 - 真正高效的做法:用
numba实现带步长的滑动点积,避免构造中间子数组,例如对每个i计算sum(w[j] * arr[i+j] for j in range(L)),并用prange并行
最易被忽略的是数值稳定性:alpha 接近 0 时,(1−alpha) 多次连乘会因浮点误差累积漂移;实际中建议用 np.nextafter 校验,或改用双精度中间变量——但代价是内存和缓存性能。别只盯着算法复杂度,内存访问模式往往才是瓶颈。


















