我有以下Python(v2.7.14)代码,它使用了SciPy(v1.0.1)中的curve_fit查找指数衰减函数的参数。大多数时候,我会得到合理的结果。有时候,即使找到的参数相对于原始图形绘制时看起来会很好,我也会得到一些超出预期范围的结果。
首先,我对指数衰减公式的理解来自https://en.wikipedia.org/wiki/Exponential_decay,我将其翻译为Python:
y = a * numpy.exp(-b * x) + c
位置:
该脚本考虑到正在拟合非负数据,并适当地抵消了初始猜测。但是,即使没有猜测,也没有使用最大/最小值(而不是第一个/最后一个值)和我尝试过的其他随机方法,我似乎也无法获得curve_fit在麻烦的数据集上产生有意义的值。
我的假设是,麻烦的数据集没有足够的曲线就可以拟合,而不会超出数据范围。我查看了curve_fit的bounds参数,并认为这可能是一个合理的选择。我不确定什么将使计算的上下限更好,或者实际上是否是我正在寻找的选项。
这是代码。注释掉的代码是我尝试过的事情。
#!/usr/local/bin/python
import numpy as numpy
from scipy.optimize import curve_fit
import matplotlib.pyplot as pyplot
def exponential_decay(x, a, b, c):
return a * numpy.exp(-b * x) + c
def fit_exponential(decay_data, time_data, decay_time):
# The start of the curve is offset by the last point, so subtract
guess_a = decay_data[0] - decay_data[-1]
#guess_a = max(decay_data) - min(decay_data)
# The time that it takes for the signal to reach 1/e becomes guess_b
guess_b = 1/decay_time
# Since this is non-negative data, above 0, we use the last data point as the baseline (c)
guess_c = decay_data[-1]
#guess_c = min(decay_data)
guess=[guess_a, guess_b, guess_c]
print "guess: {0}".format(guess)
#popt, pcov = curve_fit(exponential_decay, time_data, decay_data, maxfev=20000)
popt, pcov = curve_fit(exponential_decay, time_data, decay_data, p0=guess, maxfev=20000)
#bound_lower = [0.05, 0.05, 0.05]
#bound_upper = [decay_data[0]*2, guess_b * 10, decay_data[-1]]
#print "bound_lower: {0}".format(bound_lower)
#print "bound_upper: {0}".format(bound_upper)
#popt, pcov = curve_fit(exponential_decay, time_data, decay_data, p0=guess, bounds=[bound_lower, bound_upper], maxfev=20000)
a, b, c = popt
print "a: {0}".format(a)
print "b: {0}".format(b)
print "c: {0}".format(c)
plot_fit = exponential_decay(time_data, a, b, c)
pyplot.plot(time_data, decay_data, 'g', label='Data')
pyplot.plot(time_data, plot_fit, 'r', label='Fit')
pyplot.legend()
pyplot.show()
print "Gives reasonable results"
time_data = numpy.array([0.0,0.040000000000000036,0.08100000000000018,0.12200000000000011,0.16200000000000014,0.20300000000000007,0.2430000000000001,0.28400000000000003,0.32400000000000007,0.365,0.405,0.44599999999999995,0.486,0.5269999999999999,0.567,0.6079999999999999,0.6490000000000002,0.6889999999999998,0.7300000000000002,0.7700000000000002,0.8110000000000002,0.8510000000000002,0.8920000000000001,0.9320000000000002,0.9730000000000001])
decay_data = numpy.array([1.342146870531986,1.405586070225509,1.3439802492549762,1.3567811728250267,1.2666276377825874,1.1686375326985337,1.216119360088685,1.2022841507836042,1.1926979408026064,1.1544395213303447,1.1904416926531907,1.1054720201415882,1.112100683833435,1.0811434035632939,1.1221671794680403,1.0673295063196415,1.0036146509494743,0.9984005680821595,1.0134498134883763,0.9996920772051201,0.929782730581616,0.9646581154122312,0.9290690593684447,0.8907360533169936,0.9121560047238627])
fit_exponential(decay_data, time_data, 0.567)
print
print "Gives results that are way outside my expectations"
time_data = numpy.array([0.0,0.040000000000000036,0.08099999999999996,0.121,0.16199999999999992,0.20199999999999996,0.24300000000000033,0.28300000000000036,0.32399999999999984,0.3650000000000002,0.40500000000000025,0.44599999999999973,0.48599999999999977,0.5270000000000001,0.5670000000000002,0.6079999999999997,0.6479999999999997,0.6890000000000001,0.7290000000000001,0.7700000000000005,0.8100000000000005,0.851,0.8920000000000003,0.9320000000000004,0.9729999999999999,1.013,1.0540000000000003])
decay_data = numpy.array([1.4401611921948776,1.3720688158534153,1.3793465463227048,1.2939909686762128,1.3376345321949346,1.3352710161631154,1.3413634841956348,1.248705138603995,1.2914294791901497,1.2581763134585313,1.246975264018646,1.2006447776495062,1.188232179689515,1.1032789127515186,1.163294324147017,1.1686263160765304,1.1434009568472243,1.0511578409946472,1.0814520440570896,1.1035953824496334,1.0626893599266163,1.0645580326776076,0.994855722989818,0.9959891485338087,0.9394584009825916,0.949504060086646,0.9278639431146273])
fit_exponential(decay_data, time_data, 0.6890000000000001)
这是文本输出:
Gives reasonable results
guess: [0.4299908658081232, 1.7636684303350971, 0.9121560047238627]
a: 1.10498934435
b: 0.583046565885
c: 0.274503681044
Gives results that are way outside my expectations
guess: [0.5122972490802503, 1.4513788098693758, 0.9278639431146273]
a: 742.824622191
b: 0.000606308344957
c: -741.41398516
最值得注意的是,在第二组结果中, a 的值非常高, c 的值在负数范围内同样较低,而< strong> b 是一个非常小的十进制数字。
这是第一个数据集的图形,它给出了合理的结果。
这是第二个数据集的图形,效果不佳。
请注意,尽管直线实际上没有很好的曲线,但图形本身还是可以正确绘制。
我的问题:
再次,谢谢!
答案 0 :(得分:0)
当您说第二个拟合所提供的结果超出您的期望时,并且尽管第二个图形“正确绘制”,但该线并没有真正“具有良好的曲线拟合”,您在正确的轨道上了解发生了什么。我认为您只是迷失了一部分。
第二张图 与看起来确实是线性的曲线非常吻合。这可能意味着您实际上没有足够多的数据变化(很可能低于噪声水平)来检测它是指数衰减。
我敢打赌,如果您不仅打印出最佳拟合值,而且还打印出变量的不确定性和相关性,您会发现不确定性很大,并且某些相关性非常接近1。这可能意味着考虑到不确定性(并且测量结果总是存在不确定性),结果实际上可能符合您的期望。这也可能告诉您,您拥有的数据不能很好地支持指数衰减。
您还可以为此数据尝试其他模型(想到“线性”;),然后比较拟合优度统计数据,例如卡方和Akaike信息准则。
scipy.curve_fit
确实返回协方差矩阵-您在示例中未使用的pcov
。不幸的是,scipy.curve_fit
不会将这些值转换为不确定性和相关性值,并且根本不会尝试返回任何拟合优度统计信息。
要完全说明对数据的拟合,您不仅需要最佳拟合值,还需要估计可变参数的不确定性。而且,您需要拟合优度统计信息才能确定拟合是否良好,或者至少确定一个拟合优于另一个拟合。