如何用NumPy或SciPy实现MATLAB的movmean(中心移动均值)?
复刻MATLAB movmean函数的Python实现
需求匹配
完全对齐MATLAB movmean(x, k, 'shrink') 的行为:
- 滑动窗口以每个数据点为中心
- 端点处自动收缩窗口,仅包含数组内的有效元素
- 偶数窗口大小下,窗口中心偏向右侧(即窗口包含当前点左侧
k//2 -1个元素、当前点、右侧k//2个元素)
实现方案
使用前缀和数组快速计算任意子数组的和,避免循环中重复求和,效率更高。代码如下:
import numpy as np def movmean(x, k): x = np.asarray(x) if x.ndim != 1: raise ValueError("仅支持一维数组输入") n = len(x) if k < 1: raise ValueError("窗口大小k必须≥1") # 构建前缀和数组,pre_sum[0]=0,pre_sum[i] = x[0]+...+x[i-1] pre_sum = np.zeros(n + 1, dtype=np.float64) pre_sum[1:] = np.cumsum(x) mean_arr = np.zeros(n, dtype=np.float64) if k % 2 == 1: half = (k - 1) // 2 for i in range(n): # 计算窗口的起始和结束索引(左闭右开) start = max(0, i - half) end = min(n, i + half + 1) count = end - start mean_arr[i] = (pre_sum[end] - pre_sum[start]) / count else: left_extend = k // 2 - 1 right_extend = k // 2 for i in range(n): start = max(0, i - left_extend) end = min(n, i + right_extend + 1) count = end - start mean_arr[i] = (pre_sum[end] - pre_sum[start]) / count return mean_arr
代码解释
- 输入处理:将输入转为numpy一维数组,做合法性校验
- 前缀和计算:通过
np.cumsum生成前缀和数组,O(n)时间复杂度,后续子数组和可通过O(1)时间计算 - 窗口范围计算:
- 奇数窗口:以当前索引为中心,向左右各扩展
(k-1)//2个元素,超出数组边界则取边缘 - 偶数窗口:向左侧扩展
k//2 -1个元素,右侧扩展k//2个元素,保证窗口总长度为k,同时处理边界收缩
- 奇数窗口:以当前索引为中心,向左右各扩展
- 均值计算:每个窗口的和由前缀和数组差值得到,除以窗口内元素个数即为均值
测试验证
# 测试奇数窗口(k=3) x1 = [1, 2, 3, 4] print(movmean(x1, 3)) # 输出:[1.5 2. 3. 3.5] # 测试偶数窗口(k=4) x2 = [1, 2, 3, 4, 5] print(movmean(x2, 4)) # 输出:[2. 2.5 3.5 4. 4.5]
以上结果与MATLAB movmean 函数的输出完全一致。
内容的提问来源于stack exchange,提问作者nn123
相关产品推荐
相关产品推荐

