# ========================================================================== f.table <- function(x1=NULL, x2=NULL, count="count", data=NULL, missing.cell=NA, na.exclude=FALSE, print.it="table", row=TRUE, col=TRUE, test=TRUE, test.tables=test, perc.digits=0, yates=TRUE) { ## Purpose: analysis of 2-dim. contingency tables ## ------------------------------------------------------------------------- ## Arguments: ## usually given: x1, x2 and possibly data. ## x1, x2 : variables, or ## x1 : matrix (already a table) ## count : frequencies of [x1,x2] (if different from 1) ## data : data frame. If it is given, x1 and x2 must be columns ## of data (they default to the first two columns) ## missing.cell : if count is given and some combinations do not occur, ## they are filled with the value of missing.cell ## na.exclude : should NAs be shown in the table? ## print.it : what should be printed directly ## (regardless of an assignment of value) ## Further arguments select the components of the value ## ## Value: list with components ## col, row : column and row percentages ## test : chisquare test ## test.tables : expected counts and pearson residuals ## print.it : print the results ## perc.digits : number of digits for column and row percents ## yates : Yates' continuity correction (only for 2 x 2 tables) ## ------------------------------------------------------------------------- ## Author: Werner Stahel, Date: 14 Oct 96 #on.exit(browser()) twodim <- TRUE if (!is.null(data)) { nm <- names(data) if (is.character(count)&length(count)==1) { jc <-match(count,nm) if(!is.na(jc)) count <- data[[jc]] nm <- nm[-jc] } if (is.null(x1)) x1 <- nm[1] if (is.null(x2)) x2 <- nm[2] nm <- names(data) if (!is.character(x1)) x1 <- nm[x1] if (!is.character(x2)) x2 <- nm[x2] nm <- c(x1,x2) x1 <- data[[x1]] x2 <- data[[x2]] } else nm <- c(paste(substitute(x1),collapse=""), if (!is.null(x2)) paste(substitute(x2),collapse="") else "") # --- if(is.matrix(x1)) { tb <- x1 nm <- names(dimnames(x1)) if (all(nm=="")) nm <- NULL } else { if (is.null(x2)) { tb <- rbind(table(x1)) twodim <- col <- test <- test.tables <- FALSE } else { # --- cross two variables x1 <- factor(x1) x2 <- factor(x2) if (length(x1)!=length(x2)) stop("f.table: unsuitable arguments x1, x2") if (is.numeric(count)) { nm1 <- levels(x1) nm2 <- levels(x2) tb <- matrix(missing.cell, length(nm1),length(nm2)) dimnames(tb) <- list(nm1,nm2) tb[cbind(x1,x2)] <- count # --- more than one row for a cell if (any(duplicated(outer(nm1,nm2,paste)))) { tb <- 0*tb tb[cbind(x1,x2)] <- tb[cbind(x1,x2)]+count } } else { if (na.exclude) { tb <- table(x1,x2) } else { tb <- table(x1,x2, exclude=NULL) } if (is.numeric(x1)) dimnames(tb)[[1]] <- c(format(sort(unique(x1))), if((!na.exclude)&&any(is.na(x1))) "NA") if (is.numeric(x2)) dimnames(tb)[[2]] <- c(format(sort(unique(x2))), if((!na.exclude)&&any(is.na(x2))) "NA") } # } # else } } tb1 <- cbind(tb, total = sr <- apply(tb, 1, sum)) sr <- c(sr, n <- sum(sr)) if(twodim) tb1 <- rbind(tb1, total = sc <- apply(tb1, 2, sum)) attr(tb1, "margin.names") <- nm names(dimnames(tb1)) <- names(dimnames(tb)) tbc <- if(col) 100 * sweep(tb1, 2, sc, "/") else NULL tbr <- if(row) 100 * sweep(tb1, 1, sr, "/") else NULL ch <- if(test) chisq.test(tb, correct=yates) else NULL ex <- pr <- NULL if(test.tables){ ex <- outer(last(sr,-1),last(sc,-1)/n) pr <- (tb-ex)/sqrt(ex) } rr <- list(table=tb1, margin.names=nm, colperc=tbc, rowperc=tbr, fit=ex, resid.pearson=pr, chisq=ch) class(rr) <- c("f.table","list") if (!(is.logical(print.it)&&!print.it)) { prt <- list(margin.names=nm, table=tb1[,], colperc=if(col) round(tbc[,],perc.digits), rowperc=if(row) round(tbr[,],perc.digits), fit=if(test.tables) round(ex[,],2), resid.pearson=if(test.tables) round(pr[,],2), chisq=if(test) ch) prt <- prt[!sapply(prt,is.null)] if (is.logical(print.it)) if(print.it) print(prt) else return(rr) else if (is.character(print.it)) print(prt[print.it]) } #if (!Browse) on.exit() invisible(rr) } # ========================================================================== last <- function(x, length.out = 1, na.rm = FALSE) { ## Purpose: last element(s) of a vector ## Author: Werner Stahel, Date: Tue Jan 21 17:29:42 1992 ## ---------------------------------------------------------------- ## Arguments: ## x: vector ## length.out: if positive, return the length.out last elements of x, ## if negative, the last length.out elements are dropped ## ---------------------------------------------------------------- if (na.rm) x <- x[!is.na(x)] n <- length(x) x[sign(length.out)*(n-abs(length.out)+1):n] }