How to perform cubic spline interpolation in python?

前端 未结 4 862
名媛妹妹
名媛妹妹 2021-01-30 11:44

I have two lists to describe the function y(x):

x = [0,1,2,3,4,5]
y = [12,14,22,39,58,77]

I would like to perform cubic spline interpolation so

相关标签:
4条回答
  • 2021-01-30 12:09

    If you have scipy version >= 0.18.0 installed you can use CubicSpline function from scipy.interpolate for cubic spline interpolation.

    You can check scipy version by running following commands in python:

    #!/usr/bin/env python3
    import scipy
    scipy.version.version
    

    If your scipy version is >= 0.18.0 you can run following example code for cubic spline interpolation:

    #!/usr/bin/env python3
    
    import numpy as np
    from scipy.interpolate import CubicSpline
    
    # calculate 5 natural cubic spline polynomials for 6 points
    # (x,y) = (0,12) (1,14) (2,22) (3,39) (4,58) (5,77)
    x = np.array([0, 1, 2, 3, 4, 5])
    y = np.array([12,14,22,39,58,77])
    
    # calculate natural cubic spline polynomials
    cs = CubicSpline(x,y,bc_type='natural')
    
    # show values of interpolation function at x=1.25
    print('S(1.25) = ', cs(1.25))
    
    ## Aditional - find polynomial coefficients for different x regions
    
    # if you want to print polynomial coefficients in form
    # S0(0<=x<=1) = a0 + b0(x-x0) + c0(x-x0)^2 + d0(x-x0)^3
    # S1(1< x<=2) = a1 + b1(x-x1) + c1(x-x1)^2 + d1(x-x1)^3
    # ...
    # S4(4< x<=5) = a4 + b4(x-x4) + c5(x-x4)^2 + d5(x-x4)^3
    # x0 = 0; x1 = 1; x4 = 4; (start of x region interval)
    
    # show values of a0, b0, c0, d0, a1, b1, c1, d1 ...
    cs.c
    
    # Polynomial coefficients for 0 <= x <= 1
    a0 = cs.c.item(3,0)
    b0 = cs.c.item(2,0)
    c0 = cs.c.item(1,0)
    d0 = cs.c.item(0,0)
    
    # Polynomial coefficients for 1 < x <= 2
    a1 = cs.c.item(3,1)
    b1 = cs.c.item(2,1)
    c1 = cs.c.item(1,1)
    d1 = cs.c.item(0,1)
    
    # ...
    
    # Polynomial coefficients for 4 < x <= 5
    a4 = cs.c.item(3,4)
    b4 = cs.c.item(2,4)
    c4 = cs.c.item(1,4)
    d4 = cs.c.item(0,4)
    
    # Print polynomial equations for different x regions
    print('S0(0<=x<=1) = ', a0, ' + ', b0, '(x-0) + ', c0, '(x-0)^2  + ', d0, '(x-0)^3')
    print('S1(1< x<=2) = ', a1, ' + ', b1, '(x-1) + ', c1, '(x-1)^2  + ', d1, '(x-1)^3')
    print('...')
    print('S5(4< x<=5) = ', a4, ' + ', b4, '(x-4) + ', c4, '(x-4)^2  + ', d4, '(x-4)^3')
    
    # So we can calculate S(1.25) by using equation S1(1< x<=2)
    print('S(1.25) = ', a1 + b1*0.25 + c1*(0.25**2) + d1*(0.25**3))
    
    # Cubic spline interpolation calculus example
        #  https://www.youtube.com/watch?v=gT7F3TWihvk
    
    0 讨论(0)
  • 2021-01-30 12:11

    In case, scipy is not installed:

    import numpy as np
    from math import sqrt
    
    def cubic_interp1d(x0, x, y):
        """
        Interpolate a 1-D function using cubic splines.
          x0 : a float or an 1d-array
          x : (N,) array_like
              A 1-D array of real/complex values.
          y : (N,) array_like
              A 1-D array of real values. The length of y along the
              interpolation axis must be equal to the length of x.
    
        Implement a trick to generate at first step the cholesky matrice L of
        the tridiagonal matrice A (thus L is a bidiagonal matrice that
        can be solved in two distinct loops).
    
        additional ref: www.math.uh.edu/~jingqiu/math4364/spline.pdf 
        """
        x = np.asfarray(x)
        y = np.asfarray(y)
    
        # remove non finite values
        # indexes = np.isfinite(x)
        # x = x[indexes]
        # y = y[indexes]
    
        # check if sorted
        if np.any(np.diff(x) < 0):
            indexes = np.argsort(x)
            x = x[indexes]
            y = y[indexes]
    
        size = len(x)
    
        xdiff = np.diff(x)
        ydiff = np.diff(y)
    
        # allocate buffer matrices
        Li = np.empty(size)
        Li_1 = np.empty(size-1)
        z = np.empty(size)
    
        # fill diagonals Li and Li-1 and solve [L][y] = [B]
        Li[0] = sqrt(2*xdiff[0])
        Li_1[0] = 0.0
        B0 = 0.0 # natural boundary
        z[0] = B0 / Li[0]
    
        for i in range(1, size-1, 1):
            Li_1[i] = xdiff[i-1] / Li[i-1]
            Li[i] = sqrt(2*(xdiff[i-1]+xdiff[i]) - Li_1[i-1] * Li_1[i-1])
            Bi = 6*(ydiff[i]/xdiff[i] - ydiff[i-1]/xdiff[i-1])
            z[i] = (Bi - Li_1[i-1]*z[i-1])/Li[i]
    
        i = size - 1
        Li_1[i-1] = xdiff[-1] / Li[i-1]
        Li[i] = sqrt(2*xdiff[-1] - Li_1[i-1] * Li_1[i-1])
        Bi = 0.0 # natural boundary
        z[i] = (Bi - Li_1[i-1]*z[i-1])/Li[i]
    
        # solve [L.T][x] = [y]
        i = size-1
        z[i] = z[i] / Li[i]
        for i in range(size-2, -1, -1):
            z[i] = (z[i] - Li_1[i-1]*z[i+1])/Li[i]
    
        # find index
        index = x.searchsorted(x0)
        np.clip(index, 1, size-1, index)
    
        xi1, xi0 = x[index], x[index-1]
        yi1, yi0 = y[index], y[index-1]
        zi1, zi0 = z[index], z[index-1]
        hi1 = xi1 - xi0
    
        # calculate cubic
        f0 = zi0/(6*hi1)*(xi1-x0)**3 + \
             zi1/(6*hi1)*(x0-xi0)**3 + \
             (yi1/hi1 - zi1*hi1/6)*(x0-xi0) + \
             (yi0/hi1 - zi0*hi1/6)*(xi1-x0)
        return f0
    
    if __name__ == '__main__':
        import matplotlib.pyplot as plt
        x = np.linspace(0, 10, 11)
        y = np.sin(x)
        plt.scatter(x, y)
    
        x_new = np.linspace(0, 10, 201)
        plt.plot(x_new, cubic_interp1d(x_new, x, y))
    
        plt.show()
    
    0 讨论(0)
  • 2021-01-30 12:13

    Minimal python3 code:

    from scipy import interpolate
    
    if __name__ == '__main__':
        x = [ 0, 1, 2, 3, 4, 5]
        y = [12,14,22,39,58,77]
    
        # tck : tuple (t,c,k) a tuple containing the vector of knots,
        # the B-spline coefficients, and the degree of the spline.
        tck = interpolate.splrep(x, y)
    
        print(interpolate.splev(1.25, tck)) # Prints 15.203125000000002
        print(interpolate.splev(...other_value_here..., tck))
    

    Based on comment of cwhy and answer by youngmit

    0 讨论(0)
  • 2021-01-30 12:26

    Short answer:

    from scipy import interpolate
    
    def f(x):
        x_points = [ 0, 1, 2, 3, 4, 5]
        y_points = [12,14,22,39,58,77]
    
        tck = interpolate.splrep(x_points, y_points)
        return interpolate.splev(x, tck)
    
    print(f(1.25))
    

    Long answer:

    scipy separates the steps involved in spline interpolation into two operations, most likely for computational efficiency.

    1. The coefficients describing the spline curve are computed, using splrep(). splrep returns an array of tuples containing the coefficients.

    2. These coefficients are passed into splev() to actually evaluate the spline at the desired point x (in this example 1.25). x can also be an array. Calling f([1.0, 1.25, 1.5]) returns the interpolated points at 1, 1.25, and 1,5, respectively.

    This approach is admittedly inconvenient for single evaluations, but since the most common use case is to start with a handful of function evaluation points, then to repeatedly use the spline to find interpolated values, it is usually quite useful in practice.

    0 讨论(0)
提交回复
热议问题