Curvature when extrapolated using Natural Cubic Spline - python

One of the assumptions behind the Natural Cubic Spline is that at the endpoints of the interval of interpolation, the second derivative of the spline polynomials is set to be equal to 0. I tried to show that using the Natural Cubic Spline via from scipy.interpolate import CubicSplines in the example (code below).
from scipy.interpolate import CubicSpline
from numpy import linspace
import matplotlib.pyplot as plt
runge_f = lambda x: 1 / (1 + 25*x**2)
x = linspace(-2, 2, 11)
y = runge_f(x)
cs = CubicSpline(x, y, bc_type = "natural")
t = linspace(-5, 5, 1000)
plt.plot(x, y, "p", color="red")
plt.plot(t, runge_f(t), color="black")
plt.plot(t, cs(t), color="lightblue")
plt.show()
In the presented example, the extrapolated points' curvature is not equal to zero - shouldn't the extrapolation outside the interval be linear in the Natural Cubic Spline?

The curvature (second derivative) of the spline at the end points is indeed 0, as you can check by running this
print(cs(x[0],2), cs(x[-1], 2))
which calculates second derivatives at both ends of your x interpolation interval. However that does not mean the spline is flat beyond the limits -- it continues on as a cubic polynomial. If you want to extrapolate linearly outside the range, you have to do it yourself. It is easy enough to extrapolate flat: replace your cs=.. line with
from scipy.interpolate import interp1d
cs = interp1d(x,y,fill_value = (y[0],y[-1]), bounds_error = False)
to get something like this:
but a bit more work to extrapolate linearly. I am not sure there is a Python function for that (or rather I am sure there is one I just don't know what it is)

Related

non equally spaced points along x-axis in a plot

When I want to plot a curve f(x) with pyplot, what I usually do is to create a vector X with all the x-values equally spaced:
import numpy as np
X=np.linspace(0.,1.,100)
then I create the function
def f(x):
return x**2
and then I make the plot
from matplotlib import pyplot as plt
plt.plot(X,f(X))
plt.show()
However, in some cases I might want the x-values not to be equally spaced, when the function is very stiff in some regions and very smooth in others.
What is the correct way to properly choose the best X vector for the function I want to plot?
In its generality there is not definitive answer to this. But you can of course always choose the complete range with the required density,
X = np.linspace(0.,1., 6000)
or you can decide for some intervals and set the density differently for those
x1 = np.linspace(0.0,0.5, 60)
x2 = np.linspace(0.5,0.6, 5000)
x3 = np.linspace(0.6,1.0, 10)
X = np.concatenate((x1, x2, x3))

Find tangent line satisfying certain requirements to curve equation with optimizer?

I have what seems to be a relatively simplistic geometric optimization question, but one in which I have absolutely no experience.
I have a bunch of data that I am fitting a spline through to find its equation, such as the below,
And this is the equation I fit to the dots,
with the code,
import matplotlib.pyplot as plt
from scipy.interpolate import UnivariateSpline
plt.figure(figsize=(6.5, 4))
plt.axis([0.017,.045,.0045,.014])
plt.scatter(x,y,color='orange')
spl = UnivariateSpline(x, y)
plt.plot(xs, spl(xs), 'b', lw=3)
plt.show()
And what I want to do is find the equation of a line, y = mx + b, with a certain y-intercept a that hits the blue curve at some tangent point. So, for example, if my line has to pass through y=.006, what is the slope b of its line that makes it hit the blue line's tangent?
I though about doing a regular optimization over the derivatives of the curve at a large amount of points, but I am unsure how to set this up in a rigorous way.

What do the different values of the kind argument mean in scipy.interpolate.interp1d?

The SciPy documentation explains that interp1d's kind argument can take the values ‘linear’, ‘nearest’, ‘zero’, ‘slinear’, ‘quadratic’, ‘cubic’. The last three are spline orders and 'linear' is self-explanatory. What do 'nearest' and 'zero' do?
nearest "snaps" to the nearest data point.
zero is a zero order spline. It's value at any point is the last raw value seen.
linear performs linear interpolation and slinear uses a first
order spline. They use different code and can produce similar but subtly different results.
quadratic uses second order spline interpolation.
cubic uses third order spline interpolation.
Note that the k parameter can also accept an integer specifying the order of spline interpolation.
import numpy as np
import matplotlib.pyplot as plt
import scipy.interpolate as interpolate
np.random.seed(6)
kinds = ('nearest', 'zero', 'linear', 'slinear', 'quadratic', 'cubic')
N = 10
x = np.linspace(0, 1, N)
y = np.random.randint(10, size=(N,))
new_x = np.linspace(0, 1, 28)
fig, axs = plt.subplots(nrows=len(kinds)+1, sharex=True)
axs[0].plot(x, y, 'bo-')
axs[0].set_title('raw')
for ax, kind in zip(axs[1:], kinds):
new_y = interpolate.interp1d(x, y, kind=kind)(new_x)
ax.plot(new_x, new_y, 'ro-')
ax.set_title(kind)
plt.show()
‘nearest’ returns data point from X nearest to the argument, or
interpolates function y=f(x) at the point x using the data point nearest to x
'zero' I would guess is equivalent to truncation of argument and thus using data point closest toward zero

Python Curve Fitting

I'm trying to use Python to fit a curve to a set of points. Essentially the points look like this.
The blue curve indicates the data entered (in this case 4 points) with the green being a curve fit using np.polyfit and polyfit1d. What I essentially want is a curve fit that looks very similar to the blue line but with a smoother change in gradient at points 1 and 2 (meaning I don't require the line to pass through these points).
What would be the best way to do this? The line looks like an arctangent, is there any way to specify an arctangent fit?
I realise this is a bit of a rubbish question but I want to get away without specifying more points. Any help would be greatly appreciated.
It seems that you might be after interpolation between points rather than fitting a polynomial References: Spline Interpolation with Python and Fitting polynomials to data
However, in either case here is a code snippet that should get you started:
import numpy as np
import scipy as sp
from scipy.interpolate import interp1d
x = np.array([0,5,10,15,20,30,40,50])
y = np.array([0,0,0,12,40,40,40,40])
coeffs = np.polyfit(x, y, deg=4)#you can change degree as you see fit
poly = np.poly1d(coeffs)
yp = np.polyval(poly, x)
interpLength = 10
new_x = np.linspace(x.min(), x.max(), new_length)
new_y = sp.interpolate.interp1d(x, y, kind='cubic')(new_x)
plt.plot(x, y, '.', x, yp, '-', new_x,new_y, '--')
plt.show()

Add more sample points to data

Given some data of shape 20x45, where each row is a separate data set, say 20 different sine curves with 45 data points each, how would I go about getting the same data, but with shape 20x100?
In other words, I have some data A of shape 20x45, and some data B of length 20x100, and I would like to have A be of shape 20x100 so I can compare them better.
This is for Python and Numpy/Scipy.
I assume it can be done with splines, so I am looking for a simple example, maybe just 2x10 to 2x20 or something, where each row is just a line, to demonstrate the solution.
Thanks!
Ubuntu beat me to it while I was typing this example, but his example just uses linear interpolation, which can be more easily done with numpy.interpolate... (The difference is only a keyword argument in scipy.interpolate.interp1d, however).
I figured I'd include my example, as it shows using scipy.interpolate.interp1d with a cubic spline...
import numpy as np
import scipy as sp
import scipy.interpolate
import matplotlib.pyplot as plt
# Generate some random data
y = (np.random.random(10) - 0.5).cumsum()
x = np.arange(y.size)
# Interpolate the data using a cubic spline to "new_length" samples
new_length = 50
new_x = np.linspace(x.min(), x.max(), new_length)
new_y = sp.interpolate.interp1d(x, y, kind='cubic')(new_x)
# Plot the results
plt.figure()
plt.subplot(2,1,1)
plt.plot(x, y, 'bo-')
plt.title('Using 1D Cubic Spline Interpolation')
plt.subplot(2,1,2)
plt.plot(new_x, new_y, 'ro-')
plt.show()
One way would be to use scipy.interpolate.interp1d:
import scipy as sp
import scipy.interpolate
import numpy as np
x=np.linspace(0,2*np.pi,45)
y=np.zeros((2,45))
y[0,:]=sp.sin(x)
y[1,:]=sp.sin(2*x)
f=sp.interpolate.interp1d(x,y)
y2=f(np.linspace(0,2*np.pi,100))
If your data is fairly dense, it may not be necessary to use higher order interpolation.
If your application is not sensitive to precision or you just want a quick overview, you could just fill the unknown data points with averages from neighbouring known data points (in other words, do naive linear interpolation).

Categories