Learn R Programming

⚠️There's a newer version (0.5.3) of this package.Take me there.

splines2

Package website: release | development

The R package splines2 is intended to be a user-friendly supplementary package to the base package splines.

Features

The package splines2 provides functions to construct basis matrices of

  • B-splines
  • M-splines
  • I-splines
  • convex splines (C-splines)
  • periodic splines
  • natural cubic splines
  • generalized Bernstein polynomials
  • their integrals (except C-splines) and derivatives of given order by closed-form recursive formulas

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.

Installation of CRAN Version

You can install the released version from CRAN.

install.packages("splines2")

Development

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")
remotes::install_github("wenjie2wang/splines2", upgrade = "never")

Getting Started

The Online document provides a reference for all functions and contains the following vignettes:

Performance

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 below for reference.

library(microbenchmark)
options(microbenchmark.unit="relative")
library(splines)
library(splines2)

set.seed(123)
x <- runif(1e3)
degree <- 3
ord <- degree + 1
internal_knots <- seq.int(0.1, 0.9, 0.1)
boundary_knots <- c(0, 1)
all_knots <- sort(c(internal_knots, rep(boundary_knots, ord)))

## check equivalency of outputs
my_check <- function(values) {
    all(sapply(values[- 1], function(x) {
        all.equal(unclass(values[[1]]), unclass(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(
        x, knots = internal_knots, degree = degree,
        intercept = TRUE, Boundary.knots = boundary_knots
    ),
    check = my_check
)
Unit: relative
                  expr    min     lq   mean median     uq    max neval
           splines::bs 3.7782 3.4316 3.1477 3.3053 2.7155 9.1782   100
 splines::splineDesign 2.2235 1.9719 1.9135 2.1350 1.8306 2.3227   100
     splines2::bSpline 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000   100

Similarly, for derivatives of B-splines, splines2::dbs() provides equivalent results with splines::splineDesign(), and is about 2x faster.

## Derivatives of B-splines
derivs <- 2
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
)
Unit: relative
                  expr    min     lq   mean median     uq    max neval
 splines::splineDesign 2.6144 2.4443 2.1666   2.36 1.9582 1.7738   100
         splines2::dbs 1.0000 1.0000 1.0000   1.00 1.0000 1.0000   100

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)
coef_sp <- rnorm(length(all_knots) - ord)
microbenchmark(
    "ibs::ibs" = ibs::ibs(x, knots = all_knots, ord = ord, coef = coef_sp),
    "splines2::ibs" = as.numeric(
        splines2::ibs(x, knots = internal_knots, degree = degree,
                      intercept = TRUE, Boundary.knots = boundary_knots) %*%
        coef_sp
    ),
    check = my_check
)
Unit: relative
          expr    min     lq   mean median     uq    max neval
      ibs::ibs 20.382 18.161 19.486 18.738 18.824 25.698   100
 splines2::ibs  1.000  1.000  1.000  1.000  1.000  1.000   100

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::nsp() 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::nsp" = nsp(
        x, knots = internal_knots, intercept = TRUE,
        Boundary.knots = boundary_knots
    )
)
Unit: relative
          expr    min     lq   mean median     uq    max neval
   splines::ns 4.7595 4.4793 4.4742 4.3056 4.2125 6.0708   100
 splines2::nsp 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000   100

The functions bSpline() and mSpline() produce periodic spline basis functions based on B-splines and M-splines, respectively, when periodic = TRUE is specified. The splines::periodicSpline() returns a periodic interpolation spline (based on B-splines) instead of basis matrix. We performed a comparison with package pbs (version 1.1), where the function pbs::pbs() produces a basis matrix of periodic B-spline by using splines::spline.des().

microbenchmark(
    "pbs::pbs" = pbs::pbs(x, knots = internal_knots, degree = degree,
                          intercept = TRUE, periodic = TRUE,
                          Boundary.knots = boundary_knots),
    "splines2::bSpline" = bSpline(
        x, knots = internal_knots, degree = degree, intercept = TRUE,
        Boundary.knots = boundary_knots, periodic = TRUE
    ),
    "splines2::mSpline" = mSpline(
        x, knots = internal_knots, degree = degree, intercept = TRUE,
        Boundary.knots = boundary_knots, periodic = TRUE
    )
)
Unit: relative
              expr    min     lq    mean median     uq     max neval
          pbs::pbs 3.9822 3.9709 3.35972 3.9262 3.6969 1.26560   100
 splines2::bSpline 1.0000 1.0000 1.00000 1.0000 1.0000 1.00000   100
 splines2::mSpline 1.1411 1.1505 0.95812 1.1814 1.1423 0.12699   100
sessionInfo()
R version 4.3.1 (2023-06-16)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Arch Linux

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblas.so.0.3;  LAPACK version 3.11.0

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       

time zone: America/New_York
tzcode source: system (glibc)

attached base packages:
[1] splines   stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] splines2_0.5.1.9000   microbenchmark_1.4.10

loaded via a namespace (and not attached):
 [1] digest_0.6.31    codetools_0.2-19 ibs_1.4          fastmap_1.1.1    xfun_0.39       
 [6] pbs_1.1          knitr_1.43       htmltools_0.5.5  rmarkdown_2.21   cli_3.6.0       
[11] compiler_4.3.1   tools_4.3.1      evaluate_0.21    Rcpp_1.0.10      yaml_2.3.7      
[16] rlang_1.1.1     

License

GNU General Public License (≥ 3)

Copy Link

Version

Install

install.packages('splines2')

Monthly Downloads

6,010

Version

0.5.1

License

GPL (>= 3)

Issues

Pull Requests

Stars

Forks

Maintainer

Last Published

August 19th, 2023

Functions in splines2 (0.5.1)

bSpline

B-Spline Basis for Polynomial Splines
cSpline

C-Spline Basis for Polynomial Splines
mSpline

M-Spline Basis for Polynomial Splines
iSpline

I-Spline Basis for Polynomial Splines
plot.splines2

Visualize Spline Basis Functions
bernsteinPoly

Generalized Bernstein Polynomial Basis Functions
splines2

splines2: Regression Spline Functions and Classes
deriv

Derivatives of Spline Basis Functions
knots

Extract Knots from the Given Object
predict

Compute Spline Function for Given Coefficients
update

Update Spline Basis Functions
naturalSpline

Natural Cubic Spline Basis for Polynomial Splines