# File src/library/stats/R/manova.R # Part of the R package, https://www.R-project.org # # Copyright (C) 1995-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/ manova <- function(...) { Call <- fcall <- match.call() fcall[[1L]] <- quote(stats::aov) result <- eval(fcall, parent.frame()) if(inherits(result, "aovlist")) { for(i in seq_along(result)) { if(!inherits(result[[i]], "maov")) stop("need multiple responses") class(result[[i]]) <- c("manova", oldClass(result[[i]])) } attr(result, "call") <- Call } else { if(!inherits(result, "maov")) stop("need multiple responses") class(result) <- c("manova", oldClass(result)) result$call <- Call } result } summary.manova <- function(object, test = c("Pillai", "Wilks", "Hotelling-Lawley", "Roy"), intercept = FALSE, tol = 1e-7, ...) { if(!inherits(object, "maov")) stop(gettextf("object must be of class %s or %s", dQuote("manova"), dQuote("maov")), domain = NA) test <- match.arg(test) asgn <- object$assign[object$qr$pivot[1L:object$rank]] uasgn <- unique(asgn) nterms <- length(uasgn) effects <- object$effects if (!is.null(effects)) effects <- as.matrix(effects)[seq_along(asgn), , drop = FALSE] rdf <- object$df.residual nmeffect <- c("(Intercept)", attr(object$terms, "term.labels")) resid <- as.matrix(object$residuals) wt <- object$weights if (!is.null(wt)) resid <- resid * sqrt(wt) nresp <- NCOL(resid) if(nresp <= 1) stop("need multiple responses") if (is.null(effects)) { df <- nterms <- 0 ss <- list(0) nmrows <- character() } else { df <- numeric(nterms) ss <- list(nterms) nmrows <- character(nterms) for (i in seq(nterms)) { ai <- (asgn == uasgn[i]) nmrows[i] <- nmeffect[1 + uasgn[i]] df[i] <- sum(ai) ss[[i]] <- crossprod(effects[ai, , drop=FALSE]) } } pm <- pmatch("(Intercept)", nmrows, 0L) if (!intercept && pm > 0) { nterms <- nterms - 1 df <- df[-pm] nmrows <- nmrows[-pm] ss <- ss[-pm] } names(ss) <- nmrows nt <- nterms if (rdf > 0) { nt <- nterms + 1 df[nt] <- rdf ss[[nt]] <- crossprod(resid) names(ss)[nt] <- nmrows[nt] <- "Residuals" ok <- df[-nt] > 0 eigs <- array(NA, c(nterms, nresp), dimnames = list(nmrows[-nt], NULL)) stats <- matrix(NA, nt, 5, dimnames = list(nmrows, c(test, "approx F", "num Df", "den Df", "Pr(>F)"))) sc <- sqrt(sss <- diag(ss[[nt]])) ## Let us try to distinguish bad scaling and near-perfect fit for(i in seq_len(nterms)[ok]) sss <- sss + diag(ss[[i]]) sc[sc < sqrt(sss)*1e-6] <- 1 D <- diag(1/sc) rss.qr <- qr(D %*% ss[[nt]] %*% D, tol=tol) if(rss.qr$rank < ncol(resid)) stop(gettextf("residuals have rank %d < %d", rss.qr$rank, ncol(resid)), domain = NA) if(!is.null(rss.qr)) for(i in seq_len(nterms)[ok]) { A1 <- qr.coef(rss.qr, D %*% ss[[i]] %*% D) eigs[i, ] <- Re(eigen(A1, symmetric = FALSE, only.values = TRUE)$values) stats[i, 1L:4L] <- switch(test, "Pillai" = Pillai(eigs[i, ], df[i], df[nt]), "Wilks" = Wilks (eigs[i, ], df[i], df[nt]), "Hotelling-Lawley" = HL (eigs[i, ], df[i], df[nt]), "Roy" = Roy (eigs[i, ], df[i], df[nt])) ok <- stats[, 2L] >= 0 & stats[, 3L] > 0 & stats[, 4L] > 0 ok <- !is.na(ok) & ok stats[ok, 5L] <- pf(stats[ok, 2L], stats[ok, 3L], stats[ok, 4L], lower.tail = FALSE) } x <- list(row.names = nmrows, SS = ss, Eigenvalues = eigs, stats = cbind(Df=df, stats=stats)) } else x <- list(row.names = nmrows, SS = ss, Df = df) class(x) <- "summary.manova" x } print.summary.manova <- function(x, digits = getOption("digits"), ...) { if(length(stats <- x$stats)) { print.anova(stats) } else { cat("No error degrees of freedom\n\n") print(data.frame(Df = x$Df, row.names = x$row.names)) } invisible(x) }