R has amazing capabilities, but percentage tables are a weak spot
IMHO. There's prop.table but that's rather unwieldly, especially for
multiway tables. CrossTable by Marc Schwartz in the gregmisc library
makes percentage tables a breeze but is limited to two-way tables. So
I decided to try my own hand at writing an R-function that would make
it easy to produce nicely formatted percentage tables for one-way,
two-way, or multi-way tables.

The first argument for ctab can be either a table object or one or
more factors. The first variable is assumed to be the row variable,
the second the column variable, subsequent variables are grouping
variables. The "type" option can be used to specify percentage type
("n", "row", "column", or "total"), "digits" to specify the number of
decimal points, "percentage=FALSE" can be used to print proportions
rather than percentages. "row.vars" and "col.vars" are passed on to
ftables for formatting multiway tables.

I'd like to see something like ctab in R-base at some point in the
future. Perhaps it could be integrated in ftable? Perhaps I'll try
that myself as a next project. I'm still learning R so comments on
ctab are most welcome.

Best,
John Hendrickx

----------------examples of usage ------------------------
source("ctab.R")
data(Titanic)
ctab(Titanic)
ctab(Titanic,type="r")
ctab(Titanic,row.vars=1:3,type="r")
ctab(Titanic,col.vars=c(2,4),type="r")
ctab(Titanic,row.vars=c(1,3),type="c")
ctab(Titanic,col.vars=c("Sex","Survived"),type="r")
ctab(Titanic,col.vars=c("Sex","Survived"),type="t")
------------------ ctab ----------------------------------
# ctab: oneway, twoway, multiway percentage tables
# first argument must consist of one or more factors
# or a table object (class table, xtabs, or ftable)
# digits: number of digits after the decimal (default 2)
# type: "n" for counts, "row", "column" or "total"
# for percentages (default "n")
# row.vars:
# col.vars: same usage as ftable, ignored for one- and
# two-way tables
# percentages: FALSE==> proportions are presented rather
# than percentages (default TRUE)

# comments to John Hendrickx <[EMAIL PROTECTED]>

ctab<-function(...,digits=2,
                type=c("n", "row", "column", "total"),
                row.vars=NULL, col.vars=NULL,
                percentages=TRUE) {
        if (attributes(...)$class=="factor") {
                # create a table if the arguments are factors
                tbl<-table(...)
        }
        else if ("table" %in% class(...) || class(...)=="ftable") {
                # the argument is a table object (table, xtabs, ftable)
                tbl<-eval(...)
        }
        else {
                stop("first argument must be either factors or a table object")
        }

        type<-match.arg(type)

        # one dimensional table,restrict choices to "n" and "total"
        if (length(dim(tbl))==1) {
                type<-ifelse(type=="n","n","total")
        }

        # if the object is an ftable, use the row.vars and col.vars
        # use numeric indices to avoid finding the omitted
        # the object must be converted to a table to get the dimensions
right
        if (class(tbl)=="ftable") {
                nrowvar<-length(names(attr(tbl,"row.vars")))
                row.vars<-1:nrowvar
                col.vars<-(1:length(names(attr(tbl,"col.vars"))))+nrowvar
                tbl<-as.table(tbl)
        }

        # marginals to exclude assuming first factor is the row vaariable,
        # second factor is the column variable
        # is overridden by row.vars or col.vars
        mrg2drop<-0
        if (type=="column") {mrg2drop<-1}
        if (type=="row") {mrg2drop<-2}
        if (type=="total" && length(dim(tbl)) > 1) {mrg2drop<-c(1,2)}


        # use row.vars and col.vars to determine the
        # marginals to use when calculating percentages
        # start by translating names to variable positions
        nms<-names(dimnames(tbl))
        if (!is.null(row.vars) && !is.numeric(row.vars)) {
                row.vars<-order(match(nms,row.vars),na.last=NA)
        }
        if (!is.null(col.vars) && !is.numeric(col.vars)) {
                col.vars<-order(match(nms,col.vars),na.last=NA)
        }
        # calculate the other if only one is given
        if (!is.null(row.vars) && is.null(col.vars)) {
                col.vars<-(1:length(dim(tbl)))[-row.vars]
        }
        if (!is.null(col.vars) && is.null(row.vars)) {
                row.vars<-(1:length(dim(tbl)))[-col.vars]
        }
        # now determine the margin as the last element
        if (type=="row" && !is.null(col.vars)) {
                mrg2drop<-col.vars[length(col.vars)]
        }
        if (type=="column" && !is.null(row.vars)) {
                mrg2drop<-row.vars[length(row.vars)]
        }
        # if row.vars is given, col.vars has been determined
        if (type=="total" && !is.null(row.vars)) {
                mrg2drop<-c(col.vars[length(col.vars)],row.vars[length(row.vars)])
        }

        marg<-(1:length(dim(tbl)))[(-mrg2drop)]

        # create percentages
        if (type=="n") {
                digits<-0
        }
        else {
                tbl<-prop.table(tbl,marg)
                if (percentages) {tbl<-tbl*100}
        }


        # use ftable for more than 2 dimensions
        # (ftable doesn't work for 1 dimension,
        # and table is nicer for 2 dimensions IMHO
        if (length(dim(tbl))>2) {
                if (is.null(row.vars)) {
                        # let the second variable be the column variable
                        row.vars<-names(dimnames(tbl))[-2]
                        # reverse the order, last variables are groups, first is row
variable
                        row.vars<-rev(row.vars)
                }
                tbl<-ftable(tbl,row.vars=row.vars,col.vars=col.vars)
        }

        # get the names of the column variable
        if (class(tbl)=="ftable") {
                nms<-attr(tbl,"col.vars")[[1]]
        }
        else if (length(dim(tbl))==1) {
                nms<-dimnames(tbl)[[1]]
        }
        else{
                nms<-dimnames(tbl)[[2]]
        }

        # present the (percentage) table
        wd<-max(nchar(nms),nchar(as.integer(tbl))+digits+1)
        tbl<-formatC(tbl,format="f",width=wd,digits=digits)
        tbl
}
----------------------------------------------------------

______________________________________________
[EMAIL PROTECTED] mailing list
http://www.stat.math.ethz.ch/mailman/listinfo/r-help

Reply via email to