An alternative solution that allows you to break it down by categories is in
the functions below.  Comments/suggestions welcome and encouraged.

Note that much thanks is due to those who responded to an earlier post of
mine on a similar topic.

To use (assuming you have a data.frame falled pathDist with columns
distances (your numeric data of interest), Capacity, Surface, and SITE
(vectors of categories)):
ops=c(quote(mean),quote(median),quote(sd),quote(length))
ops.desc=list(mean="Mean",median="Median",sd="S.D.",length="N")
pathDist.indices=list
(Surface=pathDist$Surface,Capacity=pathDist$Capacity,Site=pathDist$SITE)
pathDist.bytable=bytable(pathDist$distances,pathDist.indices,ops=ops,
ops.desc=ops.desc,na.rm=TRUE)
pathDist.table=latex.table.by(pathDist.bytable,num.by.vars=length(
pathDist.indices),caption="Path Characteristics")
print(pathDist.table,
type="latex",file="E:/xdrive/projects/Ghana_air/backpack_data/plots/pathDist.table.tex",
include.rownames = FALSE, include.colnames = TRUE, sanitize.text.function =
force, tabular.environment='longtable', floating=FALSE)





# Make a table by group
# Usage:
#   print(latex.table.by(test.df), include.rownames = FALSE,
include.colnames = TRUE, sanitize.text.function = force)
#   then add \usepackage{multirow} to the preamble of your LaTeX document
#   for longtable support, add ,tabular.environment='longtable' to the print
command (plus add in ,floating=FALSE), then \usepackage{longtable} to the
LaTeX preamble
latex.table.by = function(df,num.by.vars=1,...) {
    # first num.by.vars groups must be sorted and in descending order of
priority
    if(!is.numeric(num.by.vars) | length(num.by.vars)!=1) {
        stop("num.by.vars must be a number")
    }
    # Create a by.vars vector
    by.vars=1:num.by.vars

    numcols=length(colnames(df))
    df.original=df

    # Initialize our clines variable (gives the start column of the cline
for each row)
    clines = rep(num.by.vars+1,length(df[[1]]))
    # - Make grouping columns multirow - #
    for(b in rev(by.vars)) {

        # Create a groups variable for all by.vars up to the current one
        groups=rep("",length(df[[b]]))
        for(by.vars.index in 1:b) {
            groups = paste(groups,df.original[[by.vars.index]],sep="")
        }
        # Add multirow code to current column according to the groups
pattern
        df[[b]] <- as.character(df[[b]])
        rle.lengths <- rle(groups)$lengths
        first <- !duplicated(groups)
        df[[b]][!first] <- ""
        df[[b]][first] <- paste("\\multirow{", rle.lengths, "}{*}{",
df[[b]][first], "}")

        # Store this by.var's information in the clines variable
        clines[first]=b
    }

    # Specify horizontal lines wherever all combinations of grouping
variables change
    df[[1]]<-paste("\\cline{",clines,"-",numcols,"}",df[[1]],sep="")


    align.by.vars = sapply(list(rep("|c", (length(by.vars)+1)
)),paste,collapse="")
    align.other.vars = sapply(list(rep("r|", (length(colnames(df))-length(
by.vars)) )),paste,collapse="")
    align.df = paste("|", align.by.vars , "|" , align.other.vars ,sep="")

    xt=xtable(df, align = align.df,...)


    return(xt)

}


bytable = function(datavec,indices,ops=c(quote(mean)),ops.desc=list
(mean="Mean"),na.rm=TRUE,...) {
    groups=as.character()
    combinations.others=c()

    # indices should be a list of grouping vectors, just like you would pass
to -by-, but with sensible names for each vector
    if(!is.list(indices)) {
        stop("indices needs to be a list")
    }
    # Create a selector variable from the indices given as a list
    if(length(indices) > 1) {
        for(indexnum in length(indices):1) {
            groups=paste(groups,indices[[indexnum]],sep="")
        }
    }
    if(length(indices)==1) {
        groups=indices[[1]]
    }
    first=!duplicated(groups)

    # Initialize data frame with grouping variables (indices)
    bynames=dimnames(by(datavec,indices,function(x) x=1)) # run a dummy by
statement to get the name order out...highly inefficient...could use
indices.levels=lapply(indices,function(x) x[!duplicated(x)]) instead, as
long as we're sure the ordering is the same
    for(indexnum in length(indices):1) {
        # get the number of combinations of other index levels after this
one (e.g. the number of replicates we need to make of each one in this
index)
        others.selector=rep(TRUE,length(indices))
        others.selector[length(indices):indexnum]=FALSE
        numcombinations.others = prod(unlist(subset(lapply(bynames,length),
others.selector)))
        # Replicate each level of this index the number of existing
combinations of other indices
        newcolumn=rep(bynames[[indexnum]],each=numcombinations.others)

        if(indexnum==length(indices)) { # first run
            by.df=data.frame(newcolumn)
        }
        if(indexnum!=length(indices)) {
            # newcolumn is too short by some multiple so we have to fix that
            newcolumn=rep(newcolumn, length(rownames(by.df))/length(newcolumn)
)
            # now attach our new column
            by.df=cbind(by.df,newcolumn)
        }
    }

    colnames(by.df)<-rev(names(indices))



    # Run -by- for each operation
    for(op in ops) {
        by.df[[deparse(op)]]=as.numeric(by(datavec,indices,eval(op)))
        colnames(by.df)[ colnames(by.df)==deparse(op) ] = ops.desc
[[deparse(op)]]
    }

    if(na.rm) {
        #this assumes that the NA's in the last one will be the same as the
NA's in all ops
        by.df=subset(by.df,!is.na(by.df[[length(by.df)]]))
    }

    return(by.df)
}

	[[alternative HTML version deleted]]

