I fitted a smoothing spline to data in R with
library(splines)
Model <- smooth.spline(x, y, df =6)
I would like to take the fitted spline and evaluate it for arbitrary new data in an external code (not in R). In other words, do what the predict.smooth.spline
function does. I had a look at the Model
object:
> str(Total_work_model)
List of 15
$ x : num [1:14] 0.0127 0.0186 0.0275 0.0343 0.0455 ...
$ y : num [1:14] 3174 3049 2887 2862 2975 ...
$ w : num [1:14] 1 1 1 1 1 1 1 1 1 1 ...
$ yin : num [1:14] 3173 3075 2857 2844 2984 ...
$ data :List of 3
..$ x: num [1:14] 0.0343 0.0455 0.0576 0.0697 0.0798 ...
..$ y: num [1:14] 2844 2984 3048 2805 2490 ...
..$ w: num [1:14] 1 1 1 1 1 1 1 1 1 1 ...
$ lev : num [1:14] 0.819 0.515 0.542 0.568 0.683 ...
$ cv.crit : num 6494075
$ pen.crit: num 3260
$ crit : num 3
$ df : num 8
$ spar : num 0.353
$ lambda : num 8.26e-05
$ iparms : Named int [1:3] 3 0 10
..- attr(*, "names")= chr [1:3] "icrit" "ispar" "iter"
$ fit :List of 5
..$ knot : num [1:20] 0 0 0 0 0.056 ...
..$ nk : int 16
..$ min : num 0.0127
..$ range: num 0.104
..$ coef : num [1:16] 3174 3132 3027 2871 2842 ...
..- attr(*, "class")= chr "smooth.spline.fit"
$ call : language smooth.spline(x = Phi00, y = Total, df = 8)
- attr(*, "class")= chr "smooth.spline"
I think the Model$fit$knot
and Model$fit$coef
vectors contain the full description of the fit. Note that the knots are 20, while x
and y
have 14 elements each: I always thought a smoothing spline would have as many knots as fitting points. However, since the first three and the last three knots are identical, 20-6 = 14 which makes sense. The problem is that I don't know how to use Model$fit$knot
and Model$fit$coef
to make predictions outside R. I tried to have a look at predict.smooth.spline
, but surprisingly that's what I get
> library(splines)
> predict.smooth.spline
Error: object 'predict.smooth.spline' not found
EDIT: since apparently some users misunderstood the question, I know how to use predict
in R, to get new values of my smoothing spline. The problem is that I want to make those predictions in an external code. Thus I wanted to have a look at the code for the function predict.smooth.spline
, so that I could try to reproduce the algorithm outside R. Usually in R you can read the code of a function just by entering its name (without arguments and without parentheses) at the R prompt. But when I try to do that with predict.smooth.spline
, I get the above error.
EDIT2: thanks to the great help from @r2evans, I found the source for the predict
method of smooth.spline
. I (think I) understand most of it:
> stats:::predict.smooth.spline.fit
function (object, x, deriv = 0, ...)
{
if (missing(x))
x <- seq.int(from = object$min, to = object$min + object$range,
length.out = length(object$coef) - 4L)
xs <- (x - object$min)/object$range
extrap.left <- xs < 0
extrap.right <- xs > 1
interp <- !(extrap <- extrap.left | extrap.right)
n <- sum(interp)
y <- xs
if (any(interp))
y[interp] <- .Fortran(C_bvalus, n = as.integer(n), knot = as.double(object$knot),
coef = as.double(object$coef), nk = as.integer(object$nk),
x = as.double(xs[interp]), s = double(n), order = as.integer(deriv))$s
if (any(extrap)) {
xrange <- c(object$min, object$min + object$range)
if (deriv == 0) {
end.object <- Recall(object, xrange)$y
end.slopes <- Recall(object, xrange, 1)$y * object$range
if (any(extrap.left))
y[extrap.left] <- end.object[1L] + end.slopes[1L] *
(xs[extrap.left] - 0)
if (any(extrap.right))
y[extrap.right] <- end.object[2L] + end.slopes[2L] *
(xs[extrap.right] - 1)
}
else if (deriv == 1) {
end.slopes <- Recall(object, xrange, 1)$y * object$range
y[extrap.left] <- end.slopes[1L]
y[extrap.right] <- end.slopes[2L]
}
else y[extrap] <- 0
}
if (deriv > 0)
y <- y/(object$range^deriv)
list(x = x, y = y)
}
However, I have two difficulties:
the
.Fortran()
function calls a Fortran subroutinebvalus
whose source is quite simple. However,bvalus
in turn callsbvalue
which is much more complicated, and callsinterv
whose source I cannot find. Bad news:bvalue
is way too complicated for me to understand (I'm definitely not a Fortran expert). Good news: the external code which must reproducepredict.smooth.spline.fit
is also a Fortran code. If worse comes to worst, I could just ask my coworker to include the source frombvalus
andbvalue
in his code. However, even in this admittedly not so nice scenario, I would still miss the source code forinterv
(I hope it doesn't call something else!!!).I don't understand what it's being done here (note I'm only interested in the
deriv == 0
case):
k
if (any(extrap)) {
xrange <- c(object$min, object$min + object$range)
if (deriv == 0) {
end.object <- Recall(object, xrange)$y
end.slopes <- Recall(object, xrange, 1)$y * object$range
if (any(extrap.left))
y[extrap.left] <- end.object[1L] + end.slopes[1L] *
(xs[extrap.left] - 0)
if (any(extrap.right))
y[extrap.right] <- end.object[2L] + end.slopes[2L] *
(xs[extrap.right] - 1)
}
Some sort of recursive code? Any help here?
Exporting a smoothing spline as piecewise polynomials is one way to reconstruct the spline outside R. My package
SplinesUtils
: https://github.com/ZheyuanLi/SplinesUtils can do this. You can get it byThe function to be used here is
SmoothSplinesAsPiecePoly
. I am just copying in examples for this function in its documentation.The knots of the spline are
The piecewise polynomial coefficients are
The package also has other functionality. See Identify all local extrema of a fitted smoothing spline via R function 'smooth.spline' for an example.