VGAM (version 1.0-4)

sm.os: Defining O'Sullivan Spline Smooths in VGAM Formulas


This function represents an O-spline smooth term in a vgam formula and confers automatic smoothing parameter selection.


sm.os(x, ..., niknots = 6, spar = -1, o.order = 2,
      alg.niknots = c("s", ".nknots.smspl")[1], all.knots = FALSE,
      ridge.adj = 1e-5, spillover = 0.01, maxspar = 1e12,
      outer.ok = FALSE, fixspar = FALSE)



covariate (abscissae) to be smoothed. Also called the regressor. If the xij facility is used then these covariates are inputted via the argument.

Used to accommodate the other \(M-1\) covariates when the xij facility is used. See Section 3.4.4 of Yee (2015) for something very similar. This argument, found in the second argument, means that the other argument names must be fully specified if used, e.g., outer.ok and not outer. See the example below. In the example below, the term in the main formula is sm.os(gcost.air, gcost.trn, gcost.bus) and one might be tempted to use something like sm.os(gcost) to represent that xij term. However, this is not recommended because sm.os(gcost) might not have the same number of columns as sm.os(gcost.air, gcost.trn, gcost.bus) etc. That is, it is best to select one of the diagonal elements of the block matrix to represent that term.


numeric, the number of interior knots, called \(K\) below. The default is to use this value. If you want alg.niknots to operate then assign NULL to this argument.


character. The algorithm used to determine the number of interior knots. Only used when all.knots = FALSE and niknots = NULL. Note that ".nknots.smspl" corresponds to the default of smooth.spline. The value "s" corresponds to the same algorithm as s.


logical. If TRUE then all distinct points in x are used as the interior knots. If FALSE (default) then a subset of x[] is used, specifically x[j] where the niknots indices are quantiles that are evenly spaced with respect to the argument probs---see quantile. If all.knots = FALSE and niknots = NULL then the argument alg.niknots is used to compute niknots.

spar, maxspar

spar is a vector of smoothing parameters. Negative values mean that magic will choose initial values in order to do the optimization at each P-IRLS iteration. Positive values mean that they are used as initial values for magic. If fixspar = TRUE then spar should be assigned a vector of positive values (but having values less than maxspar); then the smoothing parameters will be fixed and magic will not be used.


The order of the O'Sullivan penalzed spline. Any one value from 1:4 is acceptable. The degree of the spline is 2 * o.order - 1, so that cubic splines are the default. Setting o.order = 1 results in a linear spline which is a piecewise linear function.


small positive number to stabilize linear dependencies among B-spline bases.


small and positive proportion of the range used on the outside of the boundary values. This defines the endpoints \(a\) and \(b\) that cover the data \(x_i\), i.e., we are interested in the interval \([a,b]\) which contains all the abscissae. The interior knots are strictly inside \((a,b)\).


Fed into the argument (by the same name) of splineDesign.


logical. If TRUE then spar should be a vector with positive values and the smoothing parameters are fixed at those values. If FALSE then spar contains the initial values for the smoothing parameters, and magic is called to determine (hopefully) some good values for the smoothing parameters.


A matrix with attributes that are (only) used by vgam. The number of rows of the matrix is length(x). The number of columns is a function of the number of interior knots \(K\) and the order of the O-spline \(m\): \(K+2m-1\). In code, this is niknots + 2 * o.order - 1, or using arguments, + degree - 1 (where should be more generally interpreted as the number of intervals. The formula is the same as It transpires then that sm.os and are very similar.


Being introduced into VGAM for the first time, this function (and those associated with it) should be used cautiously. Not all options are fully working or have been tested yet, and there are bound to be some bugs lurking around.


This function is currently used by vgam to allow automatic smoothing parameter selection based on O-splines to minimize an UBRE quantity. In contrast, s operates by having a prespecified amount of smoothing, e.g., its df argument. When the sample size is reasonably large this function is recommended over s also because backfitting is not required. This function therefore allows 2nd-generation VGAMs to be fitted (called G2-VGAMs, or Penalized-VGAMs).

This function should only be used with vgam. This function uses quantile to choose the knots, whereas chooses equally-spaced knots. As Wand and Ormerod (2008) write, in most situations the differences will be minor, but it is possible for problems to arise for either strategy by constructing certain regression functions and predictor variable distributions. Any differences between O-splines and P-splines tend to be at the boundaries. O-splines have natural boundary constraints so that the solution is linear beyond the boundary knots.

Some arguments in decreasing order of precedence are: all.knots, niknots, alg.niknots.

Unlike s, which is symbolic and does not perform any smoothing itself, this function does compute the penalized spline when used by vgam---it creates the appropriate columns of the model matrix. When this function is used within vgam, automatic smoothing parameter selection is implemented by calling magic after the necessary link-ups are done.

By default this function centres the component function. This function is also smart; it can be used for smart prediction (Section 18.6 of Yee (2015)). Automatic smoothing parameter selection is performed using performance-oriented iteration whereby an optimization problem is solved at each IRLS iteration.

This function works better when the sample size is large, e.g., when in the hundreds, say.


Wand, M. P. and Ormerod, J. T. (2008). On semiparametric regression with O'Sullivan penalized splines. Australian and New Zealand Journal of Statistics, 50(2): 179--198.

See Also

vgam,, s, smartpred,, summarypvgam, smooth.spline, splineDesign, bs, magic.


Run this code

# }
data("TravelMode", package = "AER")  # Need to install "AER" first
air.df <- subset(TravelMode, mode == "air")  # Form 4 smaller data frames
bus.df <- subset(TravelMode, mode == "bus")
trn.df <- subset(TravelMode, mode == "train")
car.df <- subset(TravelMode, mode == "car")
TravelMode2 <- data.frame(income     = air.df$income,
                          wait.air   = air.df$wait  - car.df$wait,
                          wait.trn   = trn.df$wait  - car.df$wait,
                          wait.bus   = bus.df$wait  - car.df$wait,
                          gcost.air  = air.df$gcost - car.df$gcost,
                          gcost.trn  = trn.df$gcost - car.df$gcost,
                          gcost.bus  = bus.df$gcost - car.df$gcost,
                          wait       = air.df$wait)  # Value is unimportant
TravelMode2$mode <- subset(TravelMode, choice == "yes")$mode  # The response
TravelMode2 <- transform(TravelMode2, incom.air = income, incom.trn = 0,
                                      incom.bus = 0)
TravelMode2 <- transform(TravelMode2,
                         junkx2 = runif(nrow(TravelMode2)))

tfit2 <-
  vgam(mode ~ sm.os(gcost.air, gcost.trn, gcost.bus) + ns(junkx2, 4) +
              sm.os(incom.air, incom.trn, incom.bus) + wait ,
       crit = "coef",
       multinomial(parallel = FALSE ~ 1), data = TravelMode2,
       xij = list(sm.os(gcost.air, gcost.trn, gcost.bus) ~
                  sm.os(gcost.air, gcost.trn, gcost.bus) +
                  sm.os(gcost.trn, gcost.bus, gcost.air) +
                  sm.os(gcost.bus, gcost.air, gcost.trn),
                  sm.os(incom.air, incom.trn, incom.bus) ~
                  sm.os(incom.air, incom.trn, incom.bus) +
                  sm.os(incom.trn, incom.bus, incom.air) +
                  sm.os(incom.bus, incom.air, incom.trn),
                  wait   ~  wait.air +  wait.trn +  wait.bus),
       form2 = ~  sm.os(gcost.air, gcost.trn, gcost.bus) +
                  sm.os(gcost.trn, gcost.bus, gcost.air) +
                  sm.os(gcost.bus, gcost.air, gcost.trn) +
                  wait +
                  sm.os(incom.air, incom.trn, incom.bus) +
                  sm.os(incom.trn, incom.bus, incom.air) +
                  sm.os(incom.bus, incom.air, incom.trn) +
                  junkx2 + ns(junkx2, 4) +
                  incom.air + incom.trn + incom.bus +
                  gcost.air + gcost.trn + gcost.bus +
                  wait.air +  wait.trn +  wait.bus)
par(mfrow = c(2, 2))
plot(tfit2, se = TRUE, lcol = "orange", scol = "blue", ylim = c(-4, 4))
# }

Run the code above in your browser using DataCamp Workspace