0

I intend to fit a clamped b-spline to a set of control points in R, but have trouble understanding the use of the knots parameter in bs. Given a set of control points:

path <- data.frame(
    x = c(3, 3.5, 4.6875, 9.625, 5.5625, 19.62109375, 33.6796875, 40.546875, 36.59375, 34.5, 33.5, 33),
    y = c(0, 1, 4, 5, 6, 8, 7, 6, 5, 2, 1, 0)
)

I fit x and y independently against the distance along the path:

path$distance <- c(0, cumsum(sqrt(diff(path[,1])^2 + diff(path[,2])^2)))
path$distance
##  [1]  0.000000  1.118034  4.344511  9.382259 13.566026 27.766169 41.860284 48.799899 52.877545 56.535931 57.950145
## [12] 59.068179

but I want to provide an open uniform knot vector in order to anchor the fit to the first and last point - using df does not support this.

As I understand it, for my given set of points, and a degree of 3 for the spline, there must be (12-1)+3+2 = 16 knots (per m=n+p+1, for #knots=m+1, #control=n+1, degree=p), so for a clamped spline, this should be a nice knot vector:

knots <- seq(path$distance[1], path$distance[12], length.out = 10)
knots <- c(rep(knots[1], 3), knots, rep(knots[10], 3))
knots
##  [1]  0.000000  0.000000  0.000000  0.000000  6.563131 13.126262 19.689393 26.252524 32.815655
## [10] 39.378786 45.941917 52.505048 59.068179 59.068179 59.068179 59.068179

using this gives some crazy numbers though, as well as a warning about rank-deficiency, so clearly I must have it wrong somehow:

pred_df  <-  data.frame(x=0,y=0,distance=seq(min(path$distance), max(path$distance), length.out=100))
xPath <- predict(lm(x~bs(distance, knots=knots, degree = 3), path), pred_df)
## Warning message:
## In predict.lm(lm(x ~ bs(distance, knots = knots, degree = degree),  :
##   prediction from a rank-deficient fit may be misleading
summary(xPath)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## -2133.000     3.468    16.700  -161.900    64.590   857.800 

What is the correct way to specify the knot-vector given a set of control points and a degree?

ThomasP85
  • 1,624
  • 2
  • 15
  • 26
  • Feel like that the Schoenberg–Whitney condition is not satisfied. If possible, please post the exact values for the knot vector and the parameters for each point. – fang Nov 09 '15 at 17:50
  • @fang See edit. I've added the content of the knot vector. I'm unsure about what you mean with parameters for each point though. Isn't a spline given by simply control points, knot vector and degree? – ThomasP85 Nov 10 '15 at 09:02
  • You said you want to "fit x and y independently against the distance along the path". So, the 'parameter' would be the "distance along the path" for each x (or y). In B-spline interpolation, it is often needed to solve a linear equation set and if the 'parameter' and 'knots" are not well-defined, it is easy to have ill-conditioned matrix that might cause the "rank deficiency" warning. – fang Nov 10 '15 at 18:04
  • @fang I see - I've added the content of distance. My approach is based on http://stackoverflow.com/questions/33574457/fit-a-b-spline-to-a-control-path?lq=1, but if there's a better approach to drawing parametric splines I would be all ears. – ThomasP85 Nov 10 '15 at 19:51

1 Answers1

1

Judging from your 'parameter' and 'knots' data, indeed they violate the Schoenberg-Whitney condition. I am not familiar with the syntax in R, but it seems that you are generating the knot values uniformly between path$distance[1] and path$distance[12]. This is not right. You should generate your knot values in the following way:

0) Denote the parameters as p[i], where i = 0 to (n-1), p[0]=0.0 and n is number of points. For your case, n=12
1) Create the knot values as

knot[0] = (p[1]+p[2]+p[3])/3
knot[1] = (p[2]+p[3]+p[4])/3
knot[2] = (p[3]+p[4]+p[5])/3
......
These are the interior knot values. You should notice that p[0] and p[n-1] will not be used in this step. You should obtain 8 interior knot values for your case.
2) Now, add p[0] to the front of the knot values 4 times (for degree=3) and add p[n-1] to the end of the knot values 4 times and you are done. For your case, you should get 16 knot values in total.

Please note that this is not the only way to generate a valid knot vector. But knot vector generated in this way will always satisfy the Shoenberg-Whitney condition.

fang
  • 3,473
  • 1
  • 13
  • 19
  • Thanks for the explanation - my main problem appeared to be not catching that only internal knots should be supplied to the bs algorithm (this is purely me not reading the documentation carefully enough). In your knot generator is the fact that you use the mean of three points per knot that the degree is 3? – ThomasP85 Nov 11 '15 at 10:11