平滑信号并找到峰值

时间:2019-05-27 21:49:46

标签: python scipy curve-fitting

鉴于我有一个X和Y数组,如下所示:

import Foundation
import UIKit
import SwiftKeychainWrapper
import Firebase
import CoreFoundation
import AVFoundation
import FirebaseDatabase




class homepage:UITableViewController {

override func viewDidAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    if educationCache.count < 0 {
        self.performSegue(withIdentifier: "toFeed", sender: nil)
    }

    }

 override func viewDidLoad() {



  navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Sign Out", style: .plain, target: self, action: #selector(signOut))
   super.viewDidLoad()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}




@objc func signOut (_sender: AnyObject) {


    KeychainWrapper.standard.removeObject(forKey: "uid")
    do {
        try Auth.auth().signOut()
    } catch let signOutError as NSError {
        print ("Error signing out: %@", signOutError)
    }

    dismiss(animated: true, completion: nil)

     }

 }

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

我得到一个类似的情节:

enter image description here

在这里我可以看到3个非常清晰的峰。我可以使用以下数据拟合该数据:

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

enter image description here

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

此外,我拥有的峰数可能会改变。实际上,我的最终目标是将我拥有的数据集归类为可以检测到的峰数。在某些情况下,信号会有更多的噪声。

我研究了scipy.signal.find_peaks(和相关算法),但是它发现了每个峰,而不仅仅是主要峰,尤其是在嘈杂的数据中。我也研究了savgol滤波器和高斯滤波器,并且能够得到结果,但是经常必须指定多项式的阶数等,而阶数可能会随峰数的变化而变化。

是否有一种方法可以使信号平滑以得到峰值数量的近似值,而不必手动指定多项式阶数等?有没有可用的算法/方法可以检测总体趋势而无需过多的用户输入?

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

2 个答案:

答案 0 :(得分:1)

这是使用您的数据和一个简单方程式(带有偏移量的Fourier级数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)

答案 1 :(得分:0)

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()

Find peaks without on raw data

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

Find peaks after line-smoothing