сглаживать сигнал и находить пики - PullRequest
2 голосов
/ 28 мая 2019

Учитывая, что у меня есть массив 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 Ответ

1 голос
/ 28 мая 2019

Вот графическое приспособление, использующее ваши данные и простое уравнение, ряд 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)
...