blob: e66b95eb8dd023607c5e515235af15f653605bf3 [file] [log] [blame]
# File src/library/stats/R/ppr.R
# Part of the R package, https://www.R-project.org
#
# Copyright (C) 1998 B. D. Ripley
# Copyright (C) 2000-2016 The R Core Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available at
# https://www.R-project.org/Licenses/
ppr <- function(x, ...) UseMethod("ppr")
ppr.formula <-
function(formula, data, weights, subset,
na.action, contrasts = NULL, ..., model = FALSE)
{
call <- match.call()
m <- match.call(expand.dots = FALSE)
m$contrasts <- m$... <- NULL
## need stats:: for non-standard evaluation
m[[1L]] <- quote(stats::model.frame)
m <- eval(m, parent.frame())
Terms <- attr(m, "terms")
attr(Terms, "intercept") <- 0L
X <- model.matrix(Terms, m, contrasts)
Y <- model.response(m)
w <- model.weights(m)
if(length(w) == 0L) w <- rep_len(1, nrow(X))
fit <- ppr.default(X, Y, w, ...)
fit$na.action <- attr(m, "na.action")
fit$terms <- Terms
## fix up call to refer to the generic, but leave arg name as `formula'
call[[1L]] <- as.name("ppr")
fit$call <- call
fit$contrasts <- attr(X, "contrasts")
fit$xlevels <- .getXlevels(Terms, m)
if(model) fit$model <- m
structure(fit, class=c("ppr.form", "ppr"))
}
ppr.default <-
function(x, y, weights=rep(1,n), ww=rep(1,q), nterms, max.terms=nterms,
optlevel=2, sm.method=c("supsmu", "spline", "gcvspline"),
bass=0, span=0, df=5, gcvpen=1, trace = FALSE, ...)
{
call <- match.call()
call[[1L]] <- as.name("ppr")
sm.method <- match.arg(sm.method)
ism <- switch(sm.method, supsmu = 0L, spline = 1L, gcvspline = 2L)
if(trace) ism <- -(ism + 1L)
if(missing(nterms)) stop("'nterms' is missing with no default")
mu <- nterms; ml <- max.terms
x <- as.matrix(x)
y <- as.matrix(y)
if(!is.numeric(x) || !is.numeric(y))
stop("'ppr' applies only to numerical variables")
n <- nrow(x)
if(nrow(y) != n) stop("mismatched 'x' and 'y'")
p <- ncol(x)
q <- ncol(y)
xnames <- if(!is.null(dimnames(x))) dimnames(x)[[2L]] else paste0("X", 1L:p)
ynames <- if(!is.null(dimnames(y))) dimnames(y)[[2L]] else paste0("Y", 1L:q)
msmod <- ml*(p+q+2*n)+q+7+ml+1 # for asr
nsp <- n*(q+15)+q+3*p
ndp <- p*(p+1)/2+6*p
.Fortran(C_setppr,
as.double(span), as.double(bass), as.integer(optlevel),
as.integer(ism), as.double(df), as.double(gcvpen)
)
Z <- .Fortran(C_smart,
as.integer(ml), as.integer(mu),
as.integer(p), as.integer(q), as.integer(n),
as.double(weights),
as.double(t(x)),
as.double(t(y)),
as.double(ww),
smod=double(msmod), as.integer(msmod),
double(nsp), as.integer(nsp),
double(ndp), as.integer(ndp),
edf=double(ml)
)
smod <- Z$smod
ys <- smod[q+6]
tnames <- paste("term", 1L:mu)
alpha <- matrix(smod[q+6L + 1L:(p*mu)],p, mu,
dimnames=list(xnames, tnames))
beta <- matrix(smod[q+6L+p*ml + 1L:(q*mu)], q, mu,
dimnames=list(ynames, tnames))
fitted <- drop(matrix(.Fortran(C_pppred,
as.integer(nrow(x)),
as.double(x),
as.double(smod),
y = double(nrow(x)*q),
double(2*smod[4L]))$y,
ncol=q, dimnames=dimnames(y)))
jt <- q + 7 + ml*(p+q+2*n)
gof <- smod[jt] * n * ys^2
gofn <- smod[jt+1L:ml] * n * ys^2
## retain only terms for the size of model finally fitted
jf <- q+6+ml*(p+q)
smod <- smod[c(1L:(q+6+p*mu), q+6+p*ml + 1L:(q*mu),
jf + 1L:(mu*n), jf+ml*n + 1L:(mu*n))]
smod[1L] <- mu
structure(list(call=call, mu=mu, ml=ml, p=p, q=q,
gof=gof, gofn=gofn,
df=df, edf=Z$edf[1L:mu],
xnames=xnames, ynames=ynames,
alpha=drop(alpha), beta=ys*drop(beta),
yb=smod[5+1L:q], ys=ys,
fitted.values=fitted, residuals=drop(y-fitted),
smod=smod),
class="ppr")
}
print.ppr <- function(x, ...)
{
if(!is.null(cl <- x$call)) {
cat("Call:\n")
dput(cl, control=NULL)
}
mu <- x$mu; ml <- x$ml
cat("\nGoodness of fit:\n")
gof <- setNames(x$gofn, paste(1L:ml, "terms"))
print(format(gof[mu:ml], ...), quote=FALSE)
invisible(x)
}
summary.ppr <- function(object, ...)
{
class(object) <- "summary.ppr"
object
}
print.summary.ppr <- function(x, ...)
{
print.ppr(x, ...)
mu <- x$mu
cat("\nProjection direction vectors ('alpha'):\n")
print(format(x$alpha, ...), quote=FALSE)
cat("\nCoefficients of ridge terms ('beta'):\n")
print(format(x$beta, ...), quote=FALSE)
if(any(x$edf >0)) {
cat("\nEquivalent df for ridge terms:\n")
edf <- setNames(x$edf, paste("term", 1L:mu))
print(round(edf,2), ...)
}
invisible(x)
}
plot.ppr <- function(x, ask, type = "o", cex = 1/2,
main = quote(bquote(
"term"[.(i)]*":" ~~ hat(beta[.(i)]) == .(bet.i))),
xlab = quote(bquote(bold(alpha)[.(i)]^T * bold(x))),
ylab = "", ...)
{
ppr.funs <- function(obj)
{
## cols for each term
p <- obj$p; q <- obj$q
sm <- obj$smod
n <- sm[4L]; mu <- sm[5L]; m <- sm[1L]
jf <- q+6+m*(p+q)
jt <- jf+m*n
f <- matrix(sm[jf+1L:(mu*n)],n, mu)
t <- matrix(sm[jt+1L:(mu*n)],n, mu)
list(x=t, y=f)
}
obj <- ppr.funs(x)
if(!missing(ask)) {
oask <- devAskNewPage(ask)
on.exit(devAskNewPage(oask))
}
for(i in 1L:x$mu) {
ord <- order(obj$x[ ,i])
bet.i <- format(x$beta[[i]], digits = 3)
plot(obj$x[ord, i], obj$y[ord, i], type = type, cex = cex,
main = if(is.call(main)) eval(main) else main,
xlab = if(is.call(xlab)) eval(xlab) else xlab,
ylab = ylab, ...)
}
force(bet.i)# codetools
invisible()
}
predict.ppr <- function(object, newdata, ...)
{
if(missing(newdata)) return(fitted(object))
if(!is.null(object$terms)) {
newdata <- as.data.frame(newdata)
rn <- row.names(newdata)
# work hard to predict NA for rows with missing data
Terms <- delete.response(object$terms)
m <- model.frame(Terms, newdata, na.action = na.omit,
xlev = object$xlevels)
if(!is.null(cl <- attr(Terms, "dataClasses"))) .checkMFClasses(cl, m)
keep <- match(row.names(m), rn)
x <- model.matrix(Terms, m, contrasts.arg = object$contrasts)
} else {
x <- as.matrix(newdata)
keep <- seq_len(nrow(x))
rn <- dimnames(x)[[1L]]
}
if(ncol(x) != object$p) stop("wrong number of columns in 'x'")
res <- matrix(NA, length(keep), object$q,
dimnames = list(rn, object$ynames))
res[keep, ] <- matrix(.Fortran(C_pppred,
as.integer(nrow(x)),
as.double(x),
as.double(object$smod),
y = double(nrow(x)*object$q),
double(2*object$smod[4L])
)$y, ncol=object$q)
drop(res)
}