# format.xtab attempts to display a
# conventionally formatted 2D crosstabulation with
# optional chi-squared test.

format.xtab<-function(v1,v2,dataframe,dnn=NULL,fieldwidth=10,chisq=FALSE) {
 if(!missing(v1) && !missing(v2)) {
  # get the table of frequencies
  if(!missing(dataframe))
   basetab<-table(dataframe[[v1]],dataframe[[v2]])
  else basetab<-table(v1,v2)
  # get the dimension of the current table
  btdim<-dim(basetab)
  # create two empty vectors to hold each row of row and column percentages
  row.pc<-col.pc<-vector("numeric",btdim[2])
  # row and column sums
  row.sums<-apply(basetab,1,sum)
  col.sums<-apply(basetab,2,sum)
  # row and column names
  row.names<-formatC(rownames(basetab),width=fieldwidth)
  col.names<-formatC(colnames(basetab),width=fieldwidth)
  grand.total<-sum(row.sums)
  rowlabelspace<-paste(rep(" ",nchar(row.names[1])),sep="",collapse="")
  # make sure that there are some sort of dimension labels
  if(is.null(dnn))
   dnn<-formatC(c(v1,v2),width=fieldwidth)
  else dnn<-formatC(dnn,width=fieldwidth)
  # display the header
  cat("Crosstabulation of",dnn[1],"by",dnn[2],"\n")
  # display the column dimension label
  cat(rowlabelspace,dnn[2],"\n")
  # display the column labels
  cat(rowlabelspace,col.names,"\n")
  # display the row dimension label
  cat(dnn[1],"\n")
  # now display each row of frequencies with its label and row sum
  # followed by the percentages
  for(i in 1:btdim[1]) {
   row.pc<-ifelse(row.sums[i],100*basetab[i,]/row.sums[i],0)
   for(j in 1:btdim[2])
    col.pc[j]<-ifelse(col.sums[j],100*basetab[i,j]/col.sums[j],0)
   cat(row.names[i],formatC(basetab[i,],width=fieldwidth),
    formatC(row.sums[i],width=fieldwidth),"\n")
   cat(rowlabelspace,formatC(round(row.pc,2),width=fieldwidth),
    formatC(round(100*row.sums[i]/grand.total,2),width=fieldwidth),"\n")
   cat(rowlabelspace,formatC(round(col.pc,2),width=fieldwidth),"\n\n")
  }
  # display the column sums and grand total
  cat(rowlabelspace,formatC(col.sums,width=fieldwidth),
   formatC(grand.total,width=fieldwidth),"\n")
  # display the column percentages
  cat(rowlabelspace,
   formatC(round(100*col.sums/grand.total,2),width=fieldwidth),"\n\n")
  # do the chi squared if it was ordered
  if(chisq) {
   chisq.obs<-chisq.test(basetab)
   cat(names(chisq.obs$statistic),"=",
    round(chisq.obs$statistic,3),
    names(chisq.obs$parameter),"=",
    chisq.obs$parameter,"p =",
    round(chisq.obs$p.value,5),"\n\n")
  }
  invisible(basetab)
 }
 else cat("Usage: format.xtab(v1, v2, dataframe[, dnn=NULL, fieldwidth = 10, chisq = FALSE])\n")
}

# xtab will try to break down the formula passed to it into
# one or more 2D crosstabulations with hierarchical counts
# for higher level factors.

xtab<-function(formula,dataframe,dnn=NULL,fieldwidth=10,chisq=FALSE) {
 if(!missing(formula) && !missing(dataframe)) {
  xt<-as.character(attr(terms(formula),"variables")[-1])
  nxt<-length(xt)
  if(nxt > 2) {
   by.factor<-as.factor(dataframe[[xt[nxt]]])
   factor.levels<-levels(by.factor)
   nlevels<-length(factor.levels)
   brkstats<-as.list(rep(0,nlevels))
   names(brkstats)<-factor.levels
   for(i in 1:nlevels) {
    currentdata<-subset(dataframe,by.factor == factor.levels[i])
    currentcount<-length(currentdata[[nxt]])
    totalcount<-length(dataframe[[nxt]])
    cat("\nCount for",xt[nxt],"=",factor.levels[i],"is",
     currentcount,"(",round(100*currentcount/totalcount,1),"%)\n\n")
    rightside <-ifelse(nxt > 3,paste(xt[2:(nxt-1)],sep="",collapse="+"),xt[2])
    next.formula<-
     as.formula(paste(xt[1],rightside,sep="~",collapse=""))
    xtab(next.formula,currentdata,dnn,fieldwidth,chisq)
   }
  }
  else format.xtab(xt[1],xt[2],dataframe,dnn,fieldwidth,chisq)
 }
 else cat("Usage: xtab(formula, dataframe[, dnn=NULL, fieldwidth = 10, chisq = FALSE])\n")
}
