三次样条插值_深入插值算法(三次样条与埃尔米特)

一、差商

设节点为x0,x1,…,xn

xi,xj的一阶差商:

7894031b6e3411f531cf5e19b1cbfe67.png

xi,xj,xk的二阶差商:

4e55c50da52e5001c517f69c5bc0fe9d.png

x0,x1,…,xk的k阶差商:

c0720861f0ce688a0ea2c392f0531aa6.png

构建差商定义表:

1d19e30bdc664d8ddb83a6f15c346b5d.png

注:这里构建差商是为了方便后续插值函数模型的构建。

import numpy as npimport numpy.linalg as lg#构建牛顿差商矩阵函数'''构建牛顿差商矩阵表x_val:对应x轴的数据;y_val:对应y轴的数据。'''def difference_quotient(x_val, y_val):    assert len(x_val) == len(y_val)     n = len(x_val)                   p = np.zeros((n, n+1))           p[:, 0] = x_val                  p[:, 1] = y_val                  for j in range(2, n+1):              p[j-1: n, j] = (p[j-1: n, j-1] - p[j-2: n-1, j-1]) / (x_val[j-1: n] - x_val[: n+1-j])        q = np.diag(p, k=1)    return p, q

二、三次样条插值

e0bd9442ddcebaa1bdb9ed614209365b.png

eaa90d2834413555ac75c836118c8997.png

f3d1f5a51d4227b4d874370650b12939.png

29801b73d766aa343a3c392b3b9c7fb8.png

f1af2d982cfaa61f657f2a78737b4fc9.png

f4f8b5e4dd7144da96e59f83482dc2a2.png

注:为了解决数据长度差异较大出现的抖动问题,我们通常将数据进行分段。比如0年-1年,1年-10年,>10年。

#构建三次样条插值函数'''构建三次样条插值函数x_val:对应原始x轴的数据;y_val:对应原始y轴的数据;x_interp:对应需要插值的x轴的点。'''    def cubic_spline_interp(x_val, y_val, x_interp):    x_lst = [x_interp] if isinstance(x_interp, (float, int)) else x_interp    x_loc = np.searchsorted(x_val, x_lst)        p, q = difference_quotient(x_val, y_val)  #调用差商函数        n = len(x_val) - 1    h_vec = x_val[1:] - x_val[:-1]    _u_vec = h_vec[-1:] / (h_vec[:-1] + h_vec[1:])    u_vec = _u_vec[1:]    lam_vec = 1 - u_vec        diag_mat = np.diag(u_vec, k=-1) + np.diag([2] * (n-1)) + np.diag(lam_vec, k=1)    d_vec = 6 * p[2:, 3]    m_vec = np.insert(np.append(lg.solve(diag_mat, d_vec), 0), 0, 0)        res_lst = []    for x, i in zip(x_lst, x_loc):        h_i = x_val[i] - x_val[i-1]        S_i = m_vec[i-1] * (x_val[i] - x) ** 3 / (6 * h_i) +\        m_vec[i] * (x - x_val[i-1]) ** 3 / (6 * h_i)+\        (y_val[i-1] - 1/6 * m_vec[i-1] * h_i ** 2) * (x_val[i] - x) / h_i+\        (y_val[i] - 1/6 * m_vec[i] * h_i ** 2) * (x - x_val[i-1]) / h_i        res_lst.append(S_i)            return res_lst

三、埃尔米特插值(中债登债券估值插值函数)

        埃尔米特(Hermite)插值又称带指定微商值的插值,也就是说,除了给出插值节点的函数值之外,还给出了指定插值节点的导数值。Hermite插值在插值节点处取相同的插值函数和导数值。

        可以构造2n+1次的Hermite插值多项式H(x),并且H(x)是次数不超过2n+1次的插值多项式

39d0d82cf47417f05c52c6b383e0b6d3.png

采用插值基函数的办法,构造如下插值函数

d1097906676c1fbe004efaae63e7db32.png

57855aa3488aebf4bca994632e42a293.png

f92773d86d506742567ba2e51e671ef3.png

分段三次Hermite插值是按照分段插值的方法,把区间[a,b]分为若干个区间,在每个区间上使用三次Hermite插值。在各个节点上的插值基函数如下

61391d0654b18d80335fe6bcf6cfd8f0.png

对于一阶导数的问题,我们可以简要用以下式子求解每个节点的一阶导数

08661181cf003df5f215d4aaadc415b6.png

令最后一个节点的导数=0

#埃尔米特插值函数'''构建埃尔米特插值函数x_val:对应原始x轴的数据;y_val:对应原始y轴的数据;x_deriv:对应原始x轴数据的一阶导数;x_interp:对应需要插值的x轴的点。'''        def cubic_hermite_interp(x_val, y_val, x_deriv, x_interp):    x_lst = [x_interp] if isinstance(x_interp, (float, int)) else x_interp    x_loc = np.searchsorted(x_val, x_lst)    res_lst = []    for x, i in zip(x_lst, x_loc):        h_i = x_val[i] - x_val[i-1]        H_i = (1 + 2 * (x - x_val[i-1]) / h_i) * ((x - x_val[i]) / h_i) ** 2 * y_val[i-1] + \              (1 + 2 * (x_val[i] - x) / h_i) * ((x - x_val[i-1]) / h_i) ** 2 * y_val[i] + \              (x - x_val[i-1]) * ((x - x_val[i]) / h_i) ** 2 * x_deriv[i-1] + \              (x - x_val[i]) * ((x - x_val[i-1]) / h_i) ** 2 * x_deriv[i]                res_lst.append(H_i)        return res_lst

【案例分析】我们依旧以上节的国债收益率曲线为例,进行插值计算:

#分段三次埃尔米特插值k=deriv(maturity,ytm)   #端点处导数H1 = cubic_hermite_interp(maturity,ytm,k,matruity_new)   #获得分段三次埃尔米特插值函数#分段三次样条x1=maturity[0:5]  #分段为0-1y1=ytm[0:5] x2=maturity[4:10]  #分段为1-5y2=ytm[4:10]x3=maturity[9:]   #分段>5y3=ytm[9:]x1_=matruity_new[0:5]x2_=matruity_new[4:23]x3_=matruity_new[22:]ytm_new1=cubic_spline_interp(x1,y1,x1_)ytm_new2=cubic_spline_interp(x2,y2,x2_)ytm_new3=cubic_spline_interp(x3,y3,x3_)#画图import matplotlib.pyplot as pltfrom pylab import mplmpl.rcParams['font.sans-serif']=['SimHei']mpl.rcParams['axes.unicode_minus']=Falseplt.figure(figsize=(20,15))plt.plot(maturity,ytm,'b',label=u'线性',lw=1.5)plt.plot(matruity_new,H1,'r',label=u'埃尔米特插值',lw=1.5)#plt.plot(matruity_new,ytm_new,'g',label=u'三次样条插值',lw=1.5)plt.plot(x1_,ytm_new1,'g',label=u'三次样条插值',lw=1.5)plt.plot(x2_,ytm_new2,'g')plt.plot(x3_,ytm_new3,'g')plt.legend(fontsize=20)plt.grid('True')plt.show()

0c89f0f28a67bba72de4343465748b46.png

注:本篇对埃尔米特插值一阶导数的问题进行了简要构建,主要以应用为主,具体想看详细插值函数的可以参考以下内容:

https://blog.csdn.net/qq_36384657/article/details/98386776

2.https://zhuanlan.zhihu.com/p/63763725

附录:欢迎大家加好友,相互交流学习!

c7fe8bf549b4f397252d9baa41b1fcbb.png

所有python案例分析的数据地址:链接:https://pan.baidu.com/s/1ds35rhd5yf-BSmzc-fxvzw提取码:je4a

点击可留言


版权声明:本文为weixin_39969060原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。