使用curve_fit估计不同大小数据集上的通用模型参数

问题描述

我正在研究曲线拟合问题,我打算在几个大小不等的数据集上全局估计共享模型参数。我从下面链接中的代码开始工作,其中线性回归 y = a*x + b 的公共 a 参数是在具有公共 x 向量的三个不同 y 向量上估计的。 How to use curve_fit from scipy.optimize with a shared fit parameter across multiple datasets?

我设法使代码示例适应更一般的情况,使用三个不同的 x 向量,一个对应于每个 y 数据向量。但是,当我想进一步扩展它以适用于大小不等的数据集时,我遇到了以下错误:“ValueError:使用序列设置数组元素。”。

请在下面找到代码示例。非常感谢任何帮助!

干杯

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,b_1,b_2,b_3):
     return np.concatenate((f(x[0],b_1),f(x[1],b_2),f(x[2],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,b_i),label=f"{a:.1f}x{b_i:+.1f}")
    plt.plot(x_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,2.3,3.3]]

y = [[-0.80216234,3.81456949,4.25]]

x = np.array(x)
y = np.array(y)


def f(x,b_3):
    return np.concatenate((f(x[0],b):
   plt.plot(x_i,label=f"{a:.1f}x{b_i:+.1f}")
   plt.plot(x_i,color=plt.gca().lines[-1].get_color())

plt.legend()
plt.show()

解决方法

问题是您无法从参差不齐的列表中创建 numpy.array。要重新转换为 np.array,所有维度都必须匹配,即您不能有一个空的 column 条目,因为这对 Numpy 没有意义。

在您的情况下,您还没有为数组中的 之一定义条目(即最后 中的 3 个条目,而其他 em> 有 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,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,y,b):
    plt.plot(x_i,f(x_i,b_i),label=f"{a:.1f}x{b_i:+.1f}")
    plt.plot(x_i,linestyle="",marker="x",color=plt.gca().lines[-1].get_color())

plt.legend()
plt.show()

fit resutls