我只是将线性函数拟合到一些数据,但遇到了在 curve_fit 中将默认初始值设置为 1 的问题。因此,我希望更改初始值,但代码必须非常通用,因为我希望将其应用于不同的 y 变量。因此,我将截距值设置为前 20 个数据点的平均值,因为这应该非常接近最佳答案(我的 x 值偏移到接近 0 的位置)。但我把斜率设为0,因为不同的变量可能有符号关系,也可能没有。
但是,在我的示例中,斜率为零,curve_fit 不会改变该斜率 - 而是更改截距以最适合数据,将其拉离实际位置。当值更改为非零但仍然很小时,这种行为不会改变 - 在示例中 0.01 和 0.001 具有相同的效果。当它不改变参数时,它会给出“无法估计参数的协方差”警告。
数据都是float64,所以与之前讨论的问题没有直接关系这里。
我不确定这是否是最好的分享方式,但我已将数据上传到 DropBox here。下面的代码以及这些数据应该会重现该问题。我的scipy版本是1.11.3.
感谢任何关于为什么会发生这种情况的想法!
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
import pickle
def fit1(x, alpha, beta_t):
yhat = alpha + beta_t*x
return yhat
with open('example_data.pkl', 'rb') as handle:
xs, ys = pickle.load(handle)
plt.plot(xs, ys, color='blue')
for guess in [0, 0.001, 0.01, 0.1, 1, 10]:
init_guess = np.zeros(2)
init_guess[0] = np.mean(ys[:20])
init_guess[1] = guess
params, _ = curve_fit(fit1, xs, ys, p0=init_guess)
plt.plot(xs, fit1(xs, *params), label=f'init_guess[1] = {init_guess[1]}')
plt.legend()
扩展评论中提到的想法:
如果缩放数据以使大多数点都在 1 左右,则可以消除无法估计协方差的错误,并且无论初始猜测是什么,都可以拟合线性回归。
params, _ = curve_fit(fit1, xs, ys/1e9, p0=init_guess)
plt.plot(xs, fit1(xs, *params) * 1e9, label=f'init_guess[1] = {init_guess[1]}')
您还可以使用 StandardScaler 而不是乘以和除以常数。
或者,如果您使用 scipy.stats.linregress(),它可以直接找到解决方案,无需初始猜测或缩放:
res = scipy.stats.linregress(xs, ys)
plt.plot(xs, ys, color='blue')
plt.plot(xs, res.intercept + res.slope*xs)
(注:这是我将xs和ys都按xs排序后的结果。)
对于该数据集,linregress() 也比 curve_fit() 快 20 倍左右,因为 curve_fit() 更灵活。