作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用 scipy curve_fit 方法来拟合振荡数据。不幸的是我有 8 个参数,并且维度无法减小(或者我没有看到办法)。这是适合的函数:
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
def cosFitForCFF(x,c0, c1, b0, b1, b2, b3, b4, b5):
"""
Function for fit.
c0, c1, b0 irrelevant parameters
b1, b2, b3, b4, b5 are the important parameters
"""
return c0 + c1*np.cos(b0+b1*x+b2*x**2+b3*x**3+b4*x**4+b5*x**5)
前 3 个参数无关紧要,我需要后 5 个参数才能继续计算。
我有一个函数可以读取输入并使用我可以提供的所有选项(初始参数、边界)进行拟合:
def CFFMethod(initSpectrumX, initSpectrumY, referenceArmY, sampleArmY,
p0=[1, 1, 1, 1, 1, 1, 1, 1], referencePoint = 2.5):
"""
Phase modulated cosine function fit method. p0 is the array containing inital parameters for fitting.
referencePoint is some point in initSpectrumX
"""
#best bounds I can provide
bounds=((-1, -1, -1, -np.inf, -np.inf, -np.inf, -np.inf, -np.inf),
(1, 1, 1, np.inf, np.inf, np.inf, np.inf, np.inf))
#reading inputs
if len(initSpectrumY) > 0 and len(referenceArmY) > 0 and len(sampleArmY) > 0:
Ydata = (initSpectrumY-referenceArmY-sampleArmY)/(2*np.sqrt(referenceArmY*sampleArmY))
Ydata = np.asarray(Ydata)
elif len(initSpectrumY) == 0:
raise ValueError('Please load the spectrum!\n')
elif len(referenceArmY) == 0 or len(sampleArmY) == 0:
Ydata = np.asarray(initSpectrumY)
Xdata = initSpectrumX-referencePoint
Xdata = np.asarray(Xdata)
#end of reading inputs
#fitting
try:
popt, pcov = curve_fit(cosFitForCFF, Xdata, Ydata, p0, maxfev = 5000, bounds = bounds)
#plot
fig1 = plt.figure()
fig1.canvas.set_window_title('Cosine function fit method')
plt.plot(Xdata, Ydata,'r-',label = 'dataset')
plt.plot(Xdata, cosFitForCFF(Xdata, *popt),'k*', label = 'fitted')
plt.legend()
plt.grid()
plt.show()
return popt
except Exception as e:
print(e)
即使我使用该函数生成合成数据,我也会得到错误的结果。
xs = np.linspace(2.5, 3, 1000)
ys = cosFitForCFF(xs, 0, 1, 0, 0, 50, 0, 0, 0)
params = [0, 1, 0, 0, 50, 0, 0, 0] #exact same that was generated
reference = 2.7 # some point in the data, irrelevant
result = CFFMethod(xs, ys, [],[], p0 = params, referencePoint = reference)
print(result)
#outputs to:
#[-5.12643151e-01 1.00000000e+00 9.99999995e-01 2.05339754e-01
# 1.01356470e+01 -3.83963354e+01 -3.53998314e+02 1.33074662e+03]
我知道 curve_fit 正在努力处理太多参数,这就是为什么我需要将 maxfev 设置得更高。
这甚至与真实世界的数据集相差甚远,因为现实世界中的数据集可能存在噪音等。
我是不是做错了什么?也许我应该寻找另一种算法?拟合函数必须是我上面定义的函数,因为这样分散系数(我需要找到)与 b1,b2..我真的很感谢对代码的任何帮助/改进。
编辑:
禁用referencePoint后,它完全适合,但仅适用于生成的数据集。拟合真实数据集仍然会导致错误的结果。这是更新后的功能:
def CFFMethod(initSpectrumX, initSpectrumY, referenceArmY, sampleArmY, p0=[1, 1, 1, 1, 1, 1, 1, 1]):
"""
Phase modulated cosine function fit method. p0 is the array containing inital parameters for fitting.
referencePoint is some point in initSpectrumX
"""
#provided bounds
bounds=((-1, -1, -1, -np.inf, -np.inf, -np.inf, -np.inf, -np.inf), (1, 1, 1, np.inf, np.inf, np.inf, np.inf, np.inf))
#reading inputs
if len(initSpectrumY) > 0 and len(referenceArmY) > 0 and len(sampleArmY) > 0:
Ydata = (initSpectrumY-referenceArmY-sampleArmY)/(2*np.sqrt(referenceArmY*sampleArmY))
Ydata = np.asarray(Ydata)
elif len(initSpectrumY) == 0:
raise ValueError('Please load the spectrum!\n')
elif len(referenceArmY) == 0 or len(sampleArmY) == 0:
Ydata = np.asarray(initSpectrumY)
Xdata = np.asarray(initSpectrumX)
#end of reading inputs
#fitting
try:
popt, pcov = curve_fit(cosFitForCFF, Xdata, Ydata, p0, maxfev = 5000, bounds = bounds)
#plot
fig1 = plt.figure()
fig1.canvas.set_window_title('Cosine function fit method')
plt.plot(Xdata, Ydata,'r-',label = 'dataset')
plt.plot(Xdata, cosFitForCFF(Xdata, *popt),'k*', label = 'fitted')
plt.legend()
plt.grid()
plt.show()
return popt
except Exception as e:
print(e)
我在那里提供数据集生成器:
# GENERATOR FUNCTIONS
def _ensureInput(start, stop, center, resolution):
if start >= stop:
raise ValueError('start value must be less than stop')
if center < start or center > stop:
raise ValueError('center must be between start and stop')
if resolution > (stop-start):
raise ValueError('resolution is too big')
else:
pass
def _disp(x ,GD=0, GDD=0, TOD=0, FOD=0, QOD=0):
return x*GD+(GDD/2)*x**2+(TOD/6)*x**3+(FOD/24)*x**4+(QOD/120)*x**5
def generatorFreq(start, stop, center ,delay, GD=0, GDD=0, TOD=0, FOD=0, QOD=0, resolution = 0.1,
delimiter =',',pulseWidth = 0.02, includeArms = False):
_ensureInput(start, stop, center, resolution)
c = 299.793
deltaL = delay
omega0 = center
window = pulseWidth
lamend = (2*np.pi*c)/start
lamstart = (2*np.pi*c)/stop
lam = np.arange(lamstart, lamend+resolution, resolution)
omega = (2*np.pi*c)/lam
relom = omega-omega0
i1 = np.exp(-(relom)**2/(window))
i2 = np.exp(-(relom)**2/(window))
i = i1 + i2 + 2*np.cos(_disp(relom,GD=GD, GDD= GDD, TOD=TOD, FOD=FOD, QOD=QOD)+2*deltaL*omega/c)*np.sqrt(i1*i2)
if includeArms:
return omega, i, i1, i2
else:
return omega, i, np.array([]), np.array([])
## using the generator to make a dataset
a,b,c,d = generatorFreq(2 ,3, 2.5, 0, GD = 0, GDD = 200, TOD = 4000, FOD = 0, QOD = 0, resolution = 0.1, delimiter = ',', pulseWidth = 0.02, includeArms = True)
# fit to data
result = CFFMethod(a, b, c,d , p0 = [0, 1, 0, 0, 200, 4000, 0, 0])
您现在可以看到,通过复制粘贴,curve_fit 无法产生良好的结果。
最佳答案
我有一个建议作为摆脱困境的可能途径。拟合一小部分数据应该比拟合整个数据集更容易 - 当它起作用时,这些参数可以用作更大数据子集的初始参数估计,依此类推。以下是您的代码,修改如下以使用前 50 个数据点:
#fitting
Xdata = Xdata[:50]
Ydata = Ydata[:50]
try:
popt, pcov = curve_fit(cosFitForCFF, Xdata, Ydata, p0, maxfev = 5000, bounds = bounds)
结果如下:
关于python - Scipy curve_fit 因多参数拟合而失败。有没有办法改善结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57638589/
我是一名优秀的程序员,十分优秀!