SciPy medfilt 结果错误

8 投票
1 回答
12013 浏览
提问于 2025-04-18 12:11

嗨,Python爱好者们!

我现在在做信号过滤的研究,决定用SciPy这个库。没什么特别的,就是想自动化一些日常工作。

这是我的代码:

from scipy.signal import medfilt
print(medfilt([2,6,5,4,0,3,5,7,9,2,0,1], 5))

但是问题是,返回的结果计算得不对。

SciPy: [ 2. 4. 4. 4. 4. 4. 5. 5. 5. 2. 1. 0.]
Me   : [ 5. 4.5 4. 4. 4. 4. 5. 5. 5. 2. 1.5 1.]

看起来这个库的开发者在某个细节上搞错了。当窗口(在SciPy中叫做核)大于要分析的范围时,过滤的规则就不一样了。

举个例子,如果kernel=5,那么对于子序列[2, 6, 5],它的中位数应该是5,而不是SciPy计算的2,对吧?同样的,如果kernel=5,对于子序列[2, 6, 5, 4],中位数是5和4,我们需要取它们的平均值,所以中位数是4.5。

有人能告诉我在这种情况下谁的结果是正确的吗?

1 个回答

19

我认为你和SciPy的结果都是正确的。区别在于在边界处发生了什么,但我相信你和SciPy都做出了合理的选择。

问题是当你的滑动窗口在边缘时,如果没有有效的数据来填充这个窗口,该怎么办

你选择了取滑动窗口有效部分的中位数,这样做是有道理的,但可能会因为边缘点的数量相对其他点过多而产生一些偏差。

SciPy则选择在边缘用零来填充信号。因此,在边界处,SciPy实际上是在计算

>>> np.median([0, 0, 2, 6, 5])
2.0
>>> np.median([0, 2, 6, 5, 4])
4.0
>>> np.median([9, 2, 0, 1, 0])
1.0
>>> np.median([2, 0, 1, 0, 0])
0.0

SciPy这样做的原因几乎肯定是为了速度:它被优化为多次执行相同的操作,优化处理一堆5个元素的数组要比同时处理一堆5个元素的数组、两个4个元素的数组和两个3个元素的数组容易得多。确实可以说不应该用零来填充,而是用边界值来填充,但需要注意的是,没有一种边界处理策略是完美的;处理边界问题的理想方法将取决于你的具体信号。

如果你查看维基百科对中位数滤波器的描述,他们在边缘用边界值来填充信号,这也很合理。他们还提到处理边界问题的另外三种方法:

  • 避免处理边界,可以选择在处理后裁剪信号边界。
  • 从信号的其他地方获取数据。例如,对于图像,可以选择远离水平或垂直边界的像素。
  • 在边界附近缩小窗口,以确保每个窗口都是满的(就像你做的那样)。

最终,你真的需要尝试不同的选项,看看哪个对你的信号效果最好。这种滤波的核心假设是你的信号会相当大,边界问题通常不会太关键(因为大部分信号并不在边界上)。不过,如果SciPy能让你选择在边界处该怎么做,那就更好了!

撰写回答