python - 平滑信号并找到峰值

标签 python scipy curve-fitting

假设我有一个 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])

我得到这样的情节:

enter image description here

这里有 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 个峰值):

enter image description here

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

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

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

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

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

最佳答案

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

plot

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)

关于python - 平滑信号并找到峰值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56332765/

相关文章:

python - Scipy curve_fit 给出了错误的答案

python - 直接处理 Tkinter Entry 输入而不使用 Button?

python - 简单的 Google App Engine 任务队列教程? flask /Python/GAE

python - 卷积层特征图上的特殊函数

python - 使用 `scipy.integrate.odeint`时出错

python - 如何连接两个对象的路径?

python - SmoothBivariateSpline 给出了意想不到的答案

matlab - 具有负指数的 Polyfit

java峰值拟合库

python - PyQt 打开一个带有表格的子窗口(图灵机应用程序)