【问题标题】:How to get rid of the moving average in the data?如何摆脱数据中的移动平均线?
【发布时间】:2020-07-20 12:55:21
【问题描述】:

我有数据 y = [-10.5, -2.0, 5.0, -3.0, 4.0, 9.5, 18.0, 14.5, 11.0, 13.5, 25.0, 21.5, 7.5, 5.5, 3.5, 10.5, 7.0, 3.5, 1.5 , 16.0, 20.0, 22.5, 20.5, 33.5, 27.0, 38.5, 29.0, 27.0, 28.0, 24.5, 24.0, 29.5, 39.5]

并且我想摆脱垂直移动平均趋势(仅获得恒定平均值附近的差异)。在这种情况下,我应该得到类似的东西: [-7、-2、2、-4、0、3、8、5、2、3、10、7、-3、-5、-7、-3、-6、-9、-11、- 2、0、1、-1、7、2、9、2、0、0、-3、-4、-1、5]

Input Output

我的想法是在Input 上拟合一条线(线性回归),得到类似 ax + b 的东西,然后只删除 ax 以接近所需的Output。有没有更标准的数学方法来解决我的问题? (最好在 Python 中实现)。

另外,我认为 Output 和 mean(Output) 的不同值之间的一些差异可能被错误地视为直线斜率的一部分,因此,线性回归可能会给我一个 a'x + b',其中 a' 接近但与 a 不同。我该如何缓解这个问题?

【问题讨论】:

    标签: python statistics linear-regression


    【解决方案1】:

    考虑到疯狂的 FFT 优化,我认为 1D 卷积会很快完成您想要的操作:

    import numpy as np
    from scipy.signal import convolve
    
    window_size = 10
    y = np.array([-10.5, -2.0, 5.0, -3.0, 4.0, 9.5, 18.0, 14.5, 11.0, 13.5, 25.0, 21.5, 7.5, 5.5, 3.5, 10.5, 7.0, 3.5, 1.5, 16.0, 20.0, 22.5, 20.5, 33.5, 27.0, 38.5, 29.0, 27.0, 28.0, 24.5, 24.0, 29.5, 39.5])
    # Pad with zeros for entries before/after the window size
    y_rolling_mean = convolve(y, np.ones(window_size)/window_size, 'same')
    y_without_mean = y - y_rolling_mean
    

    请记住,这通常会为第一个和最后一个 window_size//2 条目产生不准确的值,因为它们的滚动平均值是使用零填充计算的,但是您可以通过在卷积之前使用所需的值进行填充来更改此行为。

    更新:添加了与第二个答案进行比较的情节

    卷积如何找到滚动平均值?

    从本质上讲,一维卷积可以被认为是两个数组的点积,一个数组“滑动”另一个数组(实际上,在这种情况下,相关性在技术上是正确的,但我现在不会深入探讨)。为了更好地理解,请考虑以下场景:

    y = 1 2 3 4 5 6
    x = 1 1 1
    c = <convolution of y and x>
    

    卷积数组的每个输出索引都是 'x' 与 y 的相同长度窗口的点积。所以

    c[0] = sum(y[0:3]*x)
    c[1] = sum(y[1:4]*x)
    c[2] = sum(y[2:5]*x)
    ...
    

    现在,考虑一个事实,即 N 个数字的平均值只是 sum(numbers)/N。或者:

    mean = sum(1/N * number)
    

    结合我们上面关于卷积的知识,让 x 的每个元素 = 1/len(x):

    y =  1    2    3   4  5   6
    x = 1/3  1/3  1/3
    c[0] = 1/3*y[0] + 1/3*y[1] + 1/3*y[2] = mean(y[0:3])
    c[1] = 1/3*y[1] + 1/3*y[2] + 1/3*y[3] = mean(y[1:4]
    ...
    

    整洁!使用特殊形成的 x 向量进行卷积的副作用是该范围的平均值! 因此,通过选择 x 为 np.ones(window_size)/window_size,您可以保证卷积将在 y 上产生滚动均值。

    这在图像处理中被大量使用,当图像中有很多不需要的高频噪声时:

    请注意,与您的一维数据类似,嘈杂图像中的尖锐“峰值”和斑点会被“四舍五入”。

    为什么窗口大小为 10?

    老实说,我随机选择了窗口大小。在实践中,这在很大程度上取决于您期望数据有多嘈杂,以及您希望输出看起来有多“平滑”。 窗口尺寸越大,输出看起来就越平坦。根据提供的玩具数字,y 中似乎有 10 个足够平坦的尖峰,而不会破坏信号。

    【讨论】:

    • 谢谢,我想你的答案就是我想要的。但是,我不明白这个神奇的“卷积”函数是如何工作的。你能详细说明你的答案吗?还有,你为什么选择window_size = 10?
    • 在参加了一些信号处理课程之后,我才对卷积感到不舒服,但我会尽力而为!
    • 我刚刚更新了我的答案。这不是最好的解释,因为卷积比我在这里说的更有用、更复杂、更有趣,但至少它有助于理解这里的应用程序。
    【解决方案2】:

    正如您在问题中提到的线性拟合的概念,我会采用简单但相当稳健的解决方案,即拟合最佳线并简单地从数据中减去它以获得去趋势跟踪:

    import numpy as np
    import matplotlib.pyplot as plt
    
    x = np.arange(len(y)) 
    coefs = np.polyfit(x, y, 1)
    line = coefs[1] + x*coefs[0]
    detrended = y-line
    fig, ax = plt.subplots(1)
    ax.plot(y)
    ax.plot(line)
    ax.plot(detrended)
    

    【讨论】:

    • 您的解决方案与我写问题时的想法非常相似。它适用于存在线性趋势的情况(或者当您知道趋势方程的程度时)。所以感谢您的回答,但我认为@ntjess 写了一个更详细的解决方案,似乎更灵活。
    • 当然,解决方案总是不止一种,您必须评估结果。对于您在问题中提供的特定数据,此处去趋势的结果比在估计和平滑数据的卷积中保持它们的内部关系和整体形状更好。另一方面,卷积在更复杂的情况下是一种强大的方法。通常我会选择最简单的应用程序,因为我知道过度处理数据是有代价的。
    猜你喜欢
    • 2017-09-01
    • 2013-12-22
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-01
    • 1970-01-01
    相关资源
    最近更新 更多