The R package splines2 is intended to be a user-friendly supplementary package to the base package splines.
The package splines2 (version 0.4.5) provides functions to construct basis matrices of
In addition to the R interface, splines2 provides a C++ header-only library integrated with Rcpp, which allows the construction of spline basis functions directly in C++ with the help of Rcpp and RcppArmadillo. Thus, it can also be treated as one of the Rcpp* packages. A toy example package that uses the C++ interface is available here.
You can install the released version from CRAN.
install.packages("splines2")
The latest version of the package is under development at GitHub. If it is able to pass the automated package checks, one may install it by
if (! require(remotes)) install.packages("remotes")
::install_github("wenjie2wang/splines2", upgrade = "never") remotes
The Online document provides a reference for all functions and contains the following vignettes:
Since v0.3.0, the implementation of the main functions has been
rewritten in C++ with the help of the Rcpp and
RcppArmadillo packages. The computational performance
has thus been boosted and comparable with the function
splines::splineDesign()
.
Some quick micro-benchmarks are provided for reference as follows:
library(microbenchmark)
options(microbenchmark.unit="relative")
library(splines)
library(splines2)
set.seed(123)
<- runif(1e3)
x <- 3
degree <- degree + 1
ord <- seq.int(0.1, 0.9, 0.1)
internal_knots <- c(0, 1)
boundary_knots <- sort(c(internal_knots, rep(boundary_knots, ord)))
all_knots
## check equivalency of outputs
<- function(values) {
my_check all(sapply(values[- 1], function(x) {
all.equal(unclass(values[[1]]), x, check.attributes = FALSE)
})) }
For B-splines, function splines2::bSpline()
provides
equivalent results with splines::bs()
and
splines::splineDesign()
, and is about 3x faster than
bs()
and 2x faster than splineDesign()
for
this example.
## B-splines
microbenchmark(
"splines::bs" = bs(x, knots = internal_knots, degree = degree,
intercept = TRUE, Boundary.knots = boundary_knots),
"splines::splineDesign" = splineDesign(x, knots = all_knots, ord = ord),
"splines2::bSpline" = bSpline(
knots = internal_knots, degree = degree,
x, intercept = TRUE, Boundary.knots = boundary_knots
),check = my_check,
times = 1e3
)
Unit: relative
expr min lq mean median uq max neval
splines::bs 3.5106 3.350 3.3205 3.2759 3.3104 1.1756 1000
splines::splineDesign 2.0964 1.978 2.1040 1.9115 1.9499 1.2490 1000
splines2::bSpline 1.0000 1.000 1.0000 1.0000 1.0000 1.0000 1000
Similarly, for derivatives of B-splines, splines2::dbs()
provides equivalent results with splines::splineDesign()
,
and is about 2x faster.
## Derivatives of B-splines
<- 2
derivs microbenchmark(
"splines::splineDesign" = splineDesign(x, knots = all_knots,
ord = ord, derivs = derivs),
"splines2::dbs" = dbs(x, derivs = derivs, knots = internal_knots,
degree = degree, intercept = TRUE,
Boundary.knots = boundary_knots),
check = my_check,
times = 1e3
)
Unit: relative
expr min lq mean median uq max neval
splines::splineDesign 2.6616 2.5268 2.541 2.4446 2.4563 1.108 1000
splines2::dbs 1.0000 1.0000 1.000 1.0000 1.0000 1.000 1000
The splines package does not contain an
implementation for integrals of B-splines. Thus, we performed a
comparison with package ibs (version
r packageVersion("ibs")
), where the function
ibs::ibs()
was also implemented in
Rcpp.
## integrals of B-splines
set.seed(123)
<- rnorm(length(all_knots) - ord)
coef_sp microbenchmark(
"ibs::ibs" = ibs::ibs(x, knots = all_knots, ord = ord, coef = coef_sp),
"splines2::ibs" = as.numeric(
::ibs(x, knots = internal_knots, degree = degree,
splines2intercept = TRUE, Boundary.knots = boundary_knots) %*%
coef_sp
),check = my_check,
times = 1e3
)
Unit: relative
expr min lq mean median uq max neval
ibs::ibs 20.41 17.921 17.714 19.104 19.017 12.92 1000
splines2::ibs 1.00 1.000 1.000 1.000 1.000 1.00 1000
The function ibs::ibs()
returns the integrated B-splines
instead of the integrals of spline basis functions. Thus, we applied the
same coefficients to the basis functions from
splines2::ibs()
for equivalent results, which was still
much faster than ibs::ibs()
.
For natural cubic splines (based on B-splines),
splines::ns()
uses the QR decomposition to find the null
space of the second derivatives of B-spline basis functions at boundary
knots, while splines2::naturalSpline()
utilizes the
closed-form null space derived from the second derivatives of cubic
B-splines, which produces nonnegative basis functions (within boundary)
and is more computationally efficient.
microbenchmark(
"splines::ns" = ns(x, knots = internal_knots, intercept = TRUE,
Boundary.knots = boundary_knots),
"splines2::naturalSpline" = naturalSpline(
knots = internal_knots, intercept = TRUE,
x, Boundary.knots = boundary_knots
),times = 1e3
)
Unit: relative
expr min lq mean median uq max neval
splines::ns 4.9761 4.7383 4.8082 4.4816 4.4964 2.209 1000
splines2::naturalSpline 1.0000 1.0000 1.0000 1.0000 1.0000 1.000 1000
The function mSpline()
produces periodic spline basis
functions (based on M-splines) when periodic = TRUE
is
specified. The splines::periodicSpline()
returns a periodic
interpolation spline (based on B-splines) instead of basis matrix. Thus,
we performed a comparison with package pbs (version
r packageVersion("pbs")
), where the function
pbs::pbs()
produces a basis matrix of periodic B-spline by
using splines::spline.des()
(a wrapper function of
splines::splineDesign()
).
microbenchmark(
"pbs::pbs" = pbs::pbs(x, knots = internal_knots, degree = degree,
intercept = TRUE, periodic = TRUE,
Boundary.knots = boundary_knots),
"splines2::mSpline" = mSpline(
knots = internal_knots, degree = degree, intercept = TRUE,
x, Boundary.knots = boundary_knots, periodic = TRUE
),times = 1e3
)
Unit: relative
expr min lq mean median uq max neval
pbs::pbs 3.4692 3.2874 3.5597 3.1446 3.1321 14.948 1000
splines2::mSpline 1.0000 1.0000 1.0000 1.0000 1.0000 1.000 1000
sessionInfo()
R version 4.2.0 (2022-04-22)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Arch Linux
Matrix products: default
BLAS: /usr/lib/libopenblasp-r0.3.20.so
LAPACK: /usr/lib/liblapack.so.3.10.1
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8
[4] LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C LC_ADDRESS=C
[10] LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] splines stats graphics grDevices utils datasets methods base
other attached packages:
[1] splines2_0.4.5 microbenchmark_1.4.9
loaded via a namespace (and not attached):
[1] Rcpp_1.0.8.3 codetools_0.2-18 ibs_1.4 digest_0.6.29 magrittr_2.0.3
[6] evaluate_0.15 rlang_1.0.2 stringi_1.7.6 cli_3.2.0 rmarkdown_2.14
[11] tools_4.2.0 stringr_1.4.0 xfun_0.30 yaml_2.3.5 fastmap_1.1.0
[16] compiler_4.2.0 pbs_1.1 htmltools_0.5.2 knitr_1.38