SciPy leastsq fit to a sine wave failing

雨燕双飞 提交于 2019-12-05 10:56:05

Here is some code implementing some of Zhenya's ideas. It uses

yhat = fftpack.rfft(yReal)
idx = (yhat**2).argmax()
freqs = fftpack.rfftfreq(N, d = (xReal[1]-xReal[0])/(2*pi))
frequency = freqs[idx]

to guess the main frequency of the data, and

amplitude = yReal.max()

to guess the amplitude.


import numpy as np
import scipy.optimize as optimize
import scipy.fftpack as fftpack
import matplotlib.pyplot as plt
pi = np.pi
plt.figure(figsize = (15, 5))

# generate a perfect data set (my real data have tiny error)
def mysine(x, a1, a2, a3):
    return a1 * np.sin(a2 * x + a3)

N = 5000
xmax = 10
xReal = np.linspace(0, xmax, N)
a1 = 200.
a2 = 2*pi/10.5  # omega, 10.5 is the period
a3 = np.deg2rad(10.) # 10 degree phase offset
print(a1, a2, a3)
yReal = mysine(xReal, a1, a2, a3) + 0.2*np.random.normal(size=len(xReal))

yhat = fftpack.rfft(yReal)
idx = (yhat**2).argmax()
freqs = fftpack.rfftfreq(N, d = (xReal[1]-xReal[0])/(2*pi))
frequency = freqs[idx]

amplitude = yReal.max()
guess = [amplitude, frequency, 0.]
print(guess)
(amplitude, frequency, phase), pcov = optimize.curve_fit(
    mysine, xReal, yReal, guess)

period = 2*pi/frequency
print(amplitude, frequency, phase)

xx = xReal
yy = mysine(xx, amplitude, frequency, phase)
# plot the real data
plt.plot(xReal, yReal, 'r', label = 'Real Values')
plt.plot(xx, yy , label = 'fit')
plt.legend(shadow = True, fancybox = True)
plt.show()

yields

(200.0, 0.5983986006837702, 0.17453292519943295)   # (a1, a2, a3)
[199.61981404516041, 0.61575216010359946, 0.0]     # guess
(200.06145097308041, 0.59841420869261097, 0.17487141943703263) # fitted parameters

Notice that by using fft, the guess for the frequency is already pretty close to final fitted parameter.

It seems you do not need to fix any of the parameters. By making the frequency guess closer to the actual value, optimize.curve_fit is able to converge to a reasonable answer.

From what I can see from playing a bit with leastsq (without fancy stuff from the cookbook, just plain direct calls to leastsq --- and by the way, full_output=True is your friend here), is that it's very hard to fit all three of the amplitude, frequency and phase in one go. On the other hand, if I fix the amplitude and fit the frequency and phase, it works; if I fix the frequency and fit the amplitude and phase, it works too.

There is more than one way out here. What might be the simplest one --- if you are sure you only have one sine wave (and this is easy to check with the Fourier transform), then you know the frequency from just the distance between consecutive maxima of your signal. Then fit the two remaining parameters.

If what you have is a mixture of several harmonics, well, again, Fourier transform will tell you that.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!