Skip to content

Cross Tables with odfTable in odfweave

5 messages · Paul Jones, Max Kuhn, Marc Schwartz

#
Hi, I've been trying to prepare some crosstables for some survey 
questions for a client. I have been using the CrossTable function in the 
gmodels package. However, this command only seems to be able to create 
CrossTables in text documents.

I've been trying to use odfTable in odfweave to create tables that are 
standalone objects in the document that I can then convert to other 
formats, copy and paste and do whatever with.

The problem is no matter how I try to get the CrossTable into the 
odfTable, i either get an error message saying there is no appropriate 
method for odfTable or the table is inproperly formatted.

The closest I get is the improperly formatted table from these commands.

x <- capture.output(CrossTable(data.frame[["var1"]],data.frame["var2"]]))

I should probably mention that I like to put the column percentages 
generated by the CrossTable function into the table. I find it easier to 
explain the tables to people when I have the column percentages in the 
table. I also put the p-value from the chisq test in the command, but I 
don't mind using the chisq command to insert the p-value after the table 
if necessary.

Honestly, I'm not even married to using odfTable. All I'm looking for is 
a nicely formatted table, that is a standalone object, that I can copy 
and paste and convert at will, and that contains the column percentages 
in the table.

Optionally, and lastly I would like to have the p-value of the chisq 
test appendended to the end of the table.

How do others output CrossTables tables in R. I would do it by hand, but 
keep in mind I actually have many tables (I'm dealing with 4 similar 
data sets here), so I would like these tables to be created 
automatically and the command should be useable from within a for loop.

Thank You So,

PJ
#
PJ,
You will have to do some work to get CrossTable results into odfWeave
for a few reasons. The main one is: CrossTable prints the output (as
opposed to outputting an object of a certain class that uses print to
make the results like table() and print.table()). In some cases, it is
easy to translate the output you see on the screen to odf markup. I'm
not sure that this function is one of those cases. That is not a
slight against the function, it just isn't meant to do that.
It would be nice to have a reproducible example along with the results
of sessionInfo(). Looking at ?odfTable, it has that the main argument
should be "a vector, matrix or data frame". I can' tell if there is a
bug in odfTable or if you are just using it incorrectly.
CrossTable may not be what you want then. The results are a list of
components of the table.
#
on 03/02/2009 03:11 PM Max Kuhn wrote:
In follow up to Max' post, if you can output or print the captured
CrossTable() content in odfWeave to a monospaced font, it would line up
appropriately. The use of a monospace font in the R console is the
presumption for CrossTable() output. However, it will look exactly as it
would in the R console (see below) as opposed to something more
formalized and "pretty".

You could largely recreate most of CrossTable's output using table() and
prop.table() by creating a matrix or dataframe, depending upon what you
want things to look like. This is what is essentially done within
CrossTable().

Using one of the examples in ?CrossTable:
...

                 | infert$induced
infert$education |         0 |         1 |         2 | Row Total |
-----------------|-----------|-----------|-----------|-----------|
          0-5yrs |         4 |         2 |         6 |        12 |
                 |     0.333 |     0.167 |     0.500 |     0.048 |
                 |     0.028 |     0.029 |     0.162 |           |
                 |     0.016 |     0.008 |     0.024 |           |
-----------------|-----------|-----------|-----------|-----------|
         6-11yrs |        78 |        27 |        15 |       120 |
                 |     0.650 |     0.225 |     0.125 |     0.484 |
                 |     0.545 |     0.397 |     0.405 |           |
                 |     0.315 |     0.109 |     0.060 |           |
-----------------|-----------|-----------|-----------|-----------|
         12+ yrs |        61 |        39 |        16 |       116 |
                 |     0.526 |     0.336 |     0.138 |     0.468 |
                 |     0.427 |     0.574 |     0.432 |           |
                 |     0.246 |     0.157 |     0.065 |           |
-----------------|-----------|-----------|-----------|-----------|
    Column Total |       143 |        68 |        37 |       248 |
                 |     0.577 |     0.274 |     0.149 |           |
-----------------|-----------|-----------|-----------|-----------|



# Do the above incrementally
# Generate counts and proportions
# row, column and table
TAB <- table(infert$education, infert$induced)
TAB.prop.r <- prop.table(table(infert$education, infert$induced), 1)
TAB.prop.c <- prop.table(table(infert$education, infert$induced), 2)
TAB.prop.t <- prop.table(table(infert$education, infert$induced))

# rbind() it all together
MAT <- rbind(TAB, TAB.prop.r, TAB.prop.c, TAB.prop.t)

# order by rownames
MAT <- MAT[order(rownames(MAT)), ]

# Set duplicated rownames to blank
rownames(MAT)[which(duplicated(rownames(MAT)))] <- ""
0            1           2
0-5yrs   4.00000000  2.000000000  6.00000000
         0.33333333  0.166666667  0.50000000
         0.02797203  0.029411765  0.16216216
         0.01612903  0.008064516  0.02419355
12+ yrs 61.00000000 39.000000000 16.00000000
         0.52586207  0.336206897  0.13793103
         0.42657343  0.573529412  0.43243243
         0.24596774  0.157258065  0.06451613
6-11yrs 78.00000000 27.000000000 15.00000000
         0.65000000  0.225000000  0.12500000
         0.54545455  0.397058824  0.40540541
         0.31451613  0.108870968  0.06048387


So that gives you the core table with counts, table, row and column
proportions in that order top to bottom for each row category as in
CrossTable(). Adjust the above based upon what you actually want in the
table output.

With some additional work, you could add row and column totals and so
forth.

If you wanted variable numbers of digits after the decimal for each row
as in CrossTable(), you could pre-format the numbers using sprintf(),
converting MAT to a character matrix in the process. For example:

MAT.3 <- t(apply(rbind(TAB.prop.r, TAB.prop.c, TAB.prop.t), 1,
                 function(x) sprintf("%.3f", x)))

MAT <- rbind(TAB, MAT.3)

# order by rownames
MAT <- MAT[order(rownames(MAT)), ]

# Set duplicated rownames to blank
rownames(MAT)[which(duplicated(rownames(MAT)))] <- ""
0       1       2
0-5yrs  "4"     "2"     "6"
        "0.333" "0.167" "0.500"
        "0.028" "0.029" "0.162"
        "0.016" "0.008" "0.024"
12+ yrs "61"    "39"    "16"
        "0.526" "0.336" "0.138"
        "0.427" "0.574" "0.432"
        "0.246" "0.157" "0.065"
6-11yrs "78"    "27"    "15"
        "0.650" "0.225" "0.125"
        "0.545" "0.397" "0.405"
        "0.315" "0.109" "0.060"



With 'MAT' finalized, you could then use Max' functions to generate
pretty output for an OO.org document or use xtable() in the xtable
package or latex() in the Hmisc package for LaTeX or perhaps HTML output.

HTH,

Marc Schwartz
6 days later
#
Hey Marc,

Your code was very helpful. You should make a new R function out of it.

Actually it looks like adding margins may be a little bit tricky if I 
only want sums of the counts, not the percentages. If I add margins 
after I rbind, then R will sum together the counts with the 
probabilities, which is undesired. If I add margins before I rbind which 
is what I've been doing, it will produce sums of the counts and 
percentages independently but this still gives me sums of probabilities 
that are pretty useless and confusing in the table.

I'm actually running into bigger trouble trying to put the for loop I 
want into odfweave.

If I use odfweave, the odfweave function produces an output file, but 
the for loop produces no tables. If I use R2HTML with similar code, I 
just get the last table the for loop produces. Here is a look at the 
code of odfweave, which seems to be correct with syntax but doesn't 
produce any tables.

<<CrossTable, echo = FALSE, results = xml>>=

# Generate counts and proportions
# row, column and table
for ( i in 5:ncol(Elementary)[1] ) {

TAB <- table(Elementary[["Curriculum"]], Elementary[,i])
TAB.prop.c <- prop.table(table(Elementary[["Curriculum"]], 
Elementary[,i]),2)


TAB <- addmargins(TAB)
TAB.prop.c <- addmargins(TAB.prop.c)

MAT.3 <- t(apply(rbind(TAB.prop.c), 1,
                 function(x) sprintf("%.3f", x)))

MAT <- rbind(TAB, MAT.3)

# order by rownames
MAT <- MAT[order(rownames(MAT)), ]

# Set duplicated rownames to blank
rownames(MAT)[which(duplicated(rownames(MAT)))] <- ""

odfTable(MAT)
}
@

Sorry for bugging everyone with a question that is probably simple. Also 
is there  a way to add a table title in the odfTable function.

Thanks again,
PJ
Marc Schwartz wrote:
#
PJ,
odfTable produces an object that needs to be printed. At the command
line, if you type odfTable(MAT) it is implicitly calling print() to
show the results.

Inside a loop, you'll have to use print(odfTable(MAT)) to explicitly
print the results.

Max