| # File src/library/stats/R/model.tables.R |
| # Part of the R package, https://www.R-project.org |
| # |
| # Copyright 1998 B. D. Ripley |
| # Copyright (C) 1998-2015 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/ |
| |
| model.tables <- function(x, ...) UseMethod("model.tables") |
| |
| model.tables.aov <- function(x, type = "effects", se = FALSE, cterms, ...) |
| { |
| if(inherits(x, "maov")) |
| stop("'model.tables' is not implemented for multiple responses") |
| type <- match.arg(type, c("effects", "means", "residuals")) |
| if(type == "residuals") |
| stop(gettextf("type '%s' is not implemented yet", type), domain = NA) |
| prjs <- proj(x, unweighted.scale = TRUE) |
| if(is.null(x$call)) stop("this fit does not inherit from \"lm\"") |
| mf <- model.frame(x) |
| factors <- attr(prjs, "factors") |
| nf <- names(factors) |
| dn.proj <- setNames(as.list(nf), nf) |
| m.factors <- factors |
| t.factor <- attr(prjs, "t.factor") |
| vars <- colnames(t.factor) |
| which <- match(vars, names(dn.proj)) |
| which <- which[!is.na(which)] |
| dn.proj <- dn.proj[which] |
| m.factors <- m.factors[which] |
| ## with cterms, can specify subset of tables by name |
| if(!missing(cterms)) { |
| if(any(is.na(match(cterms, names(factors))))) |
| stop("'cterms' argument must match terms in model object") |
| dn.proj <- dn.proj[cterms] |
| m.factors <- m.factors[cterms] |
| } |
| if(type == "means") { |
| dn.proj <- |
| lapply(dn.proj, |
| function(x, mat, vn) |
| c("(Intercept)", |
| vn[(t(mat) %*% (as.logical(mat[, x]) - 1)) == 0]), |
| t.factor, vars) |
| } |
| tables <- make.tables.aovproj(dn.proj, m.factors, prjs, mf) |
| |
| ## This was reordering some interaction terms, e.g. N + V:N |
| ##n <- replications(paste("~", paste(names(tables), collapse = "+")), |
| ## data = mf) |
| n <- NULL |
| for(xx in names(tables)) n <- c(n, replications(paste("~", xx), data=mf)) |
| if(se) |
| if(is.list(n)) { |
| message("Design is unbalanced - use se.contrast() for se's") |
| se <- FALSE |
| } else se.tables <- se.aov(x, n, type = type) |
| if(type == "means" && "(Intercept)" %in% colnames(prjs)) { |
| gmtable <- mean(prjs[,"(Intercept)"]) |
| class(gmtable) <- "mtable" |
| tables <- c("Grand mean" = gmtable, tables) |
| } |
| result <- list(tables = tables, n = n) |
| if(se) result$se <- se.tables |
| attr(result, "type") <- type |
| class(result) <- c("tables_aov", "list.of") |
| result |
| } |
| |
| se.aov <- function(object, n, type = "means") |
| { |
| ## for balanced designs only |
| rdf <- object$df.residual |
| rse <- sqrt(sum(object$residuals^2)/rdf) |
| if(type == "effects") result <- rse/sqrt(n) |
| if(type == "means") |
| result <- |
| lapply(n, |
| function(x, d) { |
| nn <- unique(x) |
| nn <- nn[!is.na(nn)] |
| mat <- outer(nn, nn, function(x, y) 1/x + 1/y) |
| dimnames(mat) <- list(paste(nn), paste(nn)) |
| d * sqrt(mat) |
| }, d=rse) |
| attr(result, "type") <- type |
| class(result) <- "mtable" |
| result |
| } |
| |
| |
| model.tables.aovlist <- function(x, type = "effects", se = FALSE, ...) |
| { |
| type <- match.arg(type, c("effects", "means", "residuals")) |
| if(type == "residuals") |
| stop(gettextf("type '%s' is not implemented yet", type), domain = NA) |
| prjs <- proj(x, unweighted.scale = TRUE) |
| mf <- model.frame.aovlist(x) |
| factors <- lapply(prjs, attr, "factors") |
| dn.proj <- unlist(lapply(factors, names), recursive = FALSE) |
| m.factors <- unlist(factors, recursive = FALSE) |
| dn.strata <- rep.int(names(factors), lengths(factors)) |
| names(dn.strata) <- names(m.factors) <- names(dn.proj) <- unlist(dn.proj) |
| t.factor <- attr(prjs, "t.factor") |
| efficiency <- FALSE |
| if(type == "effects" || type == "means") { |
| if(anyDuplicated(names(dn.proj)[names(dn.proj) != "Residuals"])) { |
| efficiency <- eff.aovlist(x) |
| ## Elect to use the effects from the lowest stratum: |
| ## usually expect this to be highest efficiency |
| eff.used <- apply(efficiency, 2L, |
| function(x, ind = seq_len(x)) { |
| temp <- (x > 0) |
| if(sum(temp) == 1) temp |
| else max(ind[temp]) == ind |
| }) |
| } |
| } |
| if(any(efficiency)) { |
| if(is.list(eff.used)) |
| stop("design is unbalanced so cannot proceed") |
| which <- match(outer(rownames(efficiency), |
| colnames(efficiency), paste)[eff.used], |
| paste(dn.strata, dn.proj)) |
| efficiency <- efficiency[eff.used] |
| } else which <- match(colnames(t.factor), names(dn.proj)) |
| which <- which[!is.na(which)] |
| dn.proj <- dn.proj[which] |
| dn.strata <- dn.strata[which] |
| m.factors <- m.factors[which] |
| if(type == "means") { |
| t.factor <- t.factor[, names(dn.proj), drop = FALSE] |
| dn.proj <- |
| lapply(dn.proj, |
| function(x, mat, vn) |
| vn[(t(mat) %*% (as.logical(mat[, x]) - 1)) == 0], |
| t.factor, colnames(t.factor)) |
| } |
| tables <- |
| if(any(efficiency)) { |
| names(efficiency) <- names(dn.proj) |
| make.tables.aovprojlist(dn.proj, dn.strata, m.factors, prjs, mf, |
| efficiency) |
| } |
| else make.tables.aovprojlist(dn.proj, dn.strata, m.factors, prjs, mf) |
| if(type == "means") { |
| gmtable <- mean(prjs[["(Intercept)"]]) |
| class(gmtable) <- "mtable" |
| tables <- lapply(tables, "+", gmtable) |
| tables <- c("Grand mean" = gmtable, tables) |
| } |
| # n <- replications(attr(x, "call"), data = mf) |
| n <- replications(terms(x), data = mf) |
| if(se) |
| if(type == "effects" && is.list(n)) { |
| message("Standard error information not returned as design is unbalanced. \nStandard errors can be obtained through 'se.contrast'.") |
| se <- FALSE |
| } else if(type != "effects") { |
| warning(gettextf("SEs for type '%s' are not yet implemented", |
| type), domain = NA) |
| se <- FALSE |
| } else { |
| se.tables <- se.aovlist(x, dn.proj, dn.strata, factors, mf, |
| efficiency, n, type = type) |
| } |
| result <- list(tables = tables, n = n) |
| if(se) result$se <- se.tables |
| attr(result, "type") <- type |
| class(result) <- c("tables_aov", "list.of") |
| result |
| } |
| |
| se.aovlist <- function(object, dn.proj, dn.strata, factors, mf, efficiency, n, |
| type = "diff.means", ...) |
| { |
| if(type != "effects") |
| stop(gettextf("SEs for type '%s' are not yet implemented", type), |
| domain = NA) |
| RSS <- sapply(object, function(x) sum(x$residuals^2)/x$df.residual) |
| res <- vector(length = length(n), mode = "list") |
| names(res) <- names(n) |
| for(i in names(n)) { |
| sse <- RSS[[dn.strata[dn.proj[[i]]]]] |
| if(any(efficiency)) |
| sse <- sse/efficiency[i] |
| res[[i]] <- as.vector(sqrt(sse/n[i])) |
| class(res[[i]]) <- "mtable" |
| } |
| attr(res, "type") <- type |
| res |
| } |
| |
| |
| make.tables.aovproj <- |
| function(proj.cols, mf.cols, prjs, mf, fun = "mean", prt = FALSE, ...) |
| { |
| tables <- setNames(vector("list", length(proj.cols)), names(proj.cols)) |
| for(i in seq_along(tables)) { |
| terms <- proj.cols[[i]] |
| terms <- terms[terms %in% colnames(prjs)] |
| data <- |
| if(length(terms) == 1L) prjs[, terms] |
| else prjs[, terms] %*% as.matrix(rep.int(1, length(terms))) |
| tables[[i]] <- tapply(data, mf[mf.cols[[i]]], |
| get(fun, mode="function")) |
| class(tables[[i]]) <- "mtable" |
| if(prt) print(tables[i], ..., quote = FALSE) |
| } |
| tables |
| } |
| |
| |
| make.tables.aovprojlist <- |
| function(proj.cols, strata.cols, model.cols, projections, model, eff, |
| fun = "mean", prt = FALSE, ...) |
| { |
| tables <- setNames(vector("list", length(proj.cols)), names(proj.cols)) |
| if(!missing(eff)) { |
| for(i in seq_along(tables)) { |
| terms <- proj.cols[[i]] |
| if(all(is.na(eff.i <- match(terms, names(eff))))) |
| eff.i <- rep.int(1, length(terms)) |
| if(length(terms) == 1L) |
| data <- projections[[strata.cols[i]]][, terms]/ eff[eff.i] |
| else { |
| if(length(strata <- unique(strata.cols[terms])) == 1L) |
| data <- projections[[strata]][, terms] %*% |
| as.matrix(1/eff[eff.i]) |
| else { |
| mat <- NULL |
| for(j in strata) { |
| mat <- cbind(mat, projections[[j]][, terms[!is.na(match(terms, |
| names(strata.cols)[strata.cols == j]))]]) |
| } |
| data <- mat %*% as.matrix(1/eff[eff.i]) |
| } |
| } |
| tables[[i]] <- tapply(data, model[model.cols[[i]]], |
| get(fun, mode="function")) |
| attr(tables[[i]], "strata") <- strata.cols[i] |
| class(tables[[i]]) <- "mtable" |
| if(prt) print(tables[i], ..., quote = FALSE) |
| } |
| } else for(i in seq_along(tables)) { |
| terms <- proj.cols[[i]] |
| if(length(terms) == 1L) data <- projections[[strata.cols[i]]][, terms] |
| else { |
| if(length(strata <- unique(strata.cols[terms])) == 1L) |
| data <- projections[[strata]][, terms] %*% |
| as.matrix(rep.int(1, length(terms))) |
| else { |
| mat <- NULL |
| for(j in strata) { |
| mat <- cbind(mat, projections[[j]][, terms[!is.na(match(terms, |
| names(strata.cols)[strata.cols == j]))]]) |
| } |
| data <- mat %*% as.matrix(rep.int(1, length(terms))) |
| } |
| } |
| tables[[i]] <- tapply(data, model[model.cols[[i]]], get(fun)) |
| attr(tables[[i]], "strata") <- strata.cols[i] |
| class(tables[[i]]) <- "mtable" |
| if(prt) print(tables[i], ..., quote = FALSE) |
| } |
| tables |
| } |
| |
| replications <- function(formula, data = NULL, na.action) |
| { |
| if(missing(data) && inherits(formula, "data.frame")) { |
| data <- formula |
| formula <- ~ . |
| } |
| if(!inherits(formula, "terms")) { |
| formula <- as.formula(formula) |
| if(length(formula) < 3L) { |
| f <- y ~ x |
| f[[3L]] <- formula[[2L]] |
| formula <- f |
| } |
| formula <- terms(formula, data = data) |
| } |
| if(missing(na.action)) |
| if(!is.null(tj <- attr(data, "na.action")) && is.function(tj)) |
| na.action <- tj |
| else { |
| naa <- getOption("na.action") |
| if(!is.null(naa)) na.action <- match.fun(naa) |
| else na.action <- na.fail |
| } |
| f <- attr(formula, "factors") |
| o <- attr(formula, "order") |
| labels <- attr(formula, "term.labels") |
| vars <- as.character(attr(formula, "variables"))[-1L] |
| if(is.null(data)) { |
| v <- c(quote(data.frame), attr(formula, "variables")) |
| data <- eval(as.call(v), parent.frame()) |
| } |
| if(!is.function(na.action)) stop("na.action must be a function") |
| data <- na.action(data) |
| class(data) <- NULL |
| n <- length(o) |
| z <- setNames(vector("list", n), labels) |
| dummy <- numeric(.row_names_info(data, 2L)) |
| data <- lapply(data, function(x) if (is.character(x)) as.factor(x) else x) |
| notfactor <- !sapply(data, function(x) inherits(x, "factor")) |
| balance <- TRUE |
| for(i in seq_len(n)) { |
| l <- labels[i] |
| if(o[i] < 1 || substring(l, 1L, 5L) == "Error") { z[[l]] <- NULL; next } |
| select <- vars[f[, i] > 0] |
| if(any(nn <- notfactor[select])) { |
| warning(gettextf("non-factors ignored: %s", |
| paste(names(nn), collapse = ", ")), |
| domain = NA) |
| next |
| } |
| if(length(select)) |
| tble <- tapply(dummy, unclass(data[select]), length) |
| nrep <- unique(as.vector(tble)) |
| if(length(nrep) > 1L) { |
| balance <- FALSE |
| tble[is.na(tble)] <- 0 |
| z[[l]] <- tble |
| } else z[[l]] <- as.vector(nrep) |
| } |
| if(balance) unlist(z) else z |
| } |
| |
| print.tables_aov <- function(x, digits = 4L, ...) |
| { |
| tables.aov <- x$tables |
| n.aov <- x$n |
| se.aov <- if(se <- !is.na(match("se", names(x)))) x$se |
| type <- attr(x, "type") |
| switch(type, |
| effects = cat("Tables of effects\n"), |
| means = cat("Tables of means\n"), |
| residuals = if(length(tables.aov) > 1L) cat( |
| "Table of residuals from each stratum\n")) |
| if(!is.na(ii <- match("Grand mean", names(tables.aov)))) { |
| cat("Grand mean\n") |
| gmtable <- tables.aov[[ii]] |
| print.mtable(gmtable, digits = digits, ...) |
| } |
| for(i in names(tables.aov)) { |
| if(i == "Grand mean") next |
| table <- tables.aov[[i]] |
| cat("\n", i, "\n") |
| if(!is.list(n.aov)) |
| print.mtable(table, digits = digits, ...) |
| else { |
| n <- n.aov[[i]] |
| if(length(dim(table)) < 2L) { |
| table <- rbind(table, n) |
| rownames(table) <- c("", "rep") |
| print(table, digits = digits, ...) |
| } else { |
| ctable <- array(c(table, n), dim = c(dim(table), 2L)) |
| dim.t <- dim(ctable) |
| d <- length(dim.t) |
| ctable <- aperm(ctable, c(1, d, 2:(d - 1))) |
| dim(ctable) <- c(dim.t[1L] * dim.t[d], dim.t[-c(1, d)]) |
| dimnames(ctable) <- |
| c(list(format(c(rownames(table), rep.int("rep", dim.t[1L])))), |
| dimnames(table)[-1L]) |
| ctable <- eval(parse(text = paste( |
| "ctable[as.numeric(t(matrix(seq(nrow(ctable)),ncol=2)))", paste(rep.int(", ", d - 2), collapse = " "), "]"), |
| keep.source = FALSE)) |
| names(dimnames(ctable)) <- names(dimnames(table)) |
| class(ctable) <- "mtable" |
| print.mtable(ctable, digits = digits, ...) |
| } |
| } |
| } |
| if(se) { |
| if(type == "residuals") rn <- "df" else rn <- "replic." |
| switch(attr(se.aov, "type"), |
| effects = cat("\nStandard errors of effects\n"), |
| means = cat("\nStandard errors for differences of means\n"), |
| residuals = cat("\nStandard errors of residuals\n")) |
| if(length(unlist(se.aov)) == length(se.aov)) { |
| ## the simplest case: single replication, unique se |
| # kludge for NA's |
| n.aov <- n.aov[!is.na(n.aov)] |
| se.aov <- unlist(se.aov) |
| cn <- names(se.aov) |
| se.aov <- rbind(format(se.aov, digits = digits), format(n.aov)) |
| dimnames(se.aov) <- list(c(" ", rn), cn) |
| print(se.aov, quote=FALSE, right=TRUE, ...) |
| } else for(i in names(se.aov)) { |
| se <- se.aov[[i]] |
| if(length(se) == 1L) { ## single se |
| se <- rbind(se, n.aov[i]) |
| dimnames(se) <- list(c(i, rn), "") |
| print(se, digits = digits, ...) |
| } else { ## different se |
| dimnames(se)[[1L]] <- "" |
| cat("\n", i, "\n") |
| cat("When comparing means with same levels of:\n") |
| print(se, digits, ...) |
| cat("replic.", n.aov[i], "\n") |
| } |
| } |
| } |
| invisible(x) |
| } |
| |
| eff.aovlist <- function(aovlist) |
| { |
| Terms <- terms(aovlist) |
| if(names(aovlist)[[1L]] == "(Intercept)") aovlist <- aovlist[-1L] |
| pure.error.strata <- sapply(aovlist, function(x) is.null(x$qr)) |
| aovlist <- aovlist[!pure.error.strata] |
| s.labs <- names(aovlist) |
| ## find which terms are in which strata |
| s.terms <- |
| lapply(aovlist, function(x) { |
| asgn <- x$assign[x$qr$pivot[1L:x$rank]] |
| attr(terms(x), "term.labels")[asgn] |
| }) |
| t.labs <- attr(Terms, "term.labels") |
| t.labs <- t.labs[t.labs %in% unlist(s.terms)] |
| eff <- matrix(0, ncol = length(t.labs), nrow = length(s.labs), |
| dimnames = list(s.labs, t.labs)) |
| for(i in names(s.terms)) eff[i, s.terms[[i]] ] <- 1 |
| cs <- colSums(eff) |
| ## if all terms are in just one stratum we are done |
| if(all(cs <= 1)) return(eff[, cs > 0, drop = FALSE]) |
| |
| nm <- t.labs[ cs > 1] |
| pl <- |
| lapply(aovlist, function(x) |
| { |
| asgn <- x$assign[x$qr$pivot[1L:x$rank]] |
| sp <- split(seq_along(asgn), attr(terms(x), "term.labels")[asgn]) |
| sp <- sp[names(sp) %in% nm] |
| sapply(sp, function(x, y) { |
| y <- y[x, x, drop = FALSE] |
| res <- sum(diag(y)^2) |
| if(nrow(y) > 1 && sum(y^2) > 1.01 * res) |
| stop("eff.aovlist: non-orthogonal contrasts would give an incorrect answer") |
| res |
| }, y=x$qr$qr) |
| }) |
| for(i in names(pl)) eff[i, names(pl[[i]]) ] <- pl[[i]] |
| cs <- colSums(eff) |
| eff <- eff/rep(cs, each = nrow(eff)) |
| eff[, cs != 0, drop = FALSE] |
| } |
| |
| |
| model.frame.aovlist <- function(formula, data = NULL, ...) |
| { |
| ## formula is an aovlist object |
| call <- match.call() |
| oc <- attr(formula, "call") |
| Terms <- attr(formula, "terms") |
| rm(formula) |
| indError <- attr(Terms, "specials")$Error |
| errorterm <- attr(Terms, "variables")[[1 + indError]] |
| form <- update.formula(Terms, |
| paste(". ~ .-", deparse(errorterm, width.cutoff=500L, |
| backtick = TRUE), |
| "+", deparse(errorterm[[2L]], width.cutoff=500L, |
| backtick = TRUE))) |
| nargs <- as.list(call) |
| oargs <- as.list(oc) |
| nargs <- nargs[match(c("data", "na.action", "subset"), names(nargs), 0)] |
| args <- oargs[match(c("data", "na.action", "subset"), names(oargs), 0)] |
| args[names(nargs)] <- nargs |
| args$formula <- form |
| env <- environment(Terms) |
| if (is.null(env)) env <- parent.frame() |
| ## need stats:: for non-standard evaluation |
| fcall <- c(list(quote(stats::model.frame)), args) |
| eval(as.call(fcall), env) |
| } |
| |
| print.mtable <- |
| function(x, ..., digits = getOption("digits"), quote = FALSE, right = FALSE) |
| { |
| xxx <- x |
| xx <- attr(x, "Notes") |
| # nn <- names(dimnames(x)) |
| a.ind <- match(names(a <- attributes(x)), c("dim", "dimnames", "names")) |
| a <- a[!is.na(a.ind)] |
| class(x) <- attributes(x) <- NULL |
| attributes(x) <- a |
| # if(length(nn) > 1L) |
| # cat(paste("Dim ",paste(seq(length(nn)), "=", nn, collapse= ", "),"\n")) |
| if(length(x) == 1 && is.null(names(x)) && is.null(dimnames(x))) |
| names(x) <- rep("", length(x)) |
| if(length(dim(x)) && is.numeric(x)) { |
| xna <- is.na(x) |
| x <- format(zapsmall(x, digits)) |
| x[xna] <- " " |
| } |
| print(x, quote = quote, right = right, ...) |
| if(length(xx)) { |
| cat("\nNotes:\n") |
| print(xx) |
| } |
| invisible(xxx) |
| } |
| |
| |