#' Prediction from a fitted FGAM model (this fixes a specific bug in `refund` comes directly from there)
#'
#' Takes a fitted \code{fgam}-object produced by \code{\link{fgam}} and produces predictions given a
#' new set of values for the model covariates or the original values used for the model fit.
#' Predictions can be accompanied by standard errors, based on the posterior distribution of the
#' model coefficients. This is a wrapper function for \code{\link{predict.gam}}()
#' @param object a fitted \code{fgam} object as produced by \code{\link{fgam}}
#' @param newdata a named list containing the values of the model covariates at which predictions
#' are required. If this is not provided then predictions corresponding to the original data are
#' returned. All variables provided to newdata should be in the format supplied to \code{\link{fgam}},
#' i.e., functional predictors must be supplied as matrices with each row corresponding to one
#' observed function. Index variables for the functional covariates are reused from the fitted model
#' object or alternatively can be supplied as attributes of the matrix of functional predictor values.
#' Any variables in the model not specified in newdata are set to their average values from the data
#' supplied during fitting the model
#' @param type character; see \code{\link{predict.gam}} for details
#' @param se.fit logical; see \code{\link{predict.gam}} for details
#' @param terms character see \code{\link{predict.gam}} for details
#' @param PredOutOfRange logical; if this argument is true then any functional predictor values in
#' newdata corresponding to \code{fgam} terms that are greater[less] than the maximum[minimum] of the
#' domain of the marginal basis for the rows of the tensor product smooth are set to the maximum[minimum]
#' of the domain. If this argument is false, attempting to predict a value of the functional predictor
#' outside the range of this basis produces an error
#' @param ... additional arguments passed on to \code{\link{predict.gam}}
#' @return If \code{type == "lpmatrix"}, the design matrix for the supplied covariate values in long
#' format. If \code{se == TRUE}, a list with entries fit and se.fit containing fits and standard errors,
#' respectively. If \code{type == "terms" or "iterms"} each of these lists is a list of matrices of the
#' same dimension as the response for newdata containing the linear predictor and its se for each term
#' @author Mathew W. McLean \email{mathew.w.mclean@@gmail.com} and Fabian Scheipl
#' @seealso \code{\link{fgam}}, \code{\link[mgcv]{predict.gam}}
#' @examples
#' ######### Octane data example #########
#' data(gasoline)
#' N <- length(gasoline$octane)
#' wavelengths = 2*450:850
#' nir = matrix(NA, 60,401)
#' test <- sample(60,20)
#' for (i in 1:60) nir[i,] = gasoline$NIR[i, ] # changes class from AsIs to matrix
#' y <- gasoline$octane
#' fit <- fgam(y~af(nir,xind=wavelengths,splinepars=list(k=c(6,6),m=list(c(2,2),c(2,2)))),
#' subset=(1:N)[-test])
#' preds <- predict(fit,newdata=list(nir=nir[test,]),type='response')
#' plot(preds,y[test])
#' abline(a=0,b=1)
#' @importFrom mgcv predict.gam
#' @importFrom splines spline.des
#' @export
predict.fgam <- function (object, newdata, type = "response", se.fit = FALSE,
terms = NULL, PredOutOfRange = FALSE, ...)
{
# browser()
call <- match.call()
string <- NULL
if (!missing(newdata)) {
nobs <- nrow(as.matrix(newdata[[1]]))
stopifnot(length(unique(sapply(newdata, function(x) ifelse(is.matrix(x),
nrow(x), length(x))))) == 1)
gamdata <- list()
varmap <- sapply(names(object$fgam$labelmap), function(x) all.vars(formula(paste("~",
x))))
for (cov in names(newdata)) {
trms <- which(sapply(varmap, function(x) any(grep(paste("^",
cov, "$", sep = ""), x))))
J <- ncol(as.matrix(newdata[[cov]]))
if (length(trms) != 0) {
for (trm in trms) {
is.af <- trm %in% object$fgam$where$where.af
is.lf <- trm %in% object$fgam$where$where.lf
if (is.af) {
af <- object$fgam$ft[[grep(paste(cov, "[,\\)]",
sep = ""), names(object$fgam$ft))]]
if(J!=length(af$xind) & type!='lpmatrix'){
stop(paste('Provided data for functional covariate',cov,'does not have same observation times as original data',sep=''))
}
L <- matrix(af$L[1, ], nobs, J, byrow = T)
tmat <- matrix(af$xind,nobs,J,byrow=TRUE)
if (grepl(paste(cov, "\\.[ot]mat", sep = ""),
deparse(af$call$x))) {
if (length(attr(newdata, "L"))) {
if(type!='lpmatrix'){
warning('Supplying new L matrix of quadrature weights only implemented for type=\'lpmatrix\' and supplied L will be ignored')
}else{
if (sum(dim(as.matrix(attr(newdata, "L")))==dim(as.matrix(newdata[[cov]])))!=2) {
warning(paste('Supplied L matrix for',cov,'is not the same dimension as the matrix of observations and will be ignored',sep=''))
}else{
L <- as.vector(attr(newdata, "L"))
}
}
}
if (length(attr(newdata, "tmat"))) {
if(type!='lpmatrix'){
warning('Supplying new tmat matrix of observation times only implemented for type=\'lpmatrix\' and supplied L will be ignored')
}else{
if (sum(dim(as.matrix(attr(newdata, "tmat")))==dim(as.matrix(newdata[[cov]])))!=2) {
warning(paste('Supplied tmat matrix for',cov,'is not the same dimension as the matrix of observations and will be ignored',sep=''))
}else{
tmat <- as.vector(attr(newdata, "tmat"))
}
}
}
if (PredOutOfRange) {
newdata[[cov]][newdata[[cov]]>af$Xrange[2]] <- af$Xrange[2]
newdata[[cov]][newdata[[cov]]<af$Xrange[1]] <- af$Xrange[1]
}
if (!is.null(af$presmooth)) {
if(type=='lpmatrix' & J!=length(af$xind)){
warning('Presmoothing of new functional covariates is only implemented for when new covariates observed at same time points as original data. No presmoothing of new covariates done.')
} else if (is.logical(af$presmooth)) {
# af_old term
if (af$presmooth) {
newXfd <- fd(tcrossprod(af$Xfd$y2cMap,
newdata[[cov]]), af$Xfd$basis)
newdata[[cov]] <- t(eval.fd(af$xind, newXfd))
}
} else {
# af term
newdata[[cov]] <- af$prep.func(newX = newdata[[cov]])$processed
}
}
# if (af$Qtransform) {
# if(type=='lpmatrix' & J!=length(af$xind)){
# stop('Prediction with quantile transformation only implemented for when new data observation times match original data observation times')
# }
# for (i in 1:nobs) {
# newdata[[cov]][i, ] <- mapply(function(tecdf,
# x) {
# tecdf(x)
# }, tecdf = af$ecdflist, x = newdata[[cov]][i,
# ])
# }
# }
if(type=='lpmatrix') newdata[[cov]] <- as.vector(newdata[[cov]])
gamdata[[paste(cov, ".omat", sep = "")]] <- newdata[[cov]]
gamdata[[paste(cov, ".tmat", sep = "")]] <- tmat
gamdata[[paste("L.", cov, sep = "")]] <- L
}
}
if (is.lf) {
lf <- object$fgam$ft[[grep(paste(cov, "[,\\)]",
sep = ""), names(object$fgam$ft))]]
if(J!=length(lf$xind) & type!='lpmatrix'){
stop(paste('Provided data for functional covariate',cov,'does not have same observation times as original data',sep=''))
}
L <- matrix(lf$L[1, ], nobs, J, byrow = T)
tmat <- matrix(lf$xind,nobs,J,byrow=TRUE)
if (grepl(paste(cov, "\\.[t]mat", sep = ""),
deparse(lf$call$x))) {
if (length(attr(newdata, "L"))) {
if(type!='lpmatrix'){
warning('Supplying new L matrix of quadrature weights only implemented for type=\'lpmatrix\' and supplied L will be ignored')
}else{
if (sum(dim(as.matrix(attr(newdata, "L")))==dim(as.matrix(newdata[[cov]])))!=2) {
warning(paste('Supplied L matrix for',cov,'is not the same dimension as the matrix of observations and will be ignored',sep=''))
}else{
L <- as.vector(attr(newdata, "L"))
}
}
}
if (length(attr(newdata, "tmat"))) {
if(type!='lpmatrix'){
warning('Supplying new tmat matrix of observation times only implemented for type=\'lpmatrix\' and supplied L will be ignored')
}else{
if (sum(dim(as.matrix(attr(newdata, "tmat")))==dim(as.matrix(newdata[[cov]])))!=2) {
warning(paste('Supplied tmat matrix for',cov,'is not the same dimension as the matrix of observations and will be ignored',sep=''))
}else{
tmat <- as.vector(attr(newdata, "tmat"))
}
}
}
if (!is.null(lf$presmooth)) {
if(type=='lpmatrix' & J!=length(lf$xind)){
warning('Presmoothing of new functional covariates is only implemented for when new covariates observed at same time points as original data. No presmoothing of new covariates done.')
} else if (is.logical(lf$presmooth)) {
# lf_old term
if (lf$presmooth) {
newXfd <- fd(tcrossprod(lf$Xfd$y2cMap,
newdata[[cov]]), lf$Xfd$basis)
newdata[[cov]] <- t(eval.fd(lf$xind, newXfd))
}
} else {
# lf() term
newdata[[cov]] <- lf$prep.func(newX = newdata[[cov]])$processed
}
}
if(type=='lpmatrix') newdata[[cov]] <- as.vector(newdata[[cov]])
gamdata[[paste(cov, ".tmat", sep = "")]] <- tmat
gamdata[[paste("L.", cov, sep = "")]] <- L *
newdata[[cov]]
}
}
if (!(is.af || is.lf)) {
gamdata[[cov]] <- drop(newdata[[cov]])
}
}
}
}
gamdata <- list2df(gamdata)
call[["newdata"]] <- gamdata
}
else {
call$newdata <- eval(call$newdata)
nobs <- object$fgam$nobs
}
if (PredOutOfRange) {
suppressMessages(trace(splines::spline.des, at = 2, quote({
outer.ok <- TRUE
}), print = FALSE))
on.exit(suppressMessages(try(untrace(splines::spline.des),
silent = TRUE)))
}
oterms <- terms
if (type %in% c("terms", "iterms") & !all(terms %in% c(names(object$smooth),
attr(object$pterms, "term.labels")))) {
if (terms %in% unlist(varmap)) {
tnames <- c(names(object$smooth), attr(object$pterms,
"term.labels"))
terms <- tnames[grep(paste(string, "[,\\.\\)]", sep = ""),
tnames)]
}
else {
stop("Invalid terms specified")
}
}
call[[1]] <- mgcv::predict.gam
call$object <- as.name("object")
call$terms <- terms
res <- eval(call)
if (type %in% c("terms", "iterms"))
colnames(res) <- oterms
return(res)
}
list2df <- function(l){
# make a list into a dataframe -- matrices are left as matrices!
nrows <- sapply(l, function(x) nrow(as.matrix(x)))
stopifnot(length(unique(nrows)) == 1)
ret <- data.frame(rep(NA, nrows[1]))
for(i in 1:length(l)) ret[[i]] <- l[[i]]
names(ret) <- names(l)
return(ret)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.