【问题标题】:SciPy leastsq fit to a sine wave failingSciPy leastsq 适合正弦波失败
【发布时间】:2012-11-16 06:21:56
【问题描述】:

我想弄清楚我在这里不明白的是什么。

我正在关注http://www.scipy.org/Cookbook/FittingData 并尝试拟合正弦波。真正的问题是卫星磁力计数据,它在旋转的航天器上产生很好的正弦波。我创建了一个数据集,然后尝试对其进行拟合以恢复输入。

这是我的代码:

import numpy as np
from scipy import optimize

from scipy.optimize import curve_fit, leastsq

import matplotlib.pyplot as plt


class Parameter:
    def __init__(self, value):
            self.value = value

    def set(self, value):
            self.value = value

    def __call__(self):
            return self.value

def fit(function, parameters, y, x = None):
    def f(params):
        i = 0
        for p in parameters:
            p.set(params[i])
            i += 1
        return y - function(x)

    if x is None: x = np.arange(y.shape[0])
    p = [param() for param in parameters]
    return optimize.leastsq(f, p, full_output=True, ftol=1e-6, xtol=1e-6)

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

xReal = np.arange(500)/10.
a1 = 200.
a2 = 2*np.pi/10.5  # omega, 10.5 is the period
a3 = np.deg2rad(10.) # 10 degree phase offset
yReal = mysine(xReal, a1, a2, a3)

# plot the real data
plt.figure(figsize=(15,5))
plt.plot(xReal, yReal, 'r', label='Real Values')

# giving initial parameters
amplitude = Parameter(175.)
frequency = Parameter(2*np.pi/8.)
phase = Parameter(0.0)

# define your function:
def f(x): return amplitude() * np.sin(frequency() * x + phase())

# fit! (given that data is an array with the data to fit)
out = fit(f, [amplitude, frequency, phase], yReal, xReal)
period = 2*np.pi/frequency()
print amplitude(), period, np.rad2deg(phase())

xx = np.linspace(0, np.max(xReal), 50)
plt.plot( xx, f(xx) , label='fit')
plt.legend(shadow=True, fancybox=True)

这使得这个情节:

[44.2434221897 8.094832581 -61.6204033699] 的恢复拟合参数与我开始使用的不相似。

对我不理解或做错了什么有什么想法?

scipy.__version__
'0.10.1'

编辑: 建议修复一个参数。在上面的示例中,将幅度固定为np.histogram(yReal)[1][-1] 仍然会产生不可接受的输出。 Fits:[175.0 8.31681375217 6.0]我应该尝试不同的拟合方法吗?有什么建议?

【问题讨论】:

    标签: python scipy curve-fitting


    【解决方案1】:

    这里有一些代码实现了Zhenya 的一些想法。 它使用

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

    猜测幅度。


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

    产量

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

    请注意,通过使用 fft,对频率的猜测已经非常接近最终拟合参数。

    您似乎不需要修复任何参数。 通过使频率猜测更接近实际值,optimize.curve_fit 能够收敛到一个合理的答案。

    【讨论】:

    • 在这种情况下,更好的猜测似乎是一件非常聪明的事情。谢谢你的想法。
    【解决方案2】:

    从我与leastsq 玩一点时所看到的(没有食谱中的花哨的东西,只是直接直接调用leastsq --- 顺便说一句,full_output=True 是你的朋友),是很难一次性拟合所有三个幅度、频率和相位。另一方面,如果我固定幅度并拟合频率和相位,它就可以工作;如果我固定频率并拟合幅度和相位,它也可以工作。

    这里有不止一种方法。什么可能是最简单的——如果你确定你只有一个正弦波(这很容易用傅里叶变换检查),那么你就可以从信号的连续最大值之间的距离知道频率。然后拟合剩下的两个参数。

    如果您所拥有的是多个谐波的混合体,那么傅里叶变换会再次告诉您这一点。

    【讨论】:

    • 谢谢,我确信我只有一个正弦波,但它会及时改变幅度和频率。我将通过从数据中获取高值来了解如何修复幅度。
    • 我会固定频率,而不是幅度。或者,将所有内容都转换为傅立叶空间并在频域中进行所有拟合。
    • 是的,这里的问题是航天器在进入日食时会改变频率,我只关心频率和相位而不是幅度,这样看起来更自然。
    • 好吧,如果频率在时间上不是恒定的,那它就不是一个单一的正弦波,你无法将它与一个正弦波相匹配。因此,在频域工作看起来更有希望。
    • 我会看看那个。时期变化缓慢,因此我认为我可以适应细分市场。我会看看频域方法,我在那里不太舒服,所以试图避免这种情况。
    猜你喜欢
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 2011-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多