使用 curve_fit 估计不同大小数据集的通用模型参数
Using curve_fit to estimate common model parameters over datasets with different sizes
我正在研究曲线拟合问题,我打算在多个大小不等的数据集上全局估计共享模型参数。我从下面 link 中的代码开始工作,其中线性回归 y = a*x + b 的公共 a 参数是在具有公共 x 向量的三个不同 y 向量上估计的。
我设法使代码示例适应更一般的情况,具有三个不同的 x 向量,一个对应于每个 y 数据向量。但是,当我想进一步扩展它以使其也适用于大小不等的数据集时,我 运行 出现以下错误:“ValueError: setting an array element with a sequence.”.
请在下面找到代码示例。非常感谢任何帮助!
干杯
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
x = [[0, 1, 2, 3],
[0.2, 1.2, 2.2, 3.2],
[0.3, 1.3, 2.3]]
y = [[-0.80216234, 1.41125365, 1.42565202, 2.42567754],
[ 1.34166743, 1.29731851, 2.98374731, 3.32110875],
[ 1.71398203, 3.29737756, 3.81456949]]
x = np.array(x)
y = np.array(y)
def f(x, a, b):
return a * x + b
def g(x, a, b_1, b_2, b_3):
return np.concatenate((f(x[0], a, b_1), f(x[1], a, b_2), f(x[2], a, b_3)))
(a, *b), _ = curve_fit(g, x, y.ravel())
for x_i, y_i, b_i in zip(x, y, b):
plt.plot(x_i, f(x_i, a, b_i), label=f"{a:.1f}x{b_i:+.1f}")
plt.plot(x_i, y_i, linestyle="", marker="x", color=plt.gca().lines[-1].get_color())
plt.legend()
plt.show()
有关具有多个大小相等的 x 向量的工作示例的代码,请参见下文:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
x = [[0, 1, 2, 3],
[0.2, 1.2, 2.2, 3.2],
[0.3, 1.3, 2.3, 3.3]]
y = [[-0.80216234, 1.41125365, 1.42565202, 2.42567754],
[ 1.34166743, 1.29731851, 2.98374731, 3.32110875],
[ 1.71398203, 3.29737756, 3.81456949, 4.25]]
x = np.array(x)
y = np.array(y)
def f(x, a, b):
return a * x + b
def g(x, a, b_1, b_2, b_3):
return np.concatenate((f(x[0], a, b_1), f(x[1], a, b_2), f(x[2], a, b_3)))
(a, *b), _ = curve_fit(g, x, y.ravel())
for x_i, y_i, b_i in zip(x, y, b):
plt.plot(x_i, f(x_i, a, b_i), label=f"{a:.1f}x{b_i:+.1f}")
plt.plot(x_i, y_i, linestyle="", marker="x", color=plt.gca().lines[-1].get_color())
plt.legend()
plt.show()
问题是您无法从参差不齐的列表中创建 numpy.array
。要重铸为 np.array
所有维度必须匹配,即你不能有一个空的 列 条目,因为这对 Numpy 没有意义。
在您的情况下,您还没有为数组中的 列 之一定义条目(即最后 行 中的 3 个条目,当其他 行 有 4 个条目)。 Numpy 根本不会让你这样做,因为它是一个数值包,它依赖于矩形明确定义的数组来进行计算。
总的来说,我同意最小二乘拟合的含义相当复杂……一些快速的想法:
- 如果从不同长度的数据集估计得到的
b
参数,你能确定它们同样有效吗?
- 你得到的 b 参数越多,他们的估计就越不确定,因为你只优化了组合的拟合性能,而不是每个单独的拟合性能
- 我也不确定雅可比的数值评估在这种情况下的效果如何......可能值得实施一个自定义
jac
函数以精确的方式评估雅可比
- ... anad 我确定还有更多我目前不知道的问题 :D
尽管如此,你当然可以欺骗scipy.optimize
做你想做的...
但是,您必须更进一步,直接使用 scipy.optimize.least_squares
而不是使用更高级别的 scipy.optimize.curve_fit
函数。
通过这种方式,您可以改变残差的计算方式以接受不同长度的数据集。
...这是其工作原理的快速实现:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import least_squares
x = [[0.0, 1.0, 2.0, 3.0, 4.0, 5.0],
[0.2, 1.2, 2.2, 3.2],
[0.3, 1.3, 2.3 ]]
y = [[-0.80216234, 1.41125365, 1.42565202, 2.42567754, 3, 4],
[ 1.34166743, 1.29731851, 2.98374731, 3.32110875],
[ 1.71398203, 3.29737756, 3.81456949 ]]
def f(x, a, b):
return a * x + b
def fun(parameters):
# separate a and b parameters
a, *b = parameters
# calculate function-results based on a shared a- and variable b- parameters
res = (f(xi, a, bi) for (xi, bi) in zip(map(np.array, x), b))
# calculate the residuals
errs = []
for i, j in zip(res, map(np.array, y)):
errs += (i - j).tolist()
return np.array(errs)
# set start-values
start_values = (1, 1, 2, 3)
# do the fit
a, *b = least_squares(fun, start_values).x
for x_i, y_i, b_i in zip(map(np.array, x), y, b):
plt.plot(x_i, f(x_i, a, b_i), label=f"{a:.1f}x{b_i:+.1f}")
plt.plot(x_i, y_i, linestyle="", marker="x", color=plt.gca().lines[-1].get_color())
plt.legend()
plt.show()
我正在研究曲线拟合问题,我打算在多个大小不等的数据集上全局估计共享模型参数。我从下面 link 中的代码开始工作,其中线性回归 y = a*x + b 的公共 a 参数是在具有公共 x 向量的三个不同 y 向量上估计的。
我设法使代码示例适应更一般的情况,具有三个不同的 x 向量,一个对应于每个 y 数据向量。但是,当我想进一步扩展它以使其也适用于大小不等的数据集时,我 运行 出现以下错误:“ValueError: setting an array element with a sequence.”.
请在下面找到代码示例。非常感谢任何帮助!
干杯
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
x = [[0, 1, 2, 3],
[0.2, 1.2, 2.2, 3.2],
[0.3, 1.3, 2.3]]
y = [[-0.80216234, 1.41125365, 1.42565202, 2.42567754],
[ 1.34166743, 1.29731851, 2.98374731, 3.32110875],
[ 1.71398203, 3.29737756, 3.81456949]]
x = np.array(x)
y = np.array(y)
def f(x, a, b):
return a * x + b
def g(x, a, b_1, b_2, b_3):
return np.concatenate((f(x[0], a, b_1), f(x[1], a, b_2), f(x[2], a, b_3)))
(a, *b), _ = curve_fit(g, x, y.ravel())
for x_i, y_i, b_i in zip(x, y, b):
plt.plot(x_i, f(x_i, a, b_i), label=f"{a:.1f}x{b_i:+.1f}")
plt.plot(x_i, y_i, linestyle="", marker="x", color=plt.gca().lines[-1].get_color())
plt.legend()
plt.show()
有关具有多个大小相等的 x 向量的工作示例的代码,请参见下文:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
x = [[0, 1, 2, 3],
[0.2, 1.2, 2.2, 3.2],
[0.3, 1.3, 2.3, 3.3]]
y = [[-0.80216234, 1.41125365, 1.42565202, 2.42567754],
[ 1.34166743, 1.29731851, 2.98374731, 3.32110875],
[ 1.71398203, 3.29737756, 3.81456949, 4.25]]
x = np.array(x)
y = np.array(y)
def f(x, a, b):
return a * x + b
def g(x, a, b_1, b_2, b_3):
return np.concatenate((f(x[0], a, b_1), f(x[1], a, b_2), f(x[2], a, b_3)))
(a, *b), _ = curve_fit(g, x, y.ravel())
for x_i, y_i, b_i in zip(x, y, b):
plt.plot(x_i, f(x_i, a, b_i), label=f"{a:.1f}x{b_i:+.1f}")
plt.plot(x_i, y_i, linestyle="", marker="x", color=plt.gca().lines[-1].get_color())
plt.legend()
plt.show()
问题是您无法从参差不齐的列表中创建 numpy.array
。要重铸为 np.array
所有维度必须匹配,即你不能有一个空的 列 条目,因为这对 Numpy 没有意义。
在您的情况下,您还没有为数组中的 列 之一定义条目(即最后 行 中的 3 个条目,当其他 行 有 4 个条目)。 Numpy 根本不会让你这样做,因为它是一个数值包,它依赖于矩形明确定义的数组来进行计算。
总的来说,我同意最小二乘拟合的含义相当复杂……一些快速的想法:
- 如果从不同长度的数据集估计得到的
b
参数,你能确定它们同样有效吗? - 你得到的 b 参数越多,他们的估计就越不确定,因为你只优化了组合的拟合性能,而不是每个单独的拟合性能
- 我也不确定雅可比的数值评估在这种情况下的效果如何......可能值得实施一个自定义
jac
函数以精确的方式评估雅可比 - ... anad 我确定还有更多我目前不知道的问题 :D
尽管如此,你当然可以欺骗scipy.optimize
做你想做的...
但是,您必须更进一步,直接使用 scipy.optimize.least_squares
而不是使用更高级别的 scipy.optimize.curve_fit
函数。
通过这种方式,您可以改变残差的计算方式以接受不同长度的数据集。
...这是其工作原理的快速实现:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import least_squares
x = [[0.0, 1.0, 2.0, 3.0, 4.0, 5.0],
[0.2, 1.2, 2.2, 3.2],
[0.3, 1.3, 2.3 ]]
y = [[-0.80216234, 1.41125365, 1.42565202, 2.42567754, 3, 4],
[ 1.34166743, 1.29731851, 2.98374731, 3.32110875],
[ 1.71398203, 3.29737756, 3.81456949 ]]
def f(x, a, b):
return a * x + b
def fun(parameters):
# separate a and b parameters
a, *b = parameters
# calculate function-results based on a shared a- and variable b- parameters
res = (f(xi, a, bi) for (xi, bi) in zip(map(np.array, x), b))
# calculate the residuals
errs = []
for i, j in zip(res, map(np.array, y)):
errs += (i - j).tolist()
return np.array(errs)
# set start-values
start_values = (1, 1, 2, 3)
# do the fit
a, *b = least_squares(fun, start_values).x
for x_i, y_i, b_i in zip(map(np.array, x), y, b):
plt.plot(x_i, f(x_i, a, b_i), label=f"{a:.1f}x{b_i:+.1f}")
plt.plot(x_i, y_i, linestyle="", marker="x", color=plt.gca().lines[-1].get_color())
plt.legend()
plt.show()