2017-05-27 101 views
4

现在几个小时,我一直试图将模型拟合到(生成的)数据集,作为我一直在努力解决的问题的一个原因。我为函数f(x)= A * cos^n(x)+ b生成了数据点,并添加了一些噪声。当我尝试使用此功能,并curve_fit以适应数据集,我得到的错误Scipy.optimize.curve_fit不适合余弦幂律

./tester.py:10: RuntimeWarning: invalid value encountered in power 
return Amp*(np.cos(x))**n + b 
/usr/lib/python2.7/dist-packages/scipy/optimize/minpack.py:690: OptimizeWarning: Covariance of the parameters could not be estimated category=OptimizeWarning) 

我使用生成的数据点和拟合模型的代码如下:

#!/usr/bin/env python 

from __future__ import print_function 
import numpy as np 
from scipy.optimize import curve_fit 
from matplotlib.pyplot import figure, show, rc, plot 

def f(x, Amp, n, b): 

    return np.real(Amp*(np.cos(x))**n + b) 

x = np.arange(0, 6.28, 0.01) 
randomPart = np.random.rand(len(x))-0.5 
fig = figure() 
sample = f(x, 5, 2, 5)+randomPart 
frame = fig.add_subplot(1,1,1) 

frame.plot(x, sample, label="Sample measurements") 

popt, pcov = curve_fit(f, x, sample, p0=(1,1,1)) 

modeldata = f(x, popt[0], popt[1], popt[2]) 
print(modeldata) 
frame.plot(x, modeldata, label="Best fit") 

frame.legend() 
frame.set_xlabel("x") 
frame.set_ylabel("y") 

show() 

的显示嘈杂的数据 - 请参阅下图。

No fit is possible

请问你们有什么事情的线索?我怀疑这与进入复杂领域的权力法律有关,因为该职能的实际部分是nowhere divergent。我已经试过只返回函数的实际部分,在curve_fit中设置逼真的边界,并且使用numpy数组而不是python列表作为p0。我正在运行最新版本的scipy,scipy 0.17.0-1。

回答

6

的问题如下:

>>> (-2)**1.1 
(-2.0386342710747223-0.6623924280875919j) 
>>> np.array(-2)**1.1 
__main__:1: RuntimeWarning: invalid value encountered in power 
nan 

与天然蟒蛇花车,numpy的双打通常拒绝参加行动导致复杂的结果:

>>> np.sqrt(-1) 
__main__:1: RuntimeWarning: invalid value encountered in sqrt 
nan 

一个快速的解决办法,我建议增加一个np.abs调用你的函数,并使用合适的界限进行拟合,以确保这不会导致伪装。如果你的模型接近真相,你的样本(我的意思是你样本中的余弦值)是正的,那么在它周围增加一个绝对值应该是一个无操作(更新:我意识到情况绝非如此,请看正确的方法下面)。

def f(x, Amp, n, b): 

    return Amp*(np.abs(np.cos(x)))**n + b # only change here 

有了这个小小的改变,我得到这样的:

result is fine

作为参考,从拟合是(4.96482314, 2.03690954, 5.03709923])比较生成与(5,2,5)参数。


发出后多一点想我意识到,余弦将总是为负半域(杜)。所以我建议的解决方法可能会有点问题,或者至少它的正确性不是微不足道的。另一方面,考虑到包含cos(x)^n的原始公式,cos(x)的值为负值,如果n是整数,则这只适用于模型,否则会得到复杂的结果。由于我们无法解决Diophantine拟合问题,因此我们需要正确处理。

最合适的方式(我的意思是偏差数据的可能性最小的方式)是这样的:首先用模型将拟合数据转换为复数,然后将复杂数值输出:

def f(x, Amp, n, b): 

    return Amp*np.abs(np.cos(x.astype(np.complex128))**n) + b 

这显然是比我的解决方法的效率低得多,因为每个配件的步骤,我们创建一个新的网格,无论是在复杂的运算和一个额外的量计算的形式做一些额外的工作。这给了我下面的配合,即使没有设置界限:

improved answer, first part

的参数是(5.02849409, 1.97655728, 4.96529108)。这些也很接近。但是,如果我们把这些数值放回到实际模型中(没有np.abs),我们得到的虚数部分大到-0.37,这并不是很大但是很重要。

所以第二步应该是重新拟合一个合适的模型---一个整数指数。从你的体型中选出一个明显的指数2,然后对这个模型进行一个新的拟合。我不相信任何其他方法会给你一个数学上合理的结果。你也可以从最初的popt开始,希望它确实接近真相。当然,我们可以将原始函数与一些currying一起使用,但使用模型的专用双特定版本要快得多。

from __future__ import print_function 
import numpy as np 
from scipy.optimize import curve_fit 
from matplotlib.pyplot import subplots, show 

def f_aux(x, Amp, n, b): 
    return Amp*np.abs(np.cos(x.astype(np.complex128))**n) + b 

def f_real(x, Amp, n, b): 
    return Amp*np.cos(x)**n + b 


x = np.arange(0, 2*np.pi, 0.01) # pi 
randomPart = np.random.rand(len(x)) - 0.5 
sample = f(x, 5, 2, 5) + randomPart 

fig,(frame_aux,frame) = subplots(ncols=2) 
for fr in frame_aux,frame: 
    fr.plot(x, sample, label="Sample measurements") 
    fr.legend() 
    fr.set_xlabel("x") 
    fr.set_ylabel("y") 

# auxiliary fit for n value 
popt_aux, pcov_aux = curve_fit(f_aux, x, sample, p0=(1,1,1)) 

modeldata = f(x, *popt_aux) 
#print(modeldata) 
print('Auxiliary fit parameters: {}'.format(popt_aux)) 
frame_aux.plot(x, modeldata, label="Auxiliary fit") 

# check visually, test if it's close to an integer, but otherwise 
n = np.round(popt_aux[1]) 

# actual fit with integral exponent 
popt, pcov = curve_fit(lambda x,Amp,b,n=n: f_real(x,Amp,n,b), x, sample, p0=(popt_aux[0],popt_aux[2])) 

modeldata = f(x, popt[0], n, popt[1]) 
#print(modeldata) 
print('Final fit parameters: {}'.format([popt[0],n,popt[1]])) 
frame.plot(x, modeldata, label="Best fit") 

frame_aux.legend() 
frame.legend() 

show() 

请注意,我改变了你的代码中的一些东西,这并不影响我的观点。从上面的图中,这样的一个,同时显示了辅助配合和正确的一个:

final fig

输出:

Auxiliary fit parameters: [ 5.02628994 2.00886409 5.00652371] 
Final fit parameters: [5.0288141074549699, 2.0, 5.0009730316739462] 

只是重申:虽然可能没有视觉差在辅助配合和正确配合之间,只有后者给你的问题提供了有意义的答案。