拟合方法——leastsq

1. 概念:

scipy官网对该方法介绍是: 最小化一组方程的平方和
x = arg ⁡ min ⁡ y ( ∑ ( ( f u n c ( y ) ) 2 , a x i s = 0 ) ) x =\arg \min\limits_{y}(\sum((func(y))^2,axis=0)) x=argymin(((func(y))2axis=0))
简单介绍一下leastsq的参数:
scipy.optimize.leastsq(func,x0,args = (),Dfun = None,full_output = 0,col_deriv = 0,ftol = 1.49012e-08,xtol = 1.49012e-08,gtol = 0.0,maxfev = 0,epsfcn = None,factor = 100,diag = None)

# 参数:
func: 所求平方和最小值的函数,一般都用于拟合时传入残差函数,当然也有其他用途,比如求函数的最小等等
x0: ndarray  一般为参数的初始值
args: 元组,可选,可以传入(x, y)其中x,y都是数组,作为求解传入的参数
# 返回值
leastsq的返回值是一个元组,里面包含了很多信息,如果是用作拟合的话,返回值的索引值为0的元素即为我们所求函数参数

有兴趣的读者可以自己看看官网介绍: leastsq官网

2. 使用:

1. 求最小值

比如求 2 ( x − 3 ) 2 + 1 2(x-3)^2+1 2(x3)2+1的最小值:

from scipy.optimize import leastsq

def func(x):
    return 2*(x-3)**2+1
print(leastsq(func, 0))   # 0为x的初始值,初始值可以由自己定,改成任意一个随机值都可以

结果为:

(array([2.99999999]), 1)

x = 2.99999999 ≈ 3 x=2.99999999\approx3 x=2.999999993时,函数最小值为1

2. 拟合

为了和上一篇文章数学建模方法—【04】拟合方法之np.polyfit、np.poly1d形成一个比较,我们这里也进行1-5阶的多项式拟合。

  1. 定义要拟合的函数关系式 (因为我们这里函数关系式是多项式,所以就用np.ploy1d获取了多项式了)
    from scipy.optimize import leastsq
    # 拟合数据集
    x = [4, 8, 10, 12, 25, 32, 43, 58, 63, 69, 79]
    y = [20, 33, 50, 56, 42, 31, 33, 46, 65, 75, 78]
    x_arr = np.array(x)
    y_arr = np.array(y)
    
    def fitting_func(p, x):
       """
       获得拟合的目标数据数组
       :param p: array[int] 多项式各项从高到低的项的系数数组
       :param x: array[int] 自变量数组
       :return: array[int] 拟合得到的结果数组
       """
       f = np.poly1d(p)    # 获得拟合后得到的多项式
       return f(x)         # 将自变量数组带入多项式计算得到拟合所得的目标数组	
    
    如果想要拟合的是其他函数,那只需要将 f = np.poly1d(p)改为你想要拟合的函数关系式即可。
  2. 定义残差计算函数
    def error_func(p, x, y):
    """
    计算残差
    :param p: array[int] 多项式各项从高到低的项的系数数组
    :param x: array[int] 自变量数组
    :param y: array[int] 原始目标数组(因变量)
    :return: 拟合得到的结果和原始目标的差值
    """
    err = fitting_func(p, x) - y
    return err
    
    残差有很多计算方法,这里因为leastsq方法本身就会将我们的残差函数平方,所以这里我们直接相减,在经过leastsq方法的平方后就变为了最小二乘法了。
  3. 获取参数
    def n_poly(n, x, y):
       """
       n 次多项式拟合函数
       :param n: 多项式的项数(包括常数项),比如n=3的话最高次项为2次项
       :return: 最终得到的系数数组
       """
       p_init = np.random.randn(n)   # 生成 n个随机数,作为各项系数的初始值,用于迭代修正
       parameters = leastsq(error_func, p_init, args=(np.array(x), np.array(y)))    # 三个参数:误差函数、函数参数列表、数据点
       return parameters[0]	# leastsq返回的是一个元组,元组的第一个元素时多项式系数数组[wn、...、w2、w1、w0]
    
  4. 函数定义好后,就可以进行拟合了(这里计算拟合优度的方法goodness_of_fit在我前面的文章有: 数学建模方法—【03】拟合优度的计算(python计算))
    一阶
    para_2 = n_poly(2, x_arr, y_arr)  # 一阶
    print("系数为:", para_2)
    print("多项式为:", np.poly1d(para_2))
    print("拟合值为:", fitting_func(para_2, x_arr))
    print("拟合优度为:", goodness_of_fit(fitting_func(para_2, x_arr), y_arr))
    
    二、三、四、五阶:
    para_3 = n_poly(3, x_arr, y_arr)  # 二阶
    para_4 = n_poly(4, x_arr, y_arr)  # 三阶
    para_5 = n_poly(5, x_arr, y_arr)  # 四阶
    para_6 = n_poly(6, x_arr, y_arr)  # 五阶
    
    这里解释一下为什么一阶传入n=2,二阶传入n=3…。因为这里的n表示的不是多项式的阶数,而是表示多项式的项数,比如二阶的多项式有 x 2 、 x x^2、x x2x还有常数项三项,所以传入的n=3。
  5. 得到拟合的数据后就可以作图了
    figure = plt.figure(figsize=(8,6))
      plt.plot(x_arr, fitting_func(para_2, x_arr), color="#72CD28", label='一阶拟合曲线')
      plt.plot(x_arr, fitting_func(para_3, x_arr), color="#EBBD43", label='二阶拟合曲线')
      plt.plot(x_arr, fitting_func(para_4, x_arr), color="#50BFFB", label='三阶拟合曲线')
      plt.plot(x_arr, fitting_func(para_5, x_arr), color="gold", label='四阶拟合曲线')
      plt.plot(x_arr, fitting_func(para_6, x_arr), color="#13EAC9", label='五阶拟合曲线')
      plt.scatter(x, y, color='#50BFFB', marker="p", label='原始数据')
      
      plt.title("leastsq拟合", fontsize=13)
      plt.xlabel('x', fontsize=13)
      plt.ylabel('y', fontsize=13)
      plt.legend(loc=4, fontsize=11)    # 指定legend的位置右下角
      plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
      plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
      plt.show()
    
    这里我只输出了一阶的拟合结果,结果为:
    系数为: [ 0.5000123  29.77227653]
    多项式为:  
    0.5 x + 29.77
    拟合值为: [31.77232574 33.77237495 34.77239955 35.77242415 42.27258408 45.77267019		 51.27280552 58.77299004 61.27305155 64.27312537 69.27324839]
    拟合优度为: 0.5207874477394239
    
    结果图为:
    数学建模方法 — 【05】 拟合方法之leastsq_python

全部代码为:

from scipy.optimize import leastsq
# 拟合数据集
x = [4, 8, 10, 12, 25, 32, 43, 58, 63, 69, 79]
y = [20, 33, 50, 56, 42, 31, 33, 46, 65, 75, 78]
x_arr = np.array(x)
y_arr = np.array(y)
def fitting_func(p, x):
    """
    获得拟合的目标数据数组
    :param p: array[int] 多项式各项从高到低的项的系数数组
    :param x: array[int] 自变量数组
    :return: array[int] 拟合得到的结果数组
    """
    f = np.poly1d(p)    # 获得拟合后得到的多项式
    return f(x)         # 将自变量数组带入多项式计算得到拟合所得的目标数组

def error_func(p, x, y):
    """
    计算残差
    :param p: array[int] 多项式各项从高到低的项的系数数组
    :param x: array[int] 自变量数组
    :param y: array[int] 原始目标数组(因变量)
    :return: 拟合得到的结果和原始目标的差值
    """
    err = fitting_func(p, x) - y
    return err


def n_poly(n, x, y):
    """
    n 次多项式拟合函数
    :param n: 多项式的项数(包括常数项),比如n=3的话最高次项为2次项
    :return: 最终得到的系数数组
    """
    p_init = np.random.randn(n)   # 生成 n个随机数,作为各项系数的初始值,用于迭代修正
    parameters = leastsq(error_func, p_init, args=(np.array(x), np.array(y)))    # 三个参数:误差函数、函数参数列表、数据点
    return parameters[0]	# leastsq返回的是一个元组,元组的第一个元素时多项式系数数组[wn、...、w2、w1、w0]

para_2 = n_poly(2, x_arr, y_arr)  # 一阶
print("系数为:", para_2)
print("多项式为:", np.poly1d(para_2))
print("拟合值为:", fitting_func(para_2, x_arr))
print("拟合优度为:", goodness_of_fit(fitting_func(para_2, x_arr), y_arr))
para_3 = n_poly(3, x_arr, y_arr)  # 二阶
para_4 = n_poly(4, x_arr, y_arr)  # 三阶
para_5 = n_poly(5, x_arr, y_arr)  # 四阶
para_6 = n_poly(6, x_arr, y_arr)  # 五阶
figure2 = plt.figure(figsize=(8,6))
plt.plot(x_arr, fitting_func(para_2, x_arr), color="#72CD28", label='一阶拟合曲线')
plt.plot(x_arr, fitting_func(para_3, x_arr), color="#EBBD43", label='二阶拟合曲线')
plt.plot(x_arr, fitting_func(para_4, x_arr), color="#50BFFB", label='三阶拟合曲线')
plt.plot(x_arr, fitting_func(para_5, x_arr), color="gold", label='四阶拟合曲线')
plt.plot(x_arr, fitting_func(para_6, x_arr), color="#13EAC9", label='五阶拟合曲线')
plt.scatter(x_arr, y_arr, color='#50BFFB', marker="p", label='原始数据')

plt.title("leastsq拟合", fontsize=13)
plt.xlabel('x', fontsize=13)
plt.ylabel('y', fontsize=13)
plt.legend(loc=4, fontsize=11)    # 指定legend的位置右下角
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
plt.show()