Nothing
#' @name RRPP-package
#' @docType package
#' @aliases RRPP
#' @title Linear Model Evaluation with Randomized Residual Permutation Procedures
#' @author Michael Collyer and Dean Adams
#' @return Key functions for this package:
#' \item{\code{\link{lm.rrpp}}}{Fits linear models, using RRPP.
#' plus model comparisons.}
#' \item{\code{\link{coef.lm.rrpp}}}{Extract coefficients or perform test
#' on coefficients, using RRPP.}
#' \item{\code{\link{predict.lm.rrpp}}}{Predict values from lm.rrpp fits
#' and generate bootstrapped confidence intervals.}
#' \item{\code{\link{pairwise}}}{Perform pairwise tests, based on lm.rrpp
#' model fits.}
#'
#' @description Functions in this package allow one to evaluate linear models
#' with residual randomization.
#' The name, "RRPP", is an acronym for, "Randomization of Residuals in a
#' Permutation Procedure." Through
#' the various functions in this package, one can use randomization of
#' residuals to generate empirical probability
#' distributions for linear model effects, for high-dimensional data or
#' distance matrices.
#'
#' An especially useful option of this package is to fit models with
#' either ordinary or generalized
#' least squares estimation (OLS or GLS, respectively), using theoretic
#' covariance matrices. Mixed linear
#' effects can also be evaluated.
#'
#' @import parallel
#' @import ggplot2
#' @import Matrix
#' @importFrom ape multi2di.phylo
#' @importFrom ape root.phylo
#' @importFrom ape collapse.singles
#' @importFrom stats na.omit anova as.dist as.formula cmdscale coef delete.response dist formula lm
#' lm.fit lm.wfit loess logLik model.frame.default model.matrix optimise prcomp qnorm quantile
#' resid sd spline var
#' @importFrom graphics abline arrows axis legend lines par plot.default points text title
#' @importFrom utils combn object.size setTxtProgressBar txtProgressBar
#' @export print.lm.rrpp
#' @export summary.lm.rrpp
#' @export print.summary.lm.rrpp
#' @export print.anova.lm.rrpp
#' @export summary.anova.lm.rrpp
#' @export print.coef.lm.rrpp
#' @export summary.coef.lm.rrpp
#' @export print.predict.lm.rrpp
#' @export summary.predict.lm.rrpp
#' @export plot.lm.rrpp
#' @export plot.predict.lm.rrpp
#' @export print.pairwise
#' @export summary.pairwise
#' @export print.summary.pairwise
#' @export print.trajectory.analysis
#' @export summary.trajectory.analysis
#' @export print.summary.trajectory.analysis
NULL
#' @section RRPP TOC:
#' RRPP-package
NULL
#' Landmarks on pupfish
#'
#' @name Pupfish
#' @docType data
#' @author Michael Collyer
#' @keywords datasets
#' @description Landmark data from Cyprinodon pecosensis body shapes, with
#' indication of Sex and
#' Population from which fish were sampled (Marsh or Sinkhole).
#' @details These data were previously aligned with GPA. Centroid size (CS)
#' is also provided.
#' See the \pkg{geomorph} package for details.
#'
#' @references Collyer, M.L., D.J. Sekora, and D.C. Adams. 2015. A method for
#' analysis of phenotypic
#' change for phenotypes described by high-dimensional data. Heredity. 113:
#' doi:10.1038/hdy.2014.75.
NULL
#' Landmarks on pupfish heads
#'
#' @name PupfishHeads
#' @docType data
#' @author Michael Collyer
#' @description Landmark data from Cyprinodon pecosensis head shapes, with
#' variables for
#' sex, month and year sampled, locality, head size, and coordinates of
#' landmarks for head shape,
#' per specimen. These data are a subset of a larger data set.
#' @details The variable, "coords", are data that were previously aligned
#' with GPA. The variable, "headSize", is the Centroid size of each vector
#' of coordinates.
#' See the \pkg{geomorph} package for details.
#' @references Gilbert, M.C. 2016. Impacts of habitat fragmentation on the
#' cranial morphology of a
#' threatened desert fish (Cyprinodon pecosensis). Masters Thesis,
#' Western Kentucky University.
NULL
#' Plethodon comparative morphological data
#'
#' @name PlethMorph
#' @docType data
#' @author Michael Collyer and Dean Adams
#' @keywords datasets
#' @description Data for 37 species of plethodontid salamanders.
#' Variables include snout to vent length
#' (SVL) as species size, tail length, head length, snout to eye length,
#' body width, forelimb length,
#' and hind limb length, all measured in mm. A grouping variable is also
#' included for functional guild size. A variable for species names is also
#' included.
#' The data set also includes a phylogenetic covariance matrix based on a
#' Brownian model of evolution, to assist in
#' generalized least squares (GLS) estimation.
#' @details The covariance matrix was estimated with the vcv.phylo function
#' of the R package, ape, based on the tree
#' described in Adams and Collyer (2018).
#' @references Adams, D.C and Collyer, M.L. 2018. Multivariate phylogenetic
#' anova: group-clade aggregation, biological
#' challenges, and a refined permutation procedure. Evolution, 72: 1204-1215.
NULL
#' Simulated motion paths
#'
#' @name motionpaths
#' @docType data
#' @author Dean Adams
#' @references Adams, D. C., and M. L. Collyer. 2009. A general framework for
#' the analysis of phenotypic
#' trajectories in evolutionary studies. Evolution 63:1143-1154.
#' @keywords datasets
NULL
#####----------------------------------------------------------------------------------------------------
# HELP FUNCTIONs
#' Create a data frame for lm.rrpp analysis
#'
#' Create a data frame for lm.rrpp analysis, when covariance or distance
#' matrices are used
#'
#' This function is not much different than \code{\link{data.frame}} but is
#' more flexible to allow
#' distance matrices and covariance matrices to be included. Essentially,
#' this function creates a list,
#' much like an object of class \code{data.frame} is also a list. However,
#' \code{rrpp.data.frame} is
#' less concerned with coercing the list into a matrix and more concerned
#' with matching the number of observations (n).
#' It is wise to use this function with any \code{lm.rrpp} analysis so that
#' \code{\link{lm.rrpp}} does not have to search
#' the global environment for needed data.
#'
#' It is assumed that multiple data sets for the same subjects are in the
#' same order.
#'
#' See \code{\link{lm.rrpp}} for examples.
#'
#' @param ... Components (objects) to combine in the data frame.
#' @export
#' @author Michael Collyer
#' @examples
#' # Why use a rrpp.data.frame?
#' y <- matrix(rnorm(30), 10, 3)
#' x <- rnorm(10)
#' df <- data.frame(x = x, y = y)
#' df
#' rdf <- rrpp.data.frame(x = x, y = y)
#' rdf # looks more like a list
#'
#' is.list(df)
#' is.list(rdf)
#'
#' d <- dist(y) # distance matrix as data
#'
#' # One can try this but it will result in an error
#' # df <- data.frame(df, d = d)
#' rdf <- rrpp.data.frame(rdf, d = d) # works
#'
#' fit <- lm.rrpp(d ~ x, data = rdf)
#' summary(fit)
rrpp.data.frame<- function(...) {
dots <- list(...)
if(length(dots) == 1 && is.data.frame(dots[[1]])) {
dots <- dots[[1]]
class(dots) <- "rrpp.data.frame"
} else if(length(dots) == 1 && inherits(dots[[1]], "geomorph.data.frame")) {
dots <- dots[[1]]
warning(
paste(
"\nThis is not an error! It is a friendly warning.\n",
"\nSome geomorph.data.frame objects might not be compatible with RRPP functions.",
"\nIf any part of the geomorph.data.frame conatins a 3D array,",
"consider converting it to a matrix before attempting to make an rrpp.data.frame\n",
"Use options(warn = -1) to turn off these warnings.\n\n", sep = " "),
noBreaks. = TRUE, call. = FALSE, immediate. = TRUE)
class(dots) <- "rrpp.data.frame"
} else if(length(dots) == 1 && inherits(dots[[1]], "rrpp.data.frame")) {
dots <- dots[[1]]
} else {
if(length(dots) > 1 && inherits(dots[[1]], "rrpp.data.frame")) {
dots1 <- dots[[1]]
dots2 <- dots[-1]
dots <- c(dots1, dots2)
}
N <- length(dots)
dots.ns <- array(NA,N)
for(i in 1:N){
if(is.array(dots[[i]])) {
if(length(dim(dots[[i]])) == 3) dots.ns[i] <- dim(dots[[i]])[[3]]
if(length(dim(dots[[i]])) == 2) dots.ns[i] <- dim(dots[[i]])[[2]]
if(length(dim(dots[[i]])) == 1) dots.ns[i] <- dim(dots[[i]])[[1]]
}
if(inherits(dots[[i]], "matrix")) {
dots.ns[i] <- dim(dots[[i]])[[1]]
dt.nms <- rownames(dots[[i]])
if(dots.ns[i] == length(dots[[i]])) {
dots[[i]] <- as.vector(dots[[i]])
names(dots[[i]]) <- dt.nms
}
}
if(inherits(dots[[i]], "dist")) dots.ns[i] <- attr(dots[[i]], "Size")
if(is.data.frame(dots[[i]])) dots.ns[i] <- dim(dots[[i]])[[1]]
if(is.vector(dots[[i]])) dots.ns[i] <- length(dots[[i]])
if(is.factor(dots[[i]])) dots.ns[i] <- length(dots[[i]])
if(is.logical(dots[[i]])) dots.ns[i] <- length(dots[[i]])
}
if(any(is.na(dots.ns)))
stop("Some input is either dimensionless or inappropriate for data frames")
if(length(unique(dots.ns)) > 1)
stop("Inputs have different numbers of observations")
class(dots) <- c("rrpp.data.frame")
}
dots
}
#####--------------------------------------------------------------\
# SUPPORT FUNCTIONS
# Parallel.setup
# Function to help parallel library adjust to settings
# for parallel processing
Parallel.setup <- function(Parallel){
ParLog <- is.logical(Parallel)
usecluster <- inherits(Parallel, "cluster")
if(usecluster){
cluster <- Parallel
ParLog <- Parallel <- TRUE
} else cluster <- FALSE
ParCores <- NULL
if(is.numeric(Parallel)) {
ParCores <- Parallel
ParLog <- TRUE
Parallel <- TRUE
}
if(ParLog && is.null(ParCores)) {
ParCores <- detectCores() - 1
if(usecluster) ParCores <- length(cluster)
if(!Parallel) ParCores <- 1
}
if(is.numeric(ParCores)) {
if(ParCores > detectCores() - 1) ParCores <- detectCores() - 1
}
Unix <- .Platform$OS.type == "unix"
forking <- Unix && !usecluster
if(is.null(ParCores)) ParCores <- 1
if(ParCores == 1) {
ParLog <- FALSE
forking <- FALSE
usecluster <- FALSE
cluster <- NULL
}
if(ParCores > 1) {
if(!Unix && !usecluster)
cluster <- makeCluster(ParCores)
if(Unix && usecluster)
Unix <- FALSE
}
list(Parallel = ParLog,
Unix = Unix,
forking = forking,
ParCores = ParCores,
usecluster = usecluster,
cluster = cluster)
}
# lm.rrpp subfunctions
# lm-like fit modified for all submodels
# general workhorse for all 'lm.rrpp' functions
get.names.from.list <- function(L) {
temp <- lapply(L, get.names)
check <- which(!sapply(temp, is.null))
temp <- if(length(check) > 0) temp[check] else NULL
nms <- if(!is.null(temp)) temp[[1]] else NULL
nms
}
add.names <- function(Y, nms) {
if(is.vector(Y)) names(Y) <- nms
if(inherits(Y, "matrix")) rownames(Y) <- nms
if(inherits(Y, "dist")) attr(Y, "Labels") <- nms
Y
}
makeDF <- function(form, data, n, nms) {
if(!is.list(data))
stop("\nThe data frame provide is not class rrpp.data.frame,
data.frame, or list, and therefore, unusable.\n", call. = FALSE)
dat <- data
class(dat) <- "list"
form <- try(as.formula(form), silent = TRUE)
if(inherits(form, "try-error"))
stop("Formula is not coercible into a formula object.
Please fix the formula.\n",
call. = FALSE)
var.names <- if(length(form) == 3) all.vars(form)[-1] else if(length(form) == 2)
all.vars(form) else
stop("\nFormula is not appropriately formatted.\n", call. = FALSE)
dat <- dat[names(dat) %in% var.names]
if(length(dat) > 0) {
var.check <- sapply(seq_len(length(dat)), function(j) {
x <- dat[[j]]
nn <- if(is.matrix(x)) nrow(x) else length(x)
nn == n
})
if(any(!var.check))
stop("One or more independent variables does not match the number
of observations in the data.\n",
call. = FALSE)
}
dat <- if(length(dat) == 0) NULL else as.data.frame(dat)
if(!is.null(dat)) rownames(dat) <- nms
dat
}
lm.args.from.formula <- function(cl){
lm.args <- list(formula = NULL, data = NULL, subset = NULL, weights = NULL,
na.action = na.omit, method = "qr", model = TRUE,
qr = TRUE,
singular.ok = TRUE, contrasts = NULL, offset = NULL, tol = 1e-7)
lm.nms <- names(lm.args)
m1 <- match(names(cl), lm.nms)
m2 <- match(lm.nms, names(cl))
lm.args[na.omit(m1)] <- cl[na.omit(m2)]
lm.args$x <- lm.args$y <- TRUE
form <- lm.args$formula
if(is.null(form))
stop("The formula is either missing or not formatted correctly.\n",
call. = FALSE)
Dy <- NULL
Y <- try(eval(lm.args$formula[[2]], lm.args$data, parent.frame()),
silent = TRUE)
nmsY <- get.names(Y)
if(inherits(Y, "try-error"))
stop("Data are missing from either the data frame or global environment.\n",
call. = FALSE)
if(is.vector(Y)) {
Y <- as.matrix(Y)
Dy <- NULL
}
if(inherits(Y, "matrix") || is.data.frame(Y)) {
if(isSymmetric(as.matrix(Y))) {
Dy <- Y <- as.dist(Y)
if(any(Dy < 0)) stop("Distances in distance matrix cannot be less than 0\n",
call. = FALSE)
lm.args$formula <- update(lm.args$formula, Y ~ .)
} else Dy <- NULL
}
if(inherits(Y, "dist")) {
if(any(Y < 0)) stop("Distances in distance matrix cannot be less than 0")
Dy <- Y
Y <- pcoa(Y)
}
if(is.array(Y) && length(dim(Y)) > 2)
stop("Data are arranged in an array rather than a matrix.
Please convert data to a matrix. \n",
call. = FALSE)
if(form[[3]] == ".") {
xs <- paste(names(lm.args$data), collapse = "+")
form <- as.formula(noquote(c("~", xs)))
}
form <- update(form, Y ~.,)
lm.args$formula <- form
n <- NROW(Y)
if(!is.null(lm.args$data)) {
nmsDF <- if(inherits(lm.args$data, "data.frame"))
attr(lm.args$data, "row.names") else
get.names.from.list(lm.args$data)
lm.args$data <- makeDF(form, lm.args$data, n, nms = NULL)
}
if(is.null(lm.args$data)) {
lm.args$data <- data.frame(Int = rep(1, n))
lm.args$data$Y <- as.matrix(Y)
lm.args$data <- lm.args$data[-1]
}
lm.args$data$Y <- Y
dfmat <- try(as.matrix(lm.args$data), silent = TRUE)
if(!inherits(dfmat, "try-error"))
f <- try(do.call(lm, lm.args), silent = TRUE)
if(inherits(f, "try-error"))
stop("Variables or data might be missing from either the data frame or
global environment, or a linear model fit just does not work...\n",
call. = FALSE)
Y <- as.matrix(f$y)
Y <- add.names(Y, nmsY)
out <- list(Terms = f$terms, model = f$model, Y = Y)
if(!is.null(Dy)) {
d <- as.matrix(Dy)
if(nrow(d) != NROW(out$Y)) d <- d[rownames(Y), rownames(Y)]
out$D <- as.dist(d)
}
out
}
.getTerms <- function(fit = NULL, Terms = NULL, SS.type = NULL) {
if(is.null(Terms)) Terms <- fit$LM$Terms
if(is.null(SS.type)) SS.type <- fit$ANOVA$SS.type
trms <- attr(Terms, "term.labels")
k <- length(trms)
mod.k <- if(k > 0) c(0, seq(1, k, 1)) else 0
if(!is.null(fit) && SS.type == "Within-subject type II") {
SS.type <- "II"
WS <- TRUE
} else WS <- FALSE
if(k > 0) {
if(SS.type == "III"){
k3 <- mod.k[-1]
modf <- lapply(as.list(k3), function(.) Terms)
modr <- lapply(as.list(k3), function(j) Terms[-j])
} else if(SS.type == "II"){
k2 <- mod.k[-1]
fac <- crossprod(attr(Terms, "factor"))
modr <- lapply(as.list(k2), function(j){
ind <- as.logical(ifelse(fac[j,] < fac[j,j], 1, 0))
Terms[ind]
})
modf <- lapply(as.list(k2), function(j){
ind <- ifelse(fac[j,] < fac[j,j], 1, 0)
ind[j] <- 1
ind <- as.logical(ind)
Terms[ind]
})
} else {
kf <- mod.k[-1]
kr <- mod.k[-(max(mod.k) + 1)]
modf <- lapply(as.list(kf), function(j) Terms[1:j])
modr <- lapply(as.list(kr), function(j) Terms[0:j])
}
names(modf) <- names(modr) <- trms
if(WS) {
trms.change <- which(trms == fit$subjects.var)
modf[[trms.change]] <- Terms
modr[[trms.change]] <- Terms[!trms %in% fit$subjects.var]
}
} else {
modr <- modf <- list("Intercept" = Terms)
}
list(terms.r = modr, terms.f = modf)
}
LM.fit <- function(x, y, offset = NULL, tol = 1e-07) {
if(inherits(x, "matrix"))
x.s <- Matrix(x, sparse = TRUE) else {
x.s <- x
x <- as.matrix(x.s)
}
osx <- object.size(x)
osxs <- object.size(x.s)
X <- if(osx < osxs) x else x.s
x <- x.s <- NULL
Q <- qr(X, tol = tol)
if(!is.null(offset)) y <- y - offset
dims <- dim(y)
n <- dims[1]
p <- dims[2]
U <- qr.Q(Q)
fitted.values <- fastFit (U, y, n, p)
residuals <- y - fitted.values
if(!is.null(offset)) fitted.values <- fitted.values + offset
list(qr = Q, fitted.values = as.matrix(fitted.values),
residuals = as.matrix(residuals))
}
getTerms <- function(Terms, SS.type = "I") {
trms <- attr(Terms, "term.labels")
k <- length(trms)
mod.k <- if(k > 0) c(0, seq(1, k, 1)) else 0
if(k > 0) {
if(SS.type == "III"){
k3 <- mod.k[-1]
modf <- lapply(as.list(k3), function(.) Terms)
modr <- lapply(as.list(k3), function(j) Terms[-j])
} else if(SS.type == "II"){
k2 <- mod.k[-1]
fac <- crossprod(attr(Terms, "factor"))
modr <- lapply(as.list(k2), function(j){
ind <- as.logical(ifelse(fac[j,] < fac[j,j], 1, 0))
Terms[ind]
})
modf <- lapply(as.list(k2), function(j){
ind <- ifelse(fac[j,] < fac[j,j], 1, 0)
ind[j] <- 1
ind <- as.logical(ind)
Terms[ind]
})
} else {
kf <- mod.k[-1]
kr <- mod.k[-(max(mod.k) + 1)]
modf <- lapply(as.list(kf), function(j) Terms[1:j])
modr <- lapply(as.list(kr), function(j) Terms[0:j])
}
names(modf) <- names(modr) <- trms
} else {
modr <- modf <- list("Intercept" = Terms)
}
list(terms.r = modr, terms.f = modf)
}
getXs <- function(Terms, Y, SS.type, tol = 1e-7,
model) {
X <- model.matrix(Terms, data = model)
X.k <- X.k.obs <- attr(X, "assign")
fit.args <- list(x = X, y = Y, tol = tol, method = "qr",
singular.ok = TRUE)
fit <- do.call(lm.fit, fit.args)
X.n.k.obs <- length(X.k.obs)
QRx <- fit$qr
X.n.k <- QRx$rank
Xobs <- X
X <- X[, QRx$pivot, drop = FALSE]
X <- X[, 1:QRx$rank, drop = FALSE]
X.k <- X.k[QRx$pivot][1:QRx$rank]
attr(X, "assign") <- X.k
attr(X, "contrasts") <- attr(Xobs, "contrasts")
uk <- unique(c(0, X.k))
if(X.n.k < X.n.k.obs) fix <- TRUE else fix <- FALSE
if(fix) {
Terms <- Terms[uk]
warning(
paste(
"\nThis is not an error! It is a friendly warning.\n",
"\nBecause variables in the linear model are redundant,",
"\nthe linear model design has been truncated (via QR decomposition).",
"\nOriginal X columns:", X.n.k.obs,
"\nFinal X columns (rank):", X.n.k,
"\nCheck coefficients or degrees of freedom in ANOVA to see changes.\n",
"\nUse options(warn = -1) to turn off these warnings. \n\n", sep = " "),
noBreaks. = TRUE, call. = FALSE, immediate. = TRUE)
}
term.labels <- attr(Terms, "term.labels")
k <- length(term.labels)
shrinkX <- function(X, cols) {
X <- as.data.frame(X)
vars <- colnames(X)[cols]
X <- X[vars]
as.matrix(X)
}
if(k > 0){
if(SS.type == "III"){
uk0 <- uk[-1]
xk0 <- unique(X.k[-1])
Xrs <- lapply(2:length(uk), function(j){
vars <- X.k %in% uk[-j]
shrinkX(X, vars)
})
Xfs <- lapply(2:length(uk), function(j) X)
} else if(SS.type == "II"){
uk0 <- uk[-1]
xk0 <- unique(X.k[-1])
fac <- crossprod(attr(Terms, "factor"))
Xrs <- lapply(1:NROW(fac), function(j){
ind <- ifelse(fac[j,] < fac[j, j], 1, 0)
ind <- as.logical(c(1, ind))
vars <- X.k %in% uk[ind]
shrinkX(X, vars)
})
Xfs <- lapply(1:NROW(fac), function(j){
ind <- ifelse(fac[j,] < fac[j, j], 1, 0)
ind[j] <- 1
ind <- as.logical(c(1, ind))
vars <- X.k %in% uk[ind]
shrinkX(X, vars)
})
} else {
Xs <- lapply(1:length(uk), function(j) {
vars <- X.k %in% uk[1:j]
shrinkX(X, vars)
})
Xrs <- Xs[1:k]
Xfs <- Xs[2:(k+1)]
}
names(Xrs) <- names(Xfs) <- term.labels
} else {
Xrs <- Xfs <- list(Intercept = X)
}
list(Xrs = Xrs, Xfs = Xfs)
}
lm.rrpp.fit <- function(x, y, Pcov = NULL, w = NULL, offset = NULL, tol = 1e-07){
getGLSlm<- function(x, y, Pcov, offset = offset, method = "qr", tol = tol){
PX <- as.matrix(Pcov %*% x)
PY <- as.matrix(Pcov %*% y)
fit <- LM.fit(x = PX, y = PY, offset = offset, tol = tol)
fit
}
z <- if(!is.null(Pcov)) getGLSlm(x, y, Pcov, offset = offset, tol = tol) else
if(!is.null(w)) lm.wfit(x = as.matrix(x), y = y, w = w, offset = offset, tol = tol) else
LM.fit(x = x, y = y, offset = offset, tol = tol)
if(!is.null(Pcov)) {
z$residuals <- fast.solve(Pcov) %*% z$residuals
z$fitted.values <- y - z$residuals
}
z
}
# NO LONGER USED but retained for potential future use
lm.rrpp.exchange <- function(x, y, Pcov = NULL, w = NULL, offset = NULL, tol = 1e-07){
getGLSlm<- function(x, y, Pcov, offset = offset, method = "qr", tol = tol){
PX <- Pcov %*% x
PY <- Pcov %*% y
fit <- LM.fit(x = PX, y = PY, offset = offset, tol = tol)
fit
}
getWlm<- function(x, y, w, offset = offset, method = "qr", tol = tol){
wts <- sqrt(w)
fit <- lm.fit(x = as.matrix(x * wts),
y = as.matrix(y * wts), offset = offset, tol = tol)
fit
}
z <- if(!is.null(Pcov)) getGLSlm(x = x, y = y, Pcov, offset = offset, tol = tol) else
if(!is.null(w)) getWlm(x = x, y = y, w = w, offset = offset, tol = tol) else
LM.fit(x = x, y = y, offset = offset, tol = tol)
z
}
# NO LONGER USED but retained for potential future use
package.exchanges <- function(Y, mods, Xs, Terms, model,
offset = NULL, w = NULL,
Pcov = NULL, tol = 1e-7, SS.type) {
Xrs <- Xs$Xrs
Xfs <- Xs$Xfs
exchange.args <- list(x = Xrs[[1]], y = Y, Pcov = Pcov, w = w,
offset = offset, tol = tol)
reduced = lapply(Xrs, function(x) {
exchange.args$x <- x
do.call(lm.rrpp.exchange, exchange.args)
})
full = lapply(Xfs, function(x) {
exchange.args$x <- x
do.call(lm.rrpp.exchange, exchange.args)
})
model.sets <- list(terms.r = mods$terms.r, terms.f = mods$terms.f,
Xrs = Xrs, Xfs = Xfs)
list(reduced = reduced, full = full, offset = offset, weights = w,
Terms = Terms, model = model, Pcov = Pcov, SS.type = SS.type,
model.sets = model.sets)
}
# NO LONGER USED but retained for potential future use
package.fits <- function(Y, mods, Xs, Terms, model,
offset = NULL, w = NULL,
Pcov = NULL, tol = 1e-7, SS.type) {
Xrs <- Xs$Xrs
Xfs <- Xs$Xfs
fit.args <- list(x = Xrs[[1]], y = Y, Pcov = Pcov, w = w,
offset = offset, tol = tol)
reduced = lapply(Xrs, function(x) {
fit.args$x <- x
do.call(lm.rrpp.fit, fit.args)
})
full = lapply(Xfs, function(x) {
fit.args$x <- x
do.call(lm.rrpp.fit, fit.args)
})
model.sets <- list(terms.r = mods$terms.r, terms.f = mods$terms.f,
Xrs = Xrs, Xfs = Xfs)
list(reduced = reduced, full = full, offset = offset, weights = w,
Terms = Terms, model = model, Pcov = Pcov, SS.type = SS.type,
model.sets = model.sets)
}
# droplevels.rrpp.data.frame
# same as droplevels for data.frame objects
# used in lm.rrpp
droplevels.rrpp.data.frame <- function (x, except = NULL, ...) {
ix <- vapply(x, is.factor, NA)
if (!is.null(except))
ix[except] <- FALSE
x[ix] <- lapply(x[ix], factor)
x
}
# getHb
# function to find "hat" matrix for coefficients
# used in lm.rrpp/SS.iter/beta.iter
getHb <- function(Q) {
S4 <- !(inherits(Q, "qr"))
k <- getRank(Q)
R <- suppressWarnings(qr.R(Q))
U <- qr.Q(Q)
Rs <- try(fast.solve(R), silent = TRUE)
if(inherits(Rs, "try-error")){
Rs <- 1
}
res <- as.matrix(tcrossprod(Rs, U))
if(is.null(rownames(res))){
rownames(res) <- if(S4) Q@R@Dimnames[[2]] else
colnames(Q$qr)
}
res
}
# checkers
# algorithms to facilitate RRPP iteration stats calculations
# used in lm.rrpp/SS.iter/beta.iter
checkers <- function(Y, Qs, Qs.sparse, Xs, turbo = FALSE,
Terms, Pcov = NULL, w = NULL) {
k <- length(attr(Terms, "term.labels"))
Qr <- Qs$reduced
Qf <- Qs$full
n <- NROW(Y)
Qrs <- Qs.sparse$reduced
Qfs <- Qs.sparse$full
Ur <- lapply(Qr, qr.Q)
Uf <- lapply(Qf, qr.Q)
kk <- length(Uf)
if(k > 0 && k != kk) k <- kk
getU <- function(Q, Qs) {
U <- try(qr.Q(Qs), silent = TRUE)
if(inherits(U, "try-error"))
U <- qr.Q(Q)
U
}
Urs <- Map(function(q, qs) getU(q, qs), Qr, Qrs)
Ufs <- Map(function(q, qs) getU(q, qs), Qf, Qfs)
Urs <- lapply(Urs, function(x)
Matrix(round(x, 12), sparse = TRUE))
Ufs <- lapply(Ufs, function(x)
Matrix(round(x, 12), sparse = TRUE))
getR <- function(Q) {
R <- if(inherits(Q, "sparseQR")) qrR(Q) else qr.R(Q)
}
if(!turbo) {
Hbf <- lapply(Qf, getHb)
Hbfs <- lapply(Hbf, function(x) Matrix(round(x, 12), sparse = TRUE))
Hbr <- lapply(Qr, getHb)
Hbrs <- lapply(Hbr, function(x) Matrix(round(x, 12), sparse = TRUE))
for(i in 1:max(1,k)) {
if(object.size(Hbfs[[i]]) < object.size(Hbf[[i]]))
Hbf[[i]] <- Hbfs[[i]]
if(object.size(Hbrs[[i]]) < object.size(Hbr[[i]]))
Hbr[[i]] <- Hbrs[[i]]
}
} else Hbr <- Hbf <- NULL
# Linear model checkers
for(i in 1:max(1, k)) {
o.ur <- object.size(Ur[[i]])
o.urs <- object.size(Urs[[i]])
if(o.urs < o.ur) {
Ur[[i]] <- Urs[[i]]
}
o.uf <- object.size(Uf[[i]])
o.ufs <- object.size(Ufs[[i]])
if(o.ufs < o.uf) {
Uf[[i]] <- Ufs[[i]]
}
}
Ufull <-Uf[[max(1, k)]]
int <- attr(Terms, "intercept")
intercept <- rep(int, n)
Qint <- if(!is.null(Pcov))
qr(Pcov %*% intercept) else if(!is.null(w))
qr(intercept * sqrt(w)) else
qr(intercept)
Unull <- qr.Q(Qint)
S4 <- !inherits(Qint, "qr")
Hbnull <- if(S4) tcrossprod(fast.solve(qrR(Qint)), Unull) else
tcrossprod(fast.solve(qr.R(Qint)), Unull)
Qout <- Qs
for(i in 1:2){
for(j in 1:max(1, k)){
oq <- object.size(Qs[[i]][[max(1, j)]])
oqs <- object.size(Qs.sparse[[i]][[max(1, j)]])
if(oqs < oq) Qout[[i]][[j]] <- Qs.sparse[[i]][[j]]
}
}
out <- list(Y = Y, Ur = Ur, Uf = Uf, Unull = Unull, Ufull = Ufull,
Hbr = Hbr, Hbf = Hbf, Hbnull = Hbnull, QR = Qout, k = k,
realized.trms = names(Xs$Xfs))
out
}
# SS.iter
# three functions: main, and two for whether PP is used
# gets appropriate SS vectors for random permutations in lm.rrpp
# generates ANOVA stats
SS.iter <- function(checkrs, ind, print.progress = TRUE,
Parallel.args) {
forking <- Parallel.args$forking
usecluster <- Parallel.args$usecluster
cluster <- Parallel.args$cluster
no_cores <- Parallel.args$ParCores
SS.iter.main(checkrs = checkrs, ind = ind,
print.progress = print.progress,
no_cores = no_cores, usecluster = usecluster,
cluster = cluster, forking = forking)
}
SS.iter.main <- function(checkrs, ind, print.progress = TRUE,
no_cores, cluster = NULL, forking = FALSE,
usecluster = FALSE) {
Ur <- checkrs$Ur
Uf <- checkrs$Uf
Unull <- checkrs$Unull
Ufull <- checkrs$Ufull
FR <- checkrs$FR
Y <- checkrs$Y
dims <- dim(Y)
n <- dims[1]
p <- dims[2]
yh0 <- as.matrix(fastFit(Unull, Y, n, p))
r0 <- as.matrix(Y - yh0)
k <- checkrs$k
trms <- checkrs$realized.trms
perms <- length(ind)
rrpp.args <- list(FR = FR, ind.i = NULL)
rrpp <- function(FR, ind.i) {
lapply(FR, function(x) x$fitted + x$residuals[ind.i, ])
}
ss <- function(ur, uf, y) c(sum(crossprod(ur, y)^2), sum(crossprod(uf, y)^2),
sum(y^2) - sum(crossprod(Ufull, y)^2))
pbbar <- FALSE
if(print.progress && no_cores > 1){
cat("\nProgress bar not available for Sums of Squares calculations...\n")
} else if(print.progress && no_cores == 1){
cat(paste("\nSums of Squares calculations:", perms, "permutations.\n"))
pb <- txtProgressBar(min = 0, max = perms, initial = 0, style=3)
pbbar <- TRUE
}
if(forking && no_cores > 1) {
result <- mclapply(1:perms, mc.cores = no_cores, function(j){
x <-ind[[j]]
rrpp.args$ind.i <- x
Yi <- do.call(rrpp, rrpp.args)
y <- as.matrix(yh0 + r0[x,])
yy <- sum(y^2)
if(k > 0) {
res <- vapply(1:max(1, k), function(j){
ss(Ur[[j]], Uf[[j]], Yi[[j]])
}, numeric(3))
SSr <- res[1, ]
SSf <- res[2, ]
RSS <- res[3, ]
TSS <- yy - sum(crossprod(Unull, y)^2)
TSS <- rep(TSS, k)
SS = SSf - SSr
} else SSr <- SSf <- SS <- RSS <- TSS <- NA
RSS.model <- yy - sum(crossprod(Ufull, y)^2)
if(k == 0) TSS <- RSS.model
list(SS = SS, RSS = RSS, TSS = TSS, RSS.model = RSS.model)
})
} else if(usecluster && no_cores > 1) {
result <- parLapply(cluster, 1:perms, function(j){
x <-ind[[j]]
rrpp.args$ind.i <- x
Yi <- do.call(rrpp, rrpp.args)
y <- as.matrix(yh0 + r0[x,])
yy <- sum(y^2)
if(k > 0) {
res <- vapply(1:max(1, k), function(j){
ss(Ur[[j]], Uf[[j]], Yi[[j]])
}, numeric(3))
SSr <- res[1, ]
SSf <- res[2, ]
RSS <- res[3, ]
TSS <- yy - sum(crossprod(Unull, y)^2)
TSS <- rep(TSS, k)
SS = SSf - SSr
} else SSr <- SSf <- SS <- RSS <- TSS <- NA
RSS.model <- yy - sum(crossprod(Ufull, y)^2)
if(k == 0) TSS <- RSS.model
list(SS = SS, RSS = RSS, TSS = TSS, RSS.model = RSS.model)
})
stopCluster(cluster)
} else {
result <- lapply(1:perms, function(j){
step <- j
if(print.progress) setTxtProgressBar(pb,step)
x <-ind[[j]]
rrpp.args$ind.i <- x
Yi <- do.call(rrpp, rrpp.args)
y <- as.matrix(yh0 + r0[x,])
yy <- sum(y^2)
if(k > 0) {
res <- vapply(1:max(1, k), function(j){
ss(Ur[[j]], Uf[[j]], Yi[[j]])
}, numeric(3))
SSr <- res[1, ]
SSf <- res[2, ]
RSS <- res[3, ]
TSS <- yy - sum(crossprod(Unull, y)^2)
RSS.model <- yy - sum(crossprod(Ufull, y)^2)
SS = SSf - SSr
} else {
SSr <- SSf <- SS <- RSS <- TSS <- NA
RSS.model <- TSS <- yy - sum(crossprod(Ufull, y)^2)
}
list(SS = SS, RSS = RSS, TSS = TSS, RSS.model = RSS.model)
})
}
SS <- matrix(sapply(result, "[[", "SS"), max(1, k), perms)
RSS <- matrix(sapply(result, "[[", "RSS"), max(1, k), perms)
TSS <- matrix(sapply(result, "[[", "TSS"), max(1, k), perms,
byrow = TRUE)
RSS.model <- matrix(sapply(result, "[[", "RSS.model"),
max(1, k), perms, byrow = TRUE)
res.names <- list(if(k > 0) trms else "Intercept",
c("obs", paste("iter", 1:(perms-1), sep=".")))
dimnames(SS) <- dimnames(RSS) <- dimnames(TSS) <- dimnames(RSS.model) <-
res.names
if(all(is.na(SS))) RSS <- SS <- NULL
if(pbbar) close(pb)
list(SS = SS, RSS = RSS, TSS = TSS, RSS.model = RSS.model)
}
# anova_parts
# construct an ANOVA tablefrom random SS output
# used in lm.rrpp
getRank <- function(Q) {
if(inherits(Q, "sparseQR")) {
d <- round(svd(Q@R)$d, 15)
r <- length(which(d > 0))
} else if(inherits(Q, "qr")) {
r <- Q$rank
} else r <- rankMatrix(Q)[1]
return(r)
}
anova_parts <- function(checkrs, SS, full.resid = FALSE){
SS.type <- checkrs$SS.type
perms <- NCOL(SS)
trms <- checkrs$terms
k <- checkrs$k
dims <- dim(checkrs$Y)
n <- dims[1]
p <- dims[2]
QRf <- checkrs$QR$full
QRr <- checkrs$QR$reduced
if(k > 0) {
df <- unlist(Map(function(qf, qr)
getRank(qf) - getRank(qr),
QRf, QRr))
dfe <- n - getRank(QRf[[k]])
RSS <- SS$RSS
TSS <- SS$TSS
RSS.model <- SS$RSS.model
SS <- SS$SS
Rsq <- SS/TSS
MS <- SS/df
RMS <- RSS/dfe
Fs <- MS/RMS
dft <- n - 1
df <- c(df, dfe, dft)
if(SS.type == "III") {
etas <- SS/TSS
cohenf <- etas/(1-etas)
} else {
etas <- Rsq
if(k == 1) unexp <- 1 - etas else unexp <- 1 - apply(etas, 2, cumsum)
cohenf <- etas/unexp
}
rownames(Fs) <- rownames(cohenf) <- rownames(SS)
if(full.resid) {
SS <- abs(SS - cbind(0, matrix(SS[,1], nrow(SS), ncol(SS) - 1)))
MS <- abs(MS - cbind(0, matrix(MS[,1], nrow(MS), ncol(MS) - 1)))
Rsq <- abs(Rsq - cbind(0, matrix(Rsq[,1], nrow(Rsq), ncol(Rsq) - 1)))
Fs <- MS/RMS
rownames(Fs) <- rownames(SS)
cohenf <- NULL
}
} else {
RSS <- NULL
TSS <- SS$TSS
RSS.model <- SS$RSS.model
SS <- NULL
Rsq <- NULL
MS <- NULL
RMS <- NULL
Fs <- NULL
cohenf <- NULL
df <- n - 1
SS.type <- NULL
}
out <- list(SS.type = SS.type, SS = SS, MS = MS, RSS = RSS,
TSS = TSS, RSS.model = RSS.model, Rsq = Rsq,
Fs = Fs, cohenf = cohenf, full.resid = full.resid,
n = n, p = p, df=df
)
out
}
# SS.mean
# support function for calculating SS quickly in SS.iter
# used in lm.rrpp
# NO LONGER USED but retained for potential future use
SS.mean <- function(x, n) if(is.vector(x)) sum(x)^2/n else sum(colSums(x)^2)/n
# beta.boot.iter
# gets appropriate beta vectors for coefficients via bootstrap
# used in predict.lm.rrpp
beta.boot.iter <- function(fit, ind) {
gls <- fit$LM$gls
id <- colnames(fit$LM$QR$qr)
fitted <- if(gls) fit$LM$gls.fitted else fit$LM$fitted
res <- if(gls) fit$LM$gls.residuals else fit$LM$residuals
w <- fit$LM$weights
if(!is.null(w)) weighted = TRUE else weighted = FALSE
if(weighted) {
fitted <- fitted * sqrt(w)
res <- res * sqrt(w)
}
Y <- fitted + res
dims <- dim(Y)
n <- dims[1]
p <- dims[2]
perms <- length(ind)
Pcov <- fit$LM$Pcov
if(is.null(Pcov) && !is.null(fit$LM$Cov))
Pcov <- Cov.proj(fit$LM$Cov)
rrpp.args <- list(fitted = as.matrix(fitted),
residuals = as.matrix(res),
ind.i = NULL)
rrpp <- function(fitted, residuals, ind.i) as.matrix(fitted + residuals[ind.i,])
Qf <- fit$LM$QR
Hf <- tcrossprod(fast.solve(qr.R(Qf)), qr.Q(Qf))
Hfs <- Matrix(round(Hf, 15), sparse = TRUE)
if(object.size(Hfs) < object.size(Hf)) Hf <- Hfs
betas <- lapply(1:perms, function(j){
x <-ind[[j]]
rrpp.args$ind.i <- x
Yi <- do.call(rrpp, rrpp.args)
if(!is.null(Pcov)) Yi <- Pcov %*% Yi
res <- as.matrix(Hf %*% Yi)
rownames(res) <- id
res
})
betas
}
# beta.iter
# three functions: main, and two for whether PP is used
# gets appropriate beta vectors for random permutations in lm.rrpp
# generates distances as statistics for summary
beta.iter <- function(checkrs, ind, print.progress = TRUE,
Parallel.args) {
forking <- Parallel.args$forking
cluster <- Parallel.args$cluster
usecluster <- Parallel.args$usecluster
no_cores <- Parallel.args$ParCores
beta.iter.main(checkrs = checkrs, ind = ind,
print.progress = print.progress,
no_cores = no_cores, usecluster = usecluster,
cluster = cluster, forking = forking)
}
beta.iter.main <- function(checkrs, ind, print.progress = TRUE,
no_cores, cluster = NULL, forking = FALSE,
usecluster = FALSE) {
k <- checkrs$k
trms <- checkrs$realized.trms
Hr <- checkrs$Hbr
Hf <- checkrs$Hbf
pert.rows <- lapply(1:max(1, k), function(j){
br.nms <- try(rownames(Hr[[j]]), silent = TRUE)
if(inherits(br.nms, "try-error"))
br.nms <- "(Intercept)"
bf.nms <- try(rownames(Hf[[j]]), silent = TRUE)
if(inherits(bf.nms, "try-error"))
bf.nms <- "(Intercept)"
if(length(bf.nms) > length(br.nms)) {
b <- bf.nms
a <- br.nms
} else {
a <- bf.nms
b <- br.nms
}
b.keep <- setdiff(bf.nms, br.nms)
res <- which(b %in% b.keep)
list(res = res, b.keep = b.keep)
})
b.names <- lapply(pert.rows, function(x) x$b.keep)
pert.rows <- lapply(pert.rows, function(x) x$res)
names(b.names) <- names(pert.rows) <- trms
FR <- checkrs$FR
o <- checkrs$offset
offst <- !is.null(o)
perms <- length(ind)
Y <- checkrs$Y
dims <- dim(Y)
n <- dims[1]
p <- dims[2]
rrpp.args <- list(FR = FR, ind.i = NULL, offst = offst, o = o)
rrpp <- function(FR, ind.i, offst, o) {
if(offst) lapply(FR, function(x) x$fitted + x$residuals[ind.i, ] - o) else
lapply(FR, function(x) x$fitted + x$residuals[ind.i, ])
}
pbbar <- FALSE
if(print.progress && no_cores > 1){
cat("\nProgress bar not available for coefficients estimation...\n")
} else if(print.progress && no_cores == 1){
cat(paste("\nCoefficients estimation:", perms, "permutations.\n"))
pb <- txtProgressBar(min = 0, max = perms, initial = 0, style=3)
pbbar <- TRUE
}
getBetas <- function(Hf, Yi, pert.rows){
Bf <- Map(function(hf, y, p) {
B <- as.matrix(hf %*% y)
d <- tcrossprod(B)[p, p]
if(length(p) > 1) d <- diag(d)
res = list(B = B, d = sqrt(d))
res
}, Hf, Yi, pert.rows)
}
if(forking && no_cores > 1) {
betas <- mclapply(1:perms, mc.cores = no_cores, function(j){
x <-ind[[j]]
rrpp.args$ind.i <- x
Yi <- do.call(rrpp, rrpp.args)
getBetas(Hf, Yi = Yi, pert.rows)
})
} else if(usecluster && no_cores > 1) {
betas <- parLapply(cluster, 1:perms, function(j){
x <-ind[[j]]
rrpp.args$ind.i <- x
Yi <- do.call(rrpp, rrpp.args)
getBetas(Hf, Yi = Yi, pert.rows)
})
} else {
betas <- lapply(1:perms, function(j){
step <- j
if(print.progress) setTxtProgressBar(pb,step)
x <-ind[[j]]
rrpp.args$ind.i <- x
Yi <- do.call(rrpp, rrpp.args)
getBetas(Hf, Yi = Yi, pert.rows)
})
}
if(pbbar) close(pb)
cnms <- colnames(Y)
betas.out <- lapply(1:max(1, k), function(j){
res <- lapply(1:perms, function(jj){
y <- betas[[jj]][[j]]$B
colnames(y) <- cnms
y
})
names(res) <- names(ind)
res
})
names(betas.out) <-trms
d.out <- lapply(1:max(1, k), function(j){
res <- sapply(1:perms, function(jj){
y <- betas[[jj]][[j]]$d
y
})
res
})
if(is.list(d.out)) d.out <- do.call(rbind, d.out)
dimnames(d.out) <- list(unlist(b.names), names(ind))
list(random.coef = betas.out,
random.coef.distances = d.out)
}
# ellipse.points
# A helper function for plotting ellipses from non-parametric CI data
# Used in predict.lm.rrpp
ellipse.points <- function(m, pr, conf) {
m <- as.matrix(m)
p <- NCOL(m)
z <- qnorm((1 - conf)/2, lower.tail = FALSE)
angles <- seq(0, 2*pi, length.out=200)
ell <- z* cbind(cos(angles), sin(angles))
del <- lapply(pr, function(x) x[,1:p] - m)
vcv <- lapply(1:NROW(m), function(j){
x <- sapply(1:length(del), function(jj){
del[[jj]][j,]
})
tcrossprod(x)/ncol(x)
})
R <- lapply(vcv, chol)
ellP <- lapply(R, function(x) ell %*% x)
np <- NROW(ellP[[1]])
ellP <- lapply(1:length(ellP), function(j){
x <- m[j,]
mm <- matrix(rep(x, each = np), np, length(x))
mm + ellP[[j]]
})
ellP <- simplify2array(ellP)
pc1lim <- c(min(ellP[,1,]), max(ellP[,1,]))
pc2lim <- c(min(ellP[,2,]), max(ellP[,2,]))
list(ellP = ellP, pc1lim = pc1lim, pc2lim = pc2lim,
means = m)
}
# aov.single.model
# performs ANOVA on a single model
# used in anova.lm.rrpp
aov.single.model <- function(object, ...,
effect.type = c("F", "cohenf", "SS", "MS", "Rsq"),
error = NULL) {
x <- object$ANOVA
if(is.null(x$Fs)) {
AN <- getANOVAStats(object, stat = "all")
x[c("SS", "MS", "Rsq", "Fs", "cohenf")] <-
AN[ c("SS", "MS", "Rsq", "Fs", "cohenf")]
AN <- NULL
}
if(is.null(x$Fs)) x$Fs <- x$F
df <- x$df
k <- length(df)-2
kk <- length(object$LM$term.labels)
if(k > 0 && k != kk) {
warning(
paste(
"\nThis is not an error! It is a friendly warning.\n",
"\nANOVA is missing some terms, likely because",
"\nsome independent variables were redundant.",
"\nIf the residual SS is 0, results should not be trusted\n",
"\nUse options(warn = -1) to turn off these warnings. \n\n", sep = " "),
noBreaks. = TRUE, call. = FALSE, immediate. = TRUE)
}
SS <- x$SS
MS <- x$MS
RSS <-x$RSS
TSS <- x$TSS
perms <- object$PermInfo$perms
pm <- object$PermInfo$perm.method
if(pm == "RRPP" && object$PermInfo$full.resid)
pm <- "FMRP"
trms <- object$LM$term.labels
if(!is.null(error)) {
if(!inherits(error, "character"))
stop("The error description is illogical. It should be a string of character
values matching ANOVA terms.",
call. = FALSE)
kk <- length(error)
if(kk != k)
stop("The error description should match in length the number of ANOVA terms
(not including Residuals)",
call. = FALSE)
MSEmatch <- match(error, c(trms, "Residuals"))
if(any(is.na(MSEmatch)))
stop("At least one of the error terms is not an ANOVA term",
call. = FALSE)
} else MSEmatch <- NULL
if(k >= 1) {
Fs <- x$Fs
if(!is.null(MSEmatch)){
Fmatch <- which(MSEmatch <= k)
Fs[Fmatch,] <- MS[Fmatch,]/MS[MSEmatch[Fmatch],]
F.effect.adj <- apply(Fs, 1, effect.size)
}
effect.type <- match.arg(effect.type)
if(object$LM$gls) {
est <- "GLS"
if(effect.type == "SS") {
warning(
paste(
"\nThis is not an error! It is a friendly warning.\n",
"\nCalculating effect size on SS is illogical with GLS.",
"\nEffect type has been changed to F distributions.\n",
"\nUse options(warn = -1) to turn off these warnings. \n\n", sep = " "),
noBreaks. = TRUE, call. = FALSE, immediate. = TRUE)
effect.type = "F"
}
if(effect.type == "MS") {
warning(
paste(
"\nThis is not an error! It is a friendly warning.\n",
"\nCalculating effect size on MS is illogical with GLS.",
"\nEffect type has been changed to F distributions.\n",
"\nUse options(warn = -1) to turn off these warnings. \n\n", sep = " "),
noBreaks. = TRUE, call. = FALSE, immediate. = TRUE)
effect.type = "F"
}
} else est <- "OLS"
ow <- options()$warn
options(warn = -1)
if(effect.type == "F") Z <- Fs
if(effect.type == "SS") Z <- x$SS
if(effect.type == "MS") Z <- x$MS
if(effect.type == "Rsq") Z <- x$Rsq
if(effect.type == "cohenf") Z <- x$cohenf
if(effect.type == "Rsq") effect.type = "R-squared"
if(effect.type == "cohenf") effect.type = "Cohen's f-squared"
Fs <- Fs[,1]
SS <- SS[,1]
MS <- MS[,1]
Rsq <- x$Rsq[,1]
cohenf <- x$cohenf[,1]
if(!is.null(Z)) {
if(!inherits(Z, "matrix")) Z <- matrix(Z, 1, length(Z))
P.val <- apply(Z, 1, pval)
Z <- apply(Z, 1, effect.size)
} else P.val <- NULL
Residuals <- c(df[k+1], RSS[[1]], RSS[[1]]/df[k+1],
RSS[[1]]/TSS[[1]], rep(NA, 3))
Total <- c(df[k+2], TSS[[1]], rep(NA, 5))
tab <- data.frame(Df = df[1:k], SS=SS, MS = MS, Rsq = Rsq,
F = Fs, Z = Z, P.val = P.val)
tab <- rbind(tab, Residuals = Residuals, Total = Total)
colnames(tab)[NCOL(tab)] <- paste("Pr(>", effect.type, ")", sep="")
class(tab) = c("anova", class(tab))
SS.type <- x$SS.type
options(warn = ow)
out <- list(table = tab, perm.method = pm, perm.number = perms,
est.method = est, SS.type = SS.type, effect.type = effect.type,
call = object$call)
} else {
RSS.model <- x$RSS.model
tab <- data.frame(df = df, SS = RSS.model[[1]], MS = RSS.model[[1]]/df)
rownames(tab) <- c("Residuals")
class(tab) = c("anova", class(tab))
if(object$LM$gls) est <-"GLS" else est <- "OLS"
out <- list(table = tab, perm.method = pm, perm.number = perms,
est.method = est, SS.type = NULL, effect.type = NULL,
call = object$call)
}
class(out) <- "anova.lm.rrpp"
out
}
# aov.multi.model
# performs ANOVA on multiple models
# used in anova.lm.rrpp
aov.multi.model <- function(object, lm.list,
effect.type = c("F", "cohenf", "SS", "MS", "Rsq"),
print.progress = TRUE) {
effect.type <- match.arg(effect.type)
if(inherits(object, "lm.rrpp")) refModel <- object else
stop("The reference model is not a class lm.rrpp object")
PermInfo <- getPermInfo(refModel, attribute = "all")
ind <- PermInfo$perm.schedule
perms <- length(ind)
if(refModel$LM$gls) {
X <- if(!is.null(refModel$LM$Pcov)) refModel$LM$Pcov %*% refModel$LM$X else
refModel$LM$X * sqrt(refModel$LM$weights)
} else X <- refModel$LM$X
if(refModel$LM$gls) {
Y <- if(!is.null(refModel$LM$Pcov)) refModel$LM$Pcov %*% refModel$LM$Y else
refModel$LM$Y * sqrt(refModel$LM$weights)
} else Y <- refModel$LM$Y
B <- if(refModel$LM$gls) refModel$LM$gls.coefficients else
refModel$LM$coefficients
U <- as.matrix(qr.Q(refModel$LM$QR))
n <- refModel$LM$n
p <- refModel$LM$p
Yh <- as.matrix(fastFit(U, Y, n, p))
R <- as.matrix(Y) - Yh
K <- length(lm.list)
Ulist <- lapply(1:K, function(j){
m <- lm.list[[j]]
as.matrix(qr.Q(m$LM$QR))
})
if(print.progress){
if(K > 1)
cat(paste("\nSums of Squares calculations for", K, "models:",
perms, "permutations.\n")) else
cat(paste("\nSums of Squares calculations for", K, "model:",
perms, "permutations.\n"))
pb <- txtProgressBar(min = 0, max = perms+5, initial = 0, style=3)
}
int <- attr(refModel$LM$Terms, "intercept")
if(refModel$LM$gls) {
int <- if(!is.null(refModel$LM$Pcov)) refModel$LM$Pcov %*% rep(int, n) else
sqrt(refModel$LM$weights)
} else int <- rep(int, n)
U0 <- as.matrix(qr.Q(qr(int)))
yh0 <- as.matrix(fastFit(U0, Y, n, p))
r0 <- as.matrix(Y) - yh0
rY <- function(ind.i) Yh + R[ind.i,]
rY0 <- function(ind.i) yh0 + r0[ind.i,]
RSS <- function(ind.i, U0, U, Ul, K, n, p, Y, yh0, r0) {
y <- as.matrix(rY(ind.i))
y0 <- as.matrix(rY0(ind.i))
rss0 <- sum(y^2) - sum(crossprod(U, y)^2)
rss00 <- sum(y0^2) - sum(crossprod(U0, y)^2)
rss <- lapply(1:K, function(j){
u <- Ul[[j]]
sum(y^2) - sum(crossprod(u, y)^2)
})
tss <- sum(y0^2) - sum(crossprod(U0, y)^2)
RSSp <- c(rss0, unlist(rss), tss)
RSSp
}
rss.list <- list(ind.i = NULL, U0 = U0, U = U,
Ul = Ulist, K = K, n = n , p = p, Y = Y,
yh0 = yh0, r0 = r0)
RSSp <- sapply(1:perms, function(j){
step <- j
if(print.progress) setTxtProgressBar(pb,step)
rss.list$ind.i <- ind[[j]]
do.call(RSS, rss.list)
})
RSSy <- as.matrix(RSSp[nrow(RSSp),])
RSSp <- as.matrix(RSSp[-(nrow(RSSp)),])
RSSy <- as.matrix(matrix(RSSy, nrow(RSSp), perms, byrow = TRUE))
fit.names <- c(refModel$call[[2]], lapply(1:K, function(j)
lm.list[[j]]$call[[2]]))
rownames(RSSp) <- rownames(RSSy) <- fit.names
SS <- rep(RSSp[1,], each = K + 1) - RSSp
Rsq <- SS / RSSy
dfe <- n - c(getRank(object$LM$QR), unlist(lapply(1:K,
function(j) getRank(lm.list[[j]]$LM$QR))))
df <- dfe[1] - dfe
df[1] <- 1
MS <- SS/rep(df, perms)
MSE <- RSSp/matrix(rep(dfe, perms), length(dfe), perms)
Fs <- MS/MSE
SS[which(zapsmall(SS) == 0)] <- 1e-32
MS[which(zapsmall(MS) == 0)] <- 1e-32
Rsq[which(zapsmall(Rsq) == 0)] <- 1e-32
Fs[which(zapsmall(Fs) == 0)] <- 1e-32
if(effect.type == "SS") {
Pvals <- apply(SS, 1, pval)
Z <- apply(SS, 1, effect.size)
} else if(effect.type == "MS") {
Pvals <- apply(MS, 1, pval)
Z <- apply(MS, 1, effect.size)
} else if(effect.type == "Rsq") {
Pvals <- apply(Rsq, 1, pval)
Z <- apply(Rsq, 1, effect.size)
} else{
Pvals <- apply(Fs, 1, pval)
Z <- apply(Fs, 1, effect.size)
}
SS[1,] <- NA
MS[1,] <- NA
Fs[1,] <- NA
Pvals[1] <- NA
Z[1] <- NA
RSS.obs <- RSSp[,1]
SS.obs <- SS[,1]
MS.obs <- MS[,1]
Rsq.obs <- Rsq[,1]
F.obs <- Fs[,1]
tab <- data.frame(ResDf = dfe, Df = df, RSS = RSS.obs, SS = SS.obs, MS = MS.obs,
Rsq = Rsq.obs, F = F.obs, Z = Z, P = Pvals)
tab$DF[1] <- NA
tab$Rsq <- zapsmall(tab$Rsq, digits = 8)
tab <- rbind(tab, c(n-1, NA, RSSy[1], NA, NA, NA, NA, NA, NA, NA))
rownames(tab)[NROW(tab)] <- "Total"
if(effect.type == "SS") p.type <- "Pr(>SS)" else
if(effect.type == "MS") p.type <- "Pr(>MS)" else
if(effect.type == "Rsq") p.type <- "Pr(>Rsq)" else
if(effect.type == "cohenf") p.type <- "Pr(>cohenf)" else
p.type <- "Pr(>F)"
names(tab)[length(names(tab))] <- p.type
rownames(tab)[1] <- paste(rownames(tab)[1], "(Null)")
class(tab) <- c("anova", class(tab))
step <- perms + 5
if(print.progress) {
setTxtProgressBar(pb,step)
close(pb)
}
pm <- "RRPP"
if(refModel$LM$gls) est <- "GLS" else est <- "OLS"
out <- list(table = tab, perm.method = pm, perm.number = perms,
est.method = est, SS.type = NULL, effect.type = effect.type,
SS = SS[-1,], MS = MS[-1,], Rsq = Rsq[-1,], F = Fs[-1,],
call = object$call)
class(out) <- "anova.lm.rrpp"
out
}
# getSlopes
# gets the slopes for groups from a lm.rrpp fit
# used in pairwise
getSlopes <- function(fit, x, g){
beta <- fit$LM$random.coef
k <- length(beta)
beta <- beta[[k]]
n <- fit$LM$n
p <- fit$LM$p
X <- fit$LM$X
X <- X[, colnames(X) %in% rownames(beta[[1]])]
getFitted <- function(b) X %*% b
fitted <- lapply(beta, getFitted)
Xn <- model.matrix(~ g * x + 0)
Q <- qr(Xn)
H <- tcrossprod(fast.solve(qr.R(Q)), qr.Q(Q))
getCoef <- function(f) H %*% f
Coef <- lapply(fitted, getCoef)
group.slopes <- function(B){ # B of form ~ group * x + 0
gp <- qr(model.matrix(~g))$rank
gnames <- rownames(B)[1:gp]
B <- as.matrix(B[-(1:gp),])
B[2:gp, ] <- B[2:gp, ] + matrix(rep(B[1,], each = gp -1), gp -1, p)
rownames(B) <- gnames
B
}
slopes <- lapply(Coef, group.slopes)
rename <- function(x) {
dimnames(x)[[1]] <- levels(g)
x
}
slopes <- lapply(slopes, rename)
slopes
}
# getLSmeans
# gets the LS means for groups from a lm.rrpp fit,
# after constraining covariates to mean values
# used in pairwise
getLSmeans <- function(fit, g){
beta <- fit$LM$random.coef
k <- length(beta)
beta <- beta[[k]]
n <- fit$LM$n
dat <- fit$LM$data
covCheck <- sapply(dat, class)
for(i in 1:length(covCheck)) if(covCheck[i] == "numeric")
dat[[i]] <- mean(dat[[i]])
L <- model.matrix(fit$LM$Terms, data = dat)
L <- L[, colnames(L) %in% rownames(beta[[1]])]
getFitted <- function(b) {
b <- b[rownames(b) %in% colnames(L),]
L %*% b
}
fitted <- lapply(beta, getFitted)
Xn <- model.matrix(~ g + 0)
Q <- qr(Xn)
H <- tcrossprod(fast.solve(qr.R(Q)), qr.Q(Q))
getCoef <- function(f) H %*% f
means <- lapply(fitted, getCoef)
rename <- function(x) {
dimnames(x)[[1]] <- levels(g)
x
}
means <- lapply(means, rename)
names(means) <- c("obs", paste("iter", 1:(length(means) - 1), sep = "."))
means
}
#' Support function for RRPP
#'
#' Calculate vector correlations for a matrix (by rows).
#' Used for pairwise comparisons.
#'
#' @param M Matrix for vector correlations.
#' @keywords utilities
#' @export
#' @author Michael Collyer
vec.cor.matrix <- function(M) {
options(warn = -1)
M = as.matrix(M)
w = 1/sqrt(rowSums(M^2))
vc = tcrossprod(M*w)
diag(vc) <- 1
options(warn = 0)
vc
}
# Pval.list
# P-values across a list
# used in pairwise
Pval.list <- function(M){
y <- sapply(M, function(x) as.vector(as.dist(x)))
if(is.vector(y)) y <- matrix(y, 1, length(y))
P <- apply(y, 1, pval)
D <- as.dist(M[[1]])
D[1:length(D)] <- P
P <- as.matrix(D)
diag(P) <- 1
P
}
# effect.list
# effect size across a list
# used in pairwise
effect.list <- function(M){
y <- sapply(M, function(x) as.vector(as.dist(x)))
if(is.vector(y)) y <- matrix(y, 1, length(y))
Z <- apply(y, 1, effect.size)
D <- as.dist(M[[1]])
D[1:length(D)] <- Z
as.matrix(D)
}
# percentile.list
# find percentiles across a list
# used in pairwise
percentile.list <- function(M, confidence = 0.95){
P <- M[[1]]
n <- length(M)
for(i in 1:length(P)) {
y <- sapply(1:n, function(j) M[[j]][i])
P[i] <- quantile(y, confidence, na.rm = TRUE)
}
P
}
# d.summary.from.list
# find distance statistics from a list
# used in pairwise
d.summary.from.list <- function(M, confidence = 0.95){
P <- Pval.list(M)
Z <- effect.list(M)
CL <- percentile.list(M, confidence)
list(D=M[[1]], P=P, Z=Z, CL=CL, confidence = confidence)
}
# d.summary.from.list
# find vec correlation statistics from a list
# used in pairwise
r.summary.from.list <- function(M, confidence = 0.95){
options(warn = -1)
acos.mat <- function(x){
a <- acos(x)
diag(a) <- 1
a
}
A <- lapply(M, acos.mat)
options(warn = 0)
P <- Pval.list(A)
Z <- effect.list(A)
aCL <- percentile.list(A, confidence)
angle = A[[1]]
diag(angle) <- 0
list(r=M[[1]], angle = angle,
P=P,
Z=Z, aCL=aCL, confidence = confidence)
}
# makePWDTable
# arrange distance statistics into a table
# used in pairwise
makePWDTable <- function(L) { # List from summary.from.list
nms <- rownames(L$D)
DD <- as.dist(L$D)
DP <- as.dist(L$P)
DZ <- as.dist(L$Z)
DC <- as.dist(L$CL)
nam.com <- combn(length(nms), 2)
name.list <- list()
for(i in 1:NCOL(nam.com))
name.list[[i]] <- paste(nms[nam.com[1,i]], nms[nam.com[2,i]], sep =":")
name.list <- unlist(name.list)
tab <- data.frame(d = as.vector(DD),
UCI = as.vector(DC),
Z = as.vector(DZ),
P = as.vector(DP))
rownames(tab) <- name.list
colnames(tab)[2] <- paste("UCL (", L$confidence*100,"%)", sep = "")
colnames(tab)[4] <- "Pr > d"
tab
}
# makePWCorTable
# arrange vec cor statistics into a table
# used in pairwise
makePWCorTable <- function(L){
nms <- rownames(L$r)
DR <- as.dist(L$r)
DA <- as.dist(L$angle)
DP <- as.dist(L$P)
DaZ <- as.dist(L$Z)
DaC <- as.dist(L$aCL)
nam.com <- combn(length(nms), 2)
name.list <- list()
for(i in 1:NCOL(nam.com))
name.list[[i]] <- paste(nms[nam.com[1,i]], nms[nam.com[2,i]], sep =":")
name.list <- unlist(name.list)
tab <- data.frame(r = as.vector(DR),
angle = as.vector(DA),
UCL = as.vector(DaC),
Z = as.vector(DaZ),
P = as.vector(DP))
rownames(tab) <- name.list
colnames(tab)[3] <- paste("UCL (", L$confidence*100,"%)", sep = "")
colnames(tab)[5] <- "Pr > angle"
tab
}
# leaveOneOut
# set-up for jackknife classification
# used in classify
leaveOneOut <- function(X, Y, n.ind) {
x <- X[-n.ind,]
QR <- qr(x)
S4 <- !inherits(QR, "qr")
Q <- qr.Q(QR)
R <- if(S4) qrR(QR) else qr.R(QR)
H <- tcrossprod(fast.solve(R), Q)
y <- Y[-n.ind,]
H %*% y
}
# RiReg
# Ridge Regularization of a covariance matrix, if needed
# used in classify
RiReg <- function(Cov, residuals){
leads <- seq(0,1,0.005)[-1]
leads <- leads[-length(leads)]
I <- diag(1, NROW(Cov))
N <- NROW(residuals)
p <- NCOL(residuals)
Covs <- lapply(1:length(leads), function(j){
lambda <- leads[[j]]
(lambda * Cov + (1 - lambda) * I)
})
logL <- sapply(1:length(leads), function(j){
C <- Covs[[j]]
N*p*log(2*pi) + N * determinant(C, logarithm = TRUE)$modulus[1] + sum(diag(
residuals %*% fast.solve(C) %*% t(residuals)
))
})
Covs[[which.min(logL)]]
}
logL <- function(fit, tol = NULL, pc.no = NULL){
if(is.null(tol)) tol = 0
gls <- fit$LM$gls
n <- fit$LM$n
p <- fit$LM$p.prime
X <- as.matrix(fit$LM$X)
Y <- as.matrix(fit$LM$Y)
R <- if(gls) fit$LM$gls.residuals else fit$LM$residuals
PCA <- ordinate(R, tol = tol, rank. = min(c(pc.no, p)))
rnk <- length(PCA$d)
w <- fit$LM$weights
Pcov <- fit$LM$Pcov
Cov <- fit$LM$Cov
R <- if(gls) {
if(!is.null(Pcov)) Pcov %*% R else R * sqrt(w)
} else R
R <- as.matrix(R)
if(!is.null(w)) {
excl <- w <= 0
logdetC <- sum(log(w[!excl]))
} else {
logdetC <- if(gls) determinant(Cov, logarithm = TRUE)$modulus else 0
}
if(NCOL(R) > rnk) R <- ordinate(R, rank. = rnk)$x
Sig <- as.matrix(crossprod(R) / n)
if(kappa(Sig) > 1e10) Sig <- RiReg(Sig, R)
logdetSig <- determinant(Sig, logarithm = TRUE)$modulus
ll <- -0.5 * (n * rnk * log(2 * pi) + rnk * logdetC +
n * logdetSig + n)
list(logL = ll, rank = rnk)
}
cov.trace <- function(fit) {
n <- fit$LM$n
p <- fit$LM$p.prime
if(fit$LM$gls){
if(!is.null(fit$LM$Pcov)) Sig <- crossprod(fit$LM$Pcov %*%
fit$LM$gls.residuals)/n else
Sig <- crossprod(fit$LM$gls.residuals * sqrt(fit$LM$weights))/n
} else {
Sig <- crossprod(fit$LM$residuals) /n
}
sum(Sig^2)
}
z.test <- function(aov.mm){
effect.type = aov.mm$effect.type
if(effect.type == "F") stat <- aov.mm$F
if(effect.type == "cohenf") stat <- aov.mm$F
if(effect.type == "SS") stat <- aov.mm$SS
if(effect.type == "MS") stat <- aov.mm$MS
if(effect.type == "Rsq") stat <- aov.mm$Rsq
perms <- ncol(stat)
m <- nrow(stat)
stat.c <- sapply(1:m, function(j){
s <- stat[j,]
center(s)
})
index <- combn(m, 2)
Dz <- Pz <- dist(matrix(0, m))
zdj <- function(x, y, j) {
obs <- x[j] - y[j]
sigd <- sqrt(var(x) + var(y))
obs/sigd
}
for(i in 1:ncol(index)) {
x <- stat.c[,index[1,i]]
y <- stat.c[,index[2,i]]
res <- array(NA, perms)
for(j in 1: perms) res[j] <- zdj(x, y, j)
Dz[i] <- abs(res[1])
Pz[i] <- pval(abs(res))
}
Z = as.matrix(Dz)
P = as.matrix(Pz)
options(warn = -1)
mds <- cmdscale(Z, m-1, eig = TRUE)
options(warn = 0)
list(Z = as.matrix(Dz), P = as.matrix(Pz),
mds = mds,
form.names = rownames(aov.mm$table)[1:m],
model.names = paste("m", 0:(m-1), sep = ""))
}
wilks <- function(e) {
e <- zapsmall(e)
e <- e[e > 0]
prod(1/(1 + e))
}
pillai <- function(e) {
e <- zapsmall(e)
e <- e[e > 0]
sum(e/(1 + e))
}
hot.law <- function(e) {
e <- zapsmall(e)
e <- e[e > 0]
sum(e)
}
# trajsize
# find path distance of trajectory
# used in: trajectory.analysis
trajsize <- function(y) {
k <- NROW(y[[1]])
tpairs <- cbind(1:(k-1),2:k)
sapply(1:length(y), function(j) {
d <- as.matrix(dist(y[[j]]))
sum(d[tpairs])
})
}
# trajorient
# find trajectory correlations from first PCs
# used in: trajectory.analysis
trajorient <- function(y, tn) {
m <- t(sapply(1:tn, function(j){
x <- y[[j]]
La.svd(center.scale(x)$coords, 0, 1)$vt
}))
vec.cor.matrix(m)
}
# trajshape
# find shape differences among trajectories
# used in: trajectory.analysis
trajshape <- function(y){
y <- Map(function(x) center.scale(x)$coords, y)
M <- Reduce("+",y)/length(y)
z <- apply.pPsup(M, y)
z <- t(sapply(z, function(x) as.vector(t(x))))
as.matrix(dist(z))
}
# lda.prep
# sets up lm.rrpp object to use in lda
# used in: lda.rrpp
lda.prep <- function(fit, tol = 1e-7, PC.no = NULL, newdata = NULL){
dat <- fit$LM$data
gls <- fit$LM$gls
w <- fit$LM$weights
Pcov <- fit$LM$Pcov
dat.class <- sapply(dat, class)
fac.list <- which(dat.class == "factor")
if(length(fac.list) == 0) group <- factor(rep(1, n)) else {
datf <- dat[fac.list]
group <- factor(apply(datf, 1, paste, collapse = "."))
}
newY <- function(fit, group, w = NULL, Pcov=NULL) {
Xg <- model.matrix(~group)
Yg <- fit$LM$Y
res <- if(gls) fit$LM$gls.residuals else fit$LM$residuals
if(!is.null(w)) {
nfit <- lm.fit(as.matrix(Xg * sqrt(w)), as.matrix(Yg * sqrt(w)))
fitted <- Yg * sqrt(w) - res * sqrt(w)
nY <- (fitted + nfit$residuals)/sqrt(w)
} else if(!is.null(Pcov)) {
PY <- Pcov %*% Yg
nfit <- lm.fit(as.matrix(Pcov %*% Xg), PY)
fitted <- PY - as.matrix(Pcov %*% res)
nY <- fast.solve(Pcov) %*% (fitted + nfit$residuals)
} else {
nfit <- lm.fit(as.matrix(Xg), as.matrix(Yg))
nY <- fit$LM$fitted + nfit$res
}
nY
}
Yn <- newY(fit, group, w, Pcov)
dims <- dim(Yn)
n <- dims[1]
p <- dims[2]
Yc <- if(gls) Yn - matrix(fit$LM$gls.mean, n, p, byrow = TRUE) else
center(Yn)
V <- crossprod(Yc)/n
if(!is.null(Pcov)) V <- crossprod(Pcov %*% Yc)/n
if(!is.null(w)) V <- crossprod(Yc * sqrt(w))/n
if(gls) s <- svd(V)
PCA <- prcomp(Yn, tol = tol)
if(gls) {
PCA$rotation <- s$v
PCA$sdev <- sqrt(s$d)
PCA$x <- Yc %*% s$v
}
d <- zapsmall(PCA$sdev^2)
d <- 1:length(d[d > 0])
if(!is.null(PC.no)) {
if(PC.no < length(d)) d <- 1:PC.no
}
PCA$rotation <- PCA$rotation[, d]
PCA$sdev <- PCA$sdev[d]
PCA$x <- as.matrix(PCA$x[, d])
colnames(PCA$x) <- colnames(PCA$rotation) <- paste("PC", d, sep = "")
Yn <- PCA$x
if(!is.null(newdata)) {
Yt <- newdata
if(NCOL(newdata) != p)
stop("\nNumber of variables in newdata does not match the number
for the data in lm.rrpp fit.\n",
call. = FALSE)
Ytc <- if(gls) Yt - matrix(fit$LM$gls.mean, NROW(Yt), p, byrow = TRUE) else
center(Yt)
Yt <- Ytc %*% PCA$rotation
} else Yt <- NULL
list(Yn = Yn, Yt = Yt, group = group, gls = gls)
}
# looPCOne
# finds one PC vector via cross-validation
# used in looCV
looPCOne <-function(fit, n.ind) {
X <- fit$LM$X
Y <- fit$LM$Y
x <- X[n.ind,]
y <- Y[n.ind,]
X <- X[-n.ind,]
Y <- Y[-n.ind,]
gls <- fit$LM$gls
Pcov <- if(gls) fit$LM$Pcov[-n.ind, -n.ind] else NULL
QR <- if(gls) qr(Pcov %*% X) else qr(X)
S4 <- !inherits(QR, "qr")
Q <- qr.Q(QR)
R <- if(S4) qrR(QR) else qr.R(QR)
H <- tcrossprod(fast.solve(R), Q)
B <- if(gls) H %*% (Pcov %*% Y) else
H %*% Y
S <- svd(X %*% B)
y %*% S$v
}
# looPCOne
# finds each PC vector via cross-validation for a model fit
# used in looCV
looPCAll<-function(fit, ...) {
n <- fit$LM$n
gls <- fit$LM$gls
ord.args <- list(...)
ord.args$Y <- fit$LM$Y
ord.args$A <- tcrossprod(qr.Q(fit$LM$QR))
if(is.null(ord.args$tol)) ord.args$tol <- 1e-6
ord <- do.call(ordinate, ord.args)
k <- 1:length(ord$d)
res <- t(sapply(1:n, function(j) looPCOne(fit, j)))
dimnames(res) <- if(gls) dimnames(fit$LM$gls.fitted) else
dimnames(fit$LM$fitted)
d <- svd(fit$LM$fitted)$d
res <- res[,k]
res <- ordinate(res, ord$x, rank. = max(k))
list(raw = ord, cv = res)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.