【问题标题】:smooth signal and find peaks平滑信号并找到峰值
【发布时间】:2019-10-13 10:11:49
【问题描述】:

假设我有一个 X 和 Y 数组,这样:

X = np.array([1,2,3,4,5,6,7,8,9,10,11,12])

Y = np.array([-19.9, -19.6, -17.6, -15.9, -19.9, -18.4, -17.7, -16.6, -19.5, -20.4, -17.6, -15.9])

我得到一个类似的情节:

我可以看到 3 个非常清晰的山峰。我可以使用以下方法拟合这些数据:

# fit polynomial
z = np.polyfit(X1, Y, 8)
f = np.poly1d(z)

# calculate new x's and y's
x_new = np.linspace(X[0], X[-1], 100)
y_new = f(x_new)

我可以得到以下显示一年中信号变化的信息 - 在这种情况下是水稻农业和农业周期数(3 个峰值):

这里我使用 scipy.signal.argrelextrema 来查找曲线的波峰和波谷。但是,要获得一条拟合良好的曲线是一种非常“手动”的方法,我必须先用肉眼解释数据,才能选择多项式阶数。我将在许多数据集(100,000 个)上重复此过程,因此无法每次都手动执行此操作。

此外,我的峰值数量可能会发生变化。事实上,我的最终目标是将我拥有的数据集分类为我可以检测到的峰值数量。也有信号有更多噪声的情况。

我研究了 scipy.signal.find_peaks (和相关算法),但这发现了每个峰值,而不仅仅是主要峰值,尤其是在噪声较大的数据中。我还研究了 savgol 过滤器和高斯过滤器,并且能够得到结果,但通常必须指定多项式等的阶数,这可能会随着峰值的数量而变化。

有没有一种方法可以平滑信号以获得峰值数量的近似值,而无需手动指定多项式阶数等?是否有可用的算法/方法可以在没有太多用户输入的情况下检测一般趋势?

如果有比曲线拟合更好的方法,我也愿意接受替代方法。我担心我得到的结果只会和我输入的一样好,因此任何一般的曲线拟合方法都会产生较差的结果。

【问题讨论】:

  • 嗯。关于@james-phillips 的回答,我们可以假设数据是周期性的吗?并且“我可以看到这里有 3 个非常清晰的山峰。”我不得不说不。你会看到两个峰值和一个上升沿,它们可能以峰值或高原结束,甚至发散。您期待第三次高峰,但那是另一回事。
  • @mikuszefski 我将引用原帖:“在这种情况下,水稻农业和农业周期数(3 个峰值)”。此信息似乎与您的评论无关。
  • @JamesPhillips 嗯,“此外,我拥有的峰数可能会改变。”即使没有这个,人们确实期望三个峰值这一事实是一回事,但上面的数据——事实上——并没有显示三个峰值。
  • 幸运的是,我的答案中的模型不依赖于峰值的数量,并且目前似乎普遍适用于手头的问题。
  • @JamesPhillips 对此毫无疑问。我只是在评论“显然是 3 个山峰”。尽管问题仍然存在于周期性数据上。很可能但不一定正确的是,这些峰是等距的,对吧?

标签: python scipy curve-fitting


【解决方案1】:
pip install findpeaks

from findpeaks import findpeaks

X = [-19.9, -19.6, -17.6, -15.9, -19.9, -18.4, -17.7, -16.6, -19.5, -20.4, -17.6, -15.9]

# Initialize
fp = findpeaks(lookahead=1)
# Make the fit
results1 = fp.fit(X)

results1['df']
# x y   labx    valley  peak    labx_topology   valley_topology peak_topology   persistence
#   0   0   -19.9   1.0 True    False   1.0 True    False   
#   1   1   -19.6   1.0 False   False   1.0 False   False   
#   2   2   -17.6   1.0 False   False   1.0 False   False   
#   3   3   -15.9   1.0 False   True    1.0 False   True    
#   4   4   -19.9   1.0 False   False   2.0 True    False   
#   5   5   -18.4   2.0 True    False   2.0 False   False   
#   6   6   -17.7   2.0 False   False   2.0 False   False   
#   7   7   -16.6   2.0 False   True    2.0 False   True    
#   8   8   -19.5   2.0 False   False   2.0 False   False   
#   9   9   -20.4   3.0 True    False   2.0 False   False   
#   10  10  -17.6   3.0 False   False   2.0 False   False   
#   11  11  -15.9   3.0 True    False   2.0 True    False   

# Make plot
fp.plot()

# Initialize
fp = findpeaks(lookahead=1, interpolate=10)
# Make the fit
results2 = fp.fit(X)
# Results
results1['df']
# Make plot
fp.plot()

【讨论】:

    【解决方案2】:

    这是一个图形拟合器,它使用您的数据和一个简单的方程,一个带有偏移量的傅里叶级数 1 项,似乎可以提供自动平滑拟合。

    import numpy, scipy, matplotlib
    import matplotlib.pyplot as plt
    from scipy.optimize import curve_fit
    
    
    xData = numpy.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0])
    yData = numpy.array([-19.9, -19.6, -17.6, -15.9, -19.9, -18.4, -17.7, -16.6, -19.5, -20.4, -17.6, -15.9])
    
    
    # Fourier Series 1 Term (scaled X) from zunzun.com
    def func(x, offset, a1, b1, c1):
        return a1 *numpy.sin(c1 * x) + b1 *numpy.cos(c1 * x) + offset
    
    
    # these are the same as the scipy defaults
    initialParameters = numpy.array([1.0, 1.0, 1.0, 1.0])
    
    # curve fit the test data
    fittedParameters, pcov = curve_fit(func, xData, yData, initialParameters)
    
    modelPredictions = func(xData, *fittedParameters) 
    
    absError = modelPredictions - yData
    
    SE = numpy.square(absError) # squared errors
    MSE = numpy.mean(SE) # mean squared errors
    RMSE = numpy.sqrt(MSE) # Root Mean Squared Error, RMSE
    Rsquared = 1.0 - (numpy.var(absError) / numpy.var(yData))
    
    print('Parameters:', fittedParameters)
    print('RMSE:', RMSE)
    print('R-squared:', Rsquared)
    
    print()
    
    
    ##########################################################
    # graphics output section
    def ModelAndScatterPlot(graphWidth, graphHeight):
        f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)
        axes = f.add_subplot(111)
    
        # first the raw data as a scatter plot
        axes.plot(xData, yData,  'D')
    
        # create data for the fitted equation plot
        xModel = numpy.linspace(min(xData), max(xData))
        yModel = func(xModel, *fittedParameters)
    
        # now the model as a line plot
        axes.plot(xModel, yModel)
    
        axes.set_xlabel('X Data') # X axis data label
        axes.set_ylabel('Y Data') # Y axis data label
    
        plt.show()
        plt.close('all') # clean up after using pyplot
    
    graphWidth = 800
    graphHeight = 600
    ModelAndScatterPlot(graphWidth, graphHeight)
    

    【讨论】:

      猜你喜欢
      • 2019-08-12
      • 1970-01-01
      • 1970-01-01
      • 2014-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多