Cubic spline implementation in Matlab
I think you are confused about what you are interpolating. You should interpolate x1 and y1 separately, and afterwards plot them against each other. The following example produces a smooth curve:
x1=[1000 3000 5000 6000 4000 2000 500 0 -1000 -3000 -5000 -6000 -4000 -2000 -500 1 999 2999 4999];
y1=[5000 4999 4990 3500 2500 2499 2498 2497 2496 2495 2494 1000 -1000 -999 -998 -997 -996 -995 -994];
s = [0,cumsum(sqrt(diff(x1).^2+diff(y1).^2))]
N = length(s);
figure();
plot(x1,y1);
hold on
s_fine = interp1(linspace(0,1,N),s,linspace(0,1,5*N));
pcx=interp1(s,x1,s_fine,'spline');
pcy=interp1(s,y1,s_fine,'spline');
plot(pcx,pcy,'r-');
How to perform cubic spline interpolation in python?
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.
The coefficients describing the spline curve are computed,
using splrep(). splrep returns an array of tuples containing the
coefficients.These coefficients are passed into splev() to actually
evaluate the spline at the desired pointx
(in this example 1.25).x
can also be an array. Callingf([1.0, 1.25, 1.5])
returns the
interpolated points at1
,1.25
, and1,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.
understand implementation of cubic interpolation
The sample codes in the links you posted are all cubic interpolations either from 4 points or from 2 points and 2 derivatives.
The 'mu' is the parameter at which you want to evaluate the y value. The input y0, y1,y2 and y3 are the y coordinates of the 4 input points. You can do the same to evaluate x value at parameter 'mu' by passing x0, x1, x2 and x3 to the function.
You said you want to have a function that will take x value as parameter input and get back y value as output. This is only possible when you represent your curve in the explicit form, i.e., y=f(x). However, explicit form is not capable of representing curves with multiple y values with the same x value. So, they are not good ways of representing curves in general. Parametric form is a better way of representing a curve. For example, a 2D curve C(t) is represented as C(t) = (x(t), y(t)) or a 3D curve can be represented as C(t)=(x(t), y(t), z(t)). Representing a full circle as (x(t), y(t))=(rcos(t),rsin(t)) is a good example of parametric form.
If you want to draw a smooth curve connecting a series of data points, the easiest way is to implement the Catmull Rom spline or Overhauser spline. Both of them are special form of cubic Hermite curve. Read the Wikipedia page about cubic Hermite curve here and go to the Catmull Rom spline section within the same page and you shall be able to know how to create a Catmull Rom spline interpolating the data points and how to evaluate it to get (x, y) value to draw the spline in your canvas.
Cubic spline interpolation factors required to pass through original datapoints (scipy, python)
First, it is true, as you wrote, that a cubic interpolation pass through its original points. You can verify this by:
all(y == interp_f(x)) # gives True
Your formula for up-sampling seems a bit mixed up. It is easy to see, if we walk through an example:
- Assume we have the interval
[0, w]
, wherew = 2
. - Having
n = 5
samples gives(n-1)
intervals of widthd = w/(n-1) = .5
. - To up-sample by a factor of
f=4
, we except the new interval width to bed_4 = d / f = 0.125
. - Thus
d_4 = w / (n_4 - 1)
needs to hold as well, wheren_4
are the number samples of the up-sampled signal. - Exploiting
1 / d_4 = f * (n-1) / w
should be equal to(n_4 - 1) / w
results inn_4 = f * (n-1) + 1 = 17
As long as f
is a positive integer, the original samples will be included in the up-sampled signal (due to d_4 = d / f
). We can verify our formula by:
n, f = 5, 4
n_4 = f * (n-1) + 1
x = np.linspace(0, 2, n)
x_4 = np.linspace(0, 2, n_4)
if all(x_4[::f] == x): # Every fourth sample is equal to the original
print("Up-sampling works.")
Cubic Spline Python code producing linear splines
Ok, got this working. The problem was in my implementation. I got it working with a different approach, where the splines are constructed individually instead of continuously. This is fully functioning cubic spline interpolation by method of first constructing the coefficients of the spline polynomials (which is 99% of the work), then implementing them. Obviously this is not the only way to do it. I may work on a different approach and post that if there is interest. One thing that would clarify the code would be an image of the linear system that is solved, but i can't post pics until my rep gets up to 10. If you want to go deeper into the algorithm, see the text book link in the comments above.
import matplotlib.pyplot as plt
from pylab import arange
from math import e
from math import pi
from math import sin
from math import cos
from numpy import poly1d
# need some zero vectors...
def zeroV(m):
z = [0]*m
return(z)
#INPUT: n; x0, x1, ... ,xn; a0 = f(x0), a1 =f(x1), ... , an = f(xn).
def cubic_spline(n, xn, a):
"""function cubic_spline(n,xn, a, xd) interpolates between the knots
specified by lists xn and a. The function computes the coefficients
and outputs the ranges of the piecewise cubic splines."""
h = zeroV(n-1)
# alpha will be values in a system of eq's that will allow us to solve for c
# and then from there we can find b, d through substitution.
alpha = zeroV(n-1)
# l, u, z are used in the method for solving the linear system
l = zeroV(n+1)
u = zeroV(n)
z = zeroV(n+1)
# b, c, d will be the coefficients along with a.
b = zeroV(n)
c = zeroV(n+1)
d = zeroV(n)
for i in range(n-1):
# h[i] is used to satisfy the condition that
# Si+1(xi+l) = Si(xi+l) for each i = 0,..,n-1
# i.e., the values at the knots are "doubled up"
h[i] = xn[i+1]-xn[i]
for i in range(1, n-1):
# Sets up the linear system and allows us to find c. Once we have
# c then b and d follow in terms of it.
alpha[i] = (3./h[i])*(a[i+1]-a[i])-(3./h[i-1])*(a[i] - a[i-1])
# I, II, (part of) III Sets up and solves tridiagonal linear system...
# I
l[0] = 1
u[0] = 0
z[0] = 0
# II
for i in range(1, n-1):
l[i] = 2*(xn[i+1] - xn[i-1]) - h[i-1]*u[i-1]
u[i] = h[i]/l[i]
z[i] = (alpha[i] - h[i-1]*z[i-1])/l[i]
l[n] = 1
z[n] = 0
c[n] = 0
# III... also find b, d in terms of c.
for j in range(n-2, -1, -1):
c[j] = z[j] - u[j]*c[j+1]
b[j] = (a[j+1] - a[j])/h[j] - h[j]*(c[j+1] + 2*c[j])/3.
d[j] = (c[j+1] - c[j])/(3*h[j])
# Now that we have the coefficients it's just a matter of constructing
# the appropriate polynomials and graphing.
for j in range(n-1):
cub_graph(a[j],b[j],c[j],d[j],xn[j],xn[j+1])
plt.show()
def cub_graph(a,b,c,d, x_i, x_i_1):
"""cub_graph takes the i'th coefficient set along with the x[i] and x[i+1]'th
data pts, and constructs the polynomial spline between the two data pts using
the poly1d python object (which simply returns a polynomial with a given root."""
# notice here that we are just building the cubic polynomial piece by piece
root = poly1d(x_i,True)
poly = 0
poly = d*(root)**3
poly = poly + c*(root)**2
poly = poly + b*root
poly = poly + a
# Set up our domain between data points, and plot the function
pts = arange(x_i,x_i_1, 0.001)
plt.plot(pts, poly(pts), '-')
return
If you want to test it, here's some data you can use to get started, which comes from the
function 1.6e^(-2x)sin(3*pi*x) between 0 and 1:
# These are our data points
x_vals = [0, 1./6, 1./3, 1./2, 7./12, 2./3, 3./4, 5./6, 11./12, 1]
# Set up the domain
x_domain = arange(0,2, 1e-2)
fx = zeroV(10)
# Defines the function so we can get our fx values
def sine_func(x):
return(1.6*e**(-2*x)*sin(3*pi*x))
for i in range(len(x_vals)):
fx[i] = sine_func(x_vals[i])
# Run cubic_spline interpolant.
cubic_spline(10,x_vals,fx)
Dividing cubic spline computation
You can start the interpolation at any time, using the available points (presumably in the correct order !). Cubic spline interpolation is a very stable process and when you add more points, most of the curve remains unchanged.
If your concern is that you want to avoid redoing the whole computation several times, I guess that it is enough to work on several sections with some overlap (say 20 points) and discard the results of the 10 extreme points of all sections.
Related Topics
Why am I Getting Inputmismatchexception
Eclipse 2021-09 Code Completion Not Showing All Methods and Classes
Rotating Coordinate Plane for Data and Text in Java
Java Socket Why Server Can Not Reply Client
Which Java Collection Should I Use
How to Get the Current Time in Yyyy-Mm-Dd Hh:Mi:Sec.Millisecond Format in Java
The Performance Impact of Using Instanceof in Java
Using Pairs or 2-Tuples in Java
Abstract Class VS Interface in Java
Convert Latitude/Longitude Point to a Pixels (X,Y) on Mercator Projection
Differencebetween Set and List
How to Respond with an Http 400 Error in a Spring MVC @Responsebody Method Returning String
Make Maven to Copy Dependencies into Target/Lib
How to Access a Value Defined in the Application.Properties File in Spring Boot
Why Would One Mark Local Variables and Method Parameters as "Final" in Java
What Does the Question Mark in Java Generics' Type Parameter Mean