的問題如下:
>>> (-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](https://i.stack.imgur.com/WEQEr.png)
作爲參考,從擬合是(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](https://i.stack.imgur.com/qWZPL.png)
的參數是(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](https://i.stack.imgur.com/q5ioe.png)
輸出:
Auxiliary fit parameters: [ 5.02628994 2.00886409 5.00652371]
Final fit parameters: [5.0288141074549699, 2.0, 5.0009730316739462]
只是重申:雖然可能沒有視覺差在輔助配合和正確配合之間,只有後者給你的問題提供了有意義的答案。