【问题标题】:Getting spline equation from UnivariateSpline object从 UnivariateSpline 对象获取样条方程
【发布时间】:2014-04-24 16:20:57
【问题描述】:

我正在使用 UnivariateSpline 为我拥有的一些数据构造分段多项式。然后我想在其他程序中使用这些样条线(在 C 或 FORTRAN 中),所以我想了解生成的样条线背后的方程式。

这是我的代码:

import numpy as np
import scipy as sp
from  scipy.interpolate import UnivariateSpline
import matplotlib.pyplot as plt
import bisect

data = np.loadtxt('test_C12H26.dat')
Tmid = 800.0
print "Tmid", Tmid
nmid = bisect.bisect(data[:,0],Tmid)
fig = plt.figure()
plt.plot(data[:,0], data[:,7],ls='',marker='o',markevery=20)
npts = len(data[:,0])
#print "npts", npts
w = np.ones(npts)
w[0] = 100
w[nmid] = 100
w[npts-1] = 100
spline1 = UnivariateSpline(data[:nmid,0],data[:nmid,7],s=1,w=w[:nmid])
coeffs = spline1.get_coeffs()
print coeffs
print spline1.get_knots()
print spline1.get_residual()
print coeffs[0] + coeffs[1] * (data[0,0] - data[0,0]) \
                + coeffs[2] * (data[0,0] - data[0,0])**2 \
                + coeffs[3] * (data[0,0] - data[0,0])**3, \
      data[0,7]
print coeffs[0] + coeffs[1] * (data[nmid,0] - data[0,0]) \
                + coeffs[2] * (data[nmid,0] - data[0,0])**2 \
                + coeffs[3] * (data[nmid,0] - data[0,0])**3, \
      data[nmid,7]

print Tmid,data[-1,0]
spline2 = UnivariateSpline(data[nmid-1:,0],data[nmid-1:,7],s=1,w=w[nmid-1:])
print spline2.get_coeffs()
print spline2.get_knots()
print spline2.get_residual()
plt.plot(data[:,0],spline1(data[:,0]))
plt.plot(data[:,0],spline2(data[:,0]))
plt.savefig('test.png')

这是结果图。我相信我对每个间隔都有有效的样条曲线,但看起来我的样条曲线方程不正确......我在 scipy 文档中找不到任何关于它应该是什么的参考。有人知道吗?谢谢!

【问题讨论】:

    标签: python scipy spline


    【解决方案1】:

    get_coeffs 给出的系数是 B-spline (Basis spline) 系数,此处描述:B-spline (Wikipedia)

    可能您将使用的任何其他程序/语言都有实现。提供节点位置和系数,您应该已经准备好了。

    【讨论】:

      【解决方案2】:

      scipy documentation 没有说明如何获取系数并手动生成样条曲线。但是,可以从现有的关于 B 样条的文献中弄清楚如何做到这一点。下面的函数bspleval展示了如何构造B样条基函数(代码中的矩阵B),通过将系数与最高阶基函数相乘并求和,可以轻松生成样条曲线:

      def bspleval(x, knots, coeffs, order, debug=False):
          '''
          Evaluate a B-spline at a set of points.
      
          Parameters
          ----------
          x : list or ndarray
              The set of points at which to evaluate the spline.
          knots : list or ndarray
              The set of knots used to define the spline.
          coeffs : list of ndarray
              The set of spline coefficients.
          order : int
              The order of the spline.
      
          Returns
          -------
          y : ndarray
              The value of the spline at each point in x.
          '''
      
          k = order
          t = knots
          m = alen(t)
          npts = alen(x)
          B = zeros((m-1,k+1,npts))
      
          if debug:
              print('k=%i, m=%i, npts=%i' % (k, m, npts))
              print('t=', t)
              print('coeffs=', coeffs)
      
          ## Create the zero-order B-spline basis functions.
          for i in range(m-1):
              B[i,0,:] = float64(logical_and(x >= t[i], x < t[i+1]))
      
          if (k == 0):
              B[m-2,0,-1] = 1.0
      
          ## Next iteratively define the higher-order basis functions, working from lower order to higher.
          for j in range(1,k+1):
              for i in range(m-j-1):
                  if (t[i+j] - t[i] == 0.0):
                      first_term = 0.0
                  else:
                      first_term = ((x - t[i]) / (t[i+j] - t[i])) * B[i,j-1,:]
      
                  if (t[i+j+1] - t[i+1] == 0.0):
                      second_term = 0.0
                  else:
                      second_term = ((t[i+j+1] - x) / (t[i+j+1] - t[i+1])) * B[i+1,j-1,:]
      
                  B[i,j,:] = first_term + second_term
              B[m-j-2,j,-1] = 1.0
      
          if debug:
              plt.figure()
              for i in range(m-1):
                  plt.plot(x, B[i,k,:])
              plt.title('B-spline basis functions')
      
          ## Evaluate the spline by multiplying the coefficients with the highest-order basis functions.
          y = zeros(npts)
          for i in range(m-k-1):
              y += coeffs[i] * B[i,k,:]
      
          if debug:
              plt.figure()
              plt.plot(x, y)
              plt.title('spline curve')
              plt.show()
      
          return(y)
      

      为了举例说明如何将其与 Scipy 现有的单变量样条函数一起使用,以下是一个示例脚本。这需要输入数据并使用 Scipy 的函数以及其面向对象的样条拟合方法。从两者中的任何一个中获取系数和节点并将它们用作我们手动计算的例程bspleval 的输入,我们重现了与它们相同的曲线。请注意,手动评估的曲线与 Scipy 的评估方法之间的差异非常小,几乎可以肯定是浮点噪声。

      x = array([-273.0, -176.4, -79.8, 16.9, 113.5, 210.1, 306.8, 403.4, 500.0])
      y = array([2.25927498e-53, 2.56028619e-03, 8.64512988e-01, 6.27456769e+00, 1.73894734e+01,
              3.29052124e+01, 5.14612316e+01, 7.20531200e+01, 9.40718450e+01])
      
      x_nodes = array([-273.0, -263.5, -234.8, -187.1, -120.3, -34.4, 70.6, 194.6, 337.8, 500.0])
      y_nodes = array([2.25927498e-53, 3.83520726e-46, 8.46685318e-11, 6.10568083e-04, 1.82380809e-01,
                      2.66344008e+00, 1.18164677e+01, 3.01811501e+01, 5.78812583e+01, 9.40718450e+01])
      
      ## Now get scipy's spline fit.
      k = 3
      tck = splrep(x_nodes, y_nodes, k=k, s=0)
      knots = tck[0]
      coeffs = tck[1]
      print('knot points=', knots)
      print('coefficients=', coeffs)
      
      ## Now try scipy's object-oriented version. The result is exactly the same as "tck": the knots are the
      ## same and the coeffs are the same, they are just queried in a different way.
      uspline = UnivariateSpline(x_nodes, y_nodes, s=0)
      uspline_knots = uspline.get_knots()
      uspline_coeffs = uspline.get_coeffs()
      
      ## Here are scipy's native spline evaluation methods. Again, "ytck" and "y_uspline" are exactly equal.
      ytck = splev(x, tck)
      y_uspline = uspline(x)
      y_knots = uspline(knots)
      
      ## Now let's try our manually-calculated evaluation function.
      y_eval = bspleval(x, knots, coeffs, k, debug=False)
      
      plt.plot(x, ytck, label='tck')
      plt.plot(x, y_uspline, label='uspline')
      plt.plot(x, y_eval, label='manual')
      
      ## Next plot the knots and nodes.
      plt.plot(x_nodes, y_nodes, 'ko', markersize=7, label='input nodes')            ## nodes
      plt.plot(knots, y_knots, 'mo', markersize=5, label='tck knots')    ## knots
      plt.xlim((-300.0,530.0))
      plt.legend(loc='best', prop={'size':14})
      
      plt.figure()
      plt.title('difference')
      plt.plot(x, ytck-y_uspline, label='tck-uspl')
      plt.plot(x, ytck-y_eval, label='tck-manual')
      plt.legend(loc='best', prop={'size':14})
      
      plt.show()
      

      【讨论】:

      • 干得好,但是,以下示例似乎失败了:x = np.linspace(0, 100, 1000)d = np.sin(x * 0.5) + 2 + np.cos(x * 0.1)x_sample = x[::50]d_sample = d[::50]s = UnivariateSpline(x_sample, d_sample, k=3, s=0.005)spl = bspleval(x, s.get_knots(), s.get_coeffs(), 3, debug=False)。结果图显示dspl 看起来完全不同。有什么想法吗?
      • @Cleb:我可以在您的示例中看到一些事情。首先,您使用x_sampled_sample 作为样条拟合的输入,但您将其与xd 进行比较。如果你让x_sample = xd_sample=d,那么你会得到更合适的。但是还是有错误。接下来,如果您将平滑因子从 s=0.005 减小到更小的值(例如 s=1.0e-15),那么您将得到一个很好的拟合。
      • 感谢您的回复;稍后会检查它。如果您根据此处的答案有this question 的解决方案,请随时添加:)
      猜你喜欢
      • 1970-01-01
      • 2013-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-02
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      相关资源
      最近更新 更多