1

I have a discrete set of points (x_n, y_n) that I would like to approximate/represent as a linear combination of B-spline basis functions. I need to be able to manually change the number of B-spline basis functions used by the method, and I am trying to implement this in python using scipy. To be specific, below is a bit of code that I am using:

import scipy
spl = scipy.interpolate.splrep(x, y)

However, unless I have misunderstood or missed something in the documentation, it seems I cannot change the number of B-spline basis functions that scipy uses. That seems to be set by the size of x and y. So, my specific questions are:

  1. Can I change the number of B-spline basis functions used by scipy in the "splrep" function that I used above?

  2. Once I have performed the transformation shown in the code above, how can I access the coefficients of the linear combination? Am I correct in thinking that these coefficients are stored in the vector spl[1]?

  3. Is there a better method/toolbox that I should be using?

Thanks in advance for any help/guidance you can provide.

DanJonesOcean
  • 55
  • 1
  • 10

2 Answers2

0

Yes, spl[1] are the coefficients, and spl[0] contains the knot vector.

However, if you want to have a better control, you can manipulate the BSpline objects and construct them with make_interp_spline or make_lsq_spline, which accepts the knot vector and that determines the b-spline basis functions to use.

ev-br
  • 24,968
  • 9
  • 65
  • 78
0

You can change the number of B-spline basis functions, by supplying a knot vector with the t parameter. Since there is a connection number of knots = number of coefficients + degree + 1, the number of knots will also define the number of coefficients (== the number of basis functions).

The usage of the t parameter is not so intuitive since the given knots should be only the inner knots. So, for example, if you want 7 coefficients for a cubic spline you need to give 3 inner knots. Inside the function it pads the first and last (degree+1) knots with the xb and xe (clamped end conditions see for example here). Furthermore, as the documentation says, the knots should satisfy the Schoenberg-Whitney conditions.

Here is an example code that does this:

# Input:
x = np.linspace(0,2*np.pi, 9)
y = np.sin(x)

# Your code:
spl = scipy.interpolate.splrep(x, y)
t,c,k = spl  # knots, coefficients, degree (==3 for cubic)

# Computing the inner knots and using them:
t3 = np.linspace(x[0],x[-1],5)  # five equally spaced knots in the interval
t3 = t3[1:-1]  # take only the three inner values

spl3 = scipy.interpolate.splrep(x, y, t=t3)

Regarding your second question, you're right that the coefficients are indeed stored in spl[1]. However, note that (as the documentation says) the last (degree+1) values are zero-padded and should be ignored.

In order to evaluate the resulting B-spline you can use the function splev or the class BSpline. Below is some example code that evaluates and draws the above splines (resulting in the following figure):

enter image description here

xx = np.linspace(x[0], x[-1], 101)  # sample points
yy = scipy.interpolate.splev(xx, spl) # evaluate original spline
yy3 = scipy.interpolate.splev(xx, spl3)  # evaluate new spline

plot(x,y,'b.')  # plot original interpolation points
plot(xx,yy,'r-', label='spl')
plot(xx,yy3,'g-', label='spl3')
Iddo Hanniel
  • 1,636
  • 10
  • 18
  • Thanks for this. Very helpful. I'm realising that what I need is a way to define a B-spline basis first, without reference to a particular set of data, and *then* start transforming my data into that B-spline basis. I need a way to access/plot/save the B-spline basis itself, which doesn't seem to be possible with splrep - unless I've missed something. – DanJonesOcean Jul 12 '18 at 09:33
  • The B-spline basis is totally defined by the knot vector (and the degree of the spline) with the Cox-DeBoor recursive formula. So if you set the inner knots, `splrep` will set the knot vector to a clamped knot vector with your inner knots inside. A hack to evaluate/plot the basis function is to set the coefficient vector to `0..0,1,0,..0`, and plot using `splev` like in my answer. If you want a non-clamped knot vector then you will probably have to implement the function yourself (using the recursive formula or more efficient methods that can be found for example in The NURBs book). – Iddo Hanniel Jul 15 '18 at 09:17
  • That's a good suggestion. You are essentially giving the function splrep a set of unit vectors and using the results to form a basis. Very nice - thanks! – DanJonesOcean Jul 19 '18 at 08:48