penalty {gps} | R Documentation |
Wiggliness penalties for penalized B-splines
Description
For penalized B-splines (including standard or general P-splines and O-splines), (1) construct matrix in the wiggliness penalty
; (2) sample B-spline coefficients from their prior distribution
; (3) compute the Moore-Penrose generalized inverse matrix
.
Usage
SparseD(xt, d, m = NULL, gps = TRUE)
PriorCoef(n, D)
MPinv(D, only.diag = FALSE)
Arguments
xt |
full knot sequence for ordinary B-splines ( |
d |
B-spline order ( |
m |
penalty order ( |
gps |
if TRUE, return |
n |
number of samples to draw from the prior distribution. |
D |
matrix |
only.diag |
if TURE, only diagonal elements are computed. |
Details
General Difference Penalty for General P-Splines
A general P-spline is characterized by an order- general difference matrix
, which can be computed by
SparseD(..., gps = TRUE)
. For interpretation, the differenced coefficients are in fact
's B-spline coefficients, so the penalty is their squared
norm.
Derivative Penalty for O-Splines
An O-spline is characterized by such that
. Since
has B-spline coefficients
, the integral can be shown to be
, where
is the Gram matrix of those B-splines representing
. Following the Cholesky factorization
, the quadratic form becomes
, so that
. This matrix can be computed by
SparseD(..., gps = FALSE)
, with and
also returned in a "sandwich" attribute.
Penalty Matrix
We can express the penalty
as quadratic form
, where
is called a penalty matrix. It is trivial to compute
(using function
crossprod
) once is available, so we don't feel the need to provide a function for this. Note that the link between
and
implies a sandwich formula
, wherease
.
The Bayesian View
In the Bayesian view, the penalty is a Gaussian prior for B-spline coefficients
. But it is an improper one because
has a null space where an unpenalized order-
polynomial lies. Let's decompose
, where
(the projection of
on this null space) is the coefficients of this order-
polynomial, and
(orthogonal to
) is the component that can be shrunk to zero by the penalty. As a result,
is not proper, but
is. Function
PriorCoef
samples this distribution, and the resulting B-spline coefficients can be used to create random spline curves. The algorithm behind PriorCoef
bypasses the Moore-Penrose generalized inverse and is very efficient. We don't recommend forming this inverse matrix because it, being completely dense, is expensive to compute and store. But if we need it anyway, it can be computed using function MPinv
.
Value
SparseD
returns a list of sparse matrices (of "dgCMatrix" class), giving or
of order
m[1]
, m[2]
, ..., m[length(m)]
. In the latter case, (sparse matrices of "dsCMatrix" or "ddiMatrix" class) and
for computing
are also returned in a "sandwich" attribute.
PriorCoef
returns a list of two components:
-
coef
gives a vector of B-spline coefficients whenn = 1
, or a matrix ofn
columns whenn > 1
, where each column is an independent sample; -
sigma
is a vector, giving the marginal standard deviation for each B-spline coefficient.
MPinv
returns the dense Moore-Penrose generalized inverse matrix if
only.diag = FALSE
, and the diagonal entries of this matrix if only.diag = TRUE
.
Author(s)
Zheyuan Li zheyuan.li@bath.edu
References
Zheyuan Li and Jiguo Cao (2022). General P-splines for non-uniform splines, doi:10.48550/arXiv.2201.06808
Examples
require(Matrix)
require(gps)
## 11 domain knots at equal quantiles of Beta(3, 3) distribution
xd <- qbeta(seq.int(0, 1, by = 0.1), 3, 3)
## full knots (with clamped boundary knots) for constructing cubic B-splines
xt <- c(0, 0, 0, xd, 1, 1, 1)
## compute D matrices of order 1 to 3 for O-splines
D.os <- SparseD(xt, d = 4, gps = FALSE)
D1.os <- D.os[[1]]; D2.os <- D.os[[2]]; D3.os <- D.os[[3]]
## get D matrices of order 1 to 3 for general P-splines
## we can of course compute them with D.gps <- SparseD(xt, d = 4, gps = TRUE)
## but they are readily stored in the "sandwich" attribute of 'D.os'
D.gps <- attr(D.os, "sandwich")$D
D1.gps <- D.gps[[1]]; D2.gps <- D.gps[[2]]; D3.gps <- D.gps[[3]]
## we can compute the penalty matrix S = D'D
S.gps <- lapply(D.gps, crossprod)
S1.gps <- S.gps[[1]]; S2.gps <- S.gps[[2]]; S3.gps <- S.gps[[3]]
S.os <- lapply(D.os, crossprod)
S1.os <- S.os[[1]]; S2.os <- S.os[[2]]; S3.os <- S.os[[3]]
## if we want to verify the sandwich formula for O-splines
## extract 'Sbar' matrices stored in the "sandwich" attribute
## and compute the relative error between S and t(D) %*% Sbar %*% D
Sbar <- attr(D.os, "sandwich")$Sbar
Sbar1 <- Sbar[[1]]; Sbar2 <- Sbar[[2]]; Sbar3 <- Sbar[[3]]
range(S1.os - t(D1.gps) %*% Sbar1 %*% D1.gps) / max(abs(S1.os))
range(S2.os - t(D2.gps) %*% Sbar2 %*% D2.gps) / max(abs(S2.os))
range(S3.os - t(D3.gps) %*% Sbar3 %*% D3.gps) / max(abs(S3.os))
## sample B-spline coefficients from their prior distribution
b.gps <- PriorCoef(n = 5, D2.gps)$coef
b.os <- PriorCoef(n = 5, D2.os)$coef
op <- par(mfrow = c(1, 2), mar = c(2, 2, 1.5, 0.5), oma = c(0, 0, 1, 0))
## prior B-spline coefficients with a general difference penalty
matplot(b.gps, type = "l", lty = 1, ann = FALSE)
title("general difference penalty")
## prior B-spline coefficients with a derivative penalty
matplot(b.os, type = "l", lty = 1, ann = FALSE)
title("derivative penalty")
title("random B-spline coefficients from their prior", outer = TRUE)
par(op)
## plot the corresponding cubic splines with these B-spline coefficients
x <- MakeGrid(xd, n = 11)
B <- splines::splineDesign(xt, x, ord = 4, sparse = TRUE)
y.gps <- B %*% b.gps
y.os <- B %*% b.os
op <- par(mfrow = c(1, 2), mar = c(2, 2, 1.5, 0.5), oma = c(0, 0, 1, 0))
matplot(x, y.gps, type = "l", lty = 1, ann = FALSE)
title("general difference penalty")
matplot(x, y.os, type = "l", lty = 1, ann = FALSE)
title("derivative penalty")
title("random cubic splines with prior B-spline coefficients", outer = TRUE)
par(op)