While functionality of pander
and knitr
overlap in report generation, we have the feeling that the best way to use all power of R/knitr/pander
for report generation is to utilize them together. This short vignette aims to explain how to embed output of pander
in reports generated by knitr
. If you are not aware of what knitr
is, be sure to check out project’s homepage which contains extensive documentation and examples.
One of the most useful feature of knitr
is the ability to convert tables to output format on the fly. For example:
head(iris)
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
knitr::kable(head(iris))
Sepal.Length | Sepal.Width | Petal.Length | Petal.Width | Species |
---|---|---|---|---|
5.1 | 3.5 | 1.4 | 0.2 | setosa |
4.9 | 3.0 | 1.4 | 0.2 | setosa |
4.7 | 3.2 | 1.3 | 0.2 | setosa |
4.6 | 3.1 | 1.5 | 0.2 | setosa |
5.0 | 3.6 | 1.4 | 0.2 | setosa |
5.4 | 3.9 | 1.7 | 0.4 | setosa |
However, kable
table generator is simple by design, and does not capture all the variety of classes that R
has to offer. For example, CrossTable
and tabular
is not supported:
library(descr, quietly = TRUE)
ct <- CrossTable(mtcars$gear, mtcars$cyl)
#> Warning in chisq.test(tab, correct = FALSE, ...): Chi-squared approximation
#> may be incorrect
knitr::kable(ct)
#> Error in as.data.frame.default(x): cannot coerce class ""CrossTable"" to a data.frame
library(tables, quietly = TRUE)
#>
#> Attaching package: 'Hmisc'
#>
#> The following objects are masked from 'package:base':
#>
#> format.pval, round.POSIXt, trunc.POSIXt, units
tab <- tabular( (Species + 1) ~ (n=1) + Format(digits=2)*
(Sepal.Length + Sepal.Width)*(mean + sd), data=iris )
knitr::kable(tab)
#> Error in `colnames<-`(`*tmp*`, value = c("term", "term", "term", "term", : length of 'dimnames' [2] not equal to array extent
This is where pander
comes handy. pander
support rendering for many popular classes:
methods(pander)
#> [1] pander.anova* pander.aov*
#> [3] pander.aovlist* pander.Arima*
#> [5] pander.call* pander.cast_df*
#> [7] pander.character* pander.clogit*
#> [9] pander.coxph* pander.cph*
#> [11] pander.CrossTable* pander.data.frame*
#> [13] pander.Date* pander.default*
#> [15] pander.density* pander.describe*
#> [17] pander.evals* pander.factor*
#> [19] pander.formula* pander.ftable*
#> [21] pander.function* pander.glm*
#> [23] pander.Glm* pander.gtable*
#> [25] pander.htest* pander.image*
#> [27] pander.irts* pander.list*
#> [29] pander.lm* pander.lme*
#> [31] pander.logical* pander.lrm*
#> [33] pander.manova* pander.matrix*
#> [35] pander.microbenchmark* pander.mtable*
#> [37] pander.name* pander.nls*
#> [39] pander.NULL* pander.numeric*
#> [41] pander.ols* pander.orm*
#> [43] pander.polr* pander.POSIXct*
#> [45] pander.POSIXlt* pander.prcomp*
#> [47] pander.randomForest* pander.rapport*
#> [49] pander.rlm* pander.sessionInfo*
#> [51] pander.smooth.spline* pander.stat.table*
#> [53] pander.summary.aov* pander.summary.aovlist*
#> [55] pander.summary.glm* pander.summary.lm*
#> [57] pander.summary.lme* pander.summary.manova*
#> [59] pander.summary.nls* pander.summary.polr*
#> [61] pander.summary.prcomp* pander.summary.rms*
#> [63] pander.summary.survreg* pander.summary.table*
#> [65] pander.survdiff* pander.survfit*
#> [67] pander.survreg* pander.table*
#> [69] pander.tabular* pander.ts*
#> [71] pander.zoo*
#> see '?methods' for accessing help and source code
And it’s integrated with knitr
by default. pander
simply identifies if knitr
is running in the backgorund, and if so, it capture.output
and return the resulting string as an knit_asis
object, so that you do not need to specify the results='asis'
option in your knitr chunk:
library(descr, quietly = TRUE)
pander(CrossTable(mtcars$gear, mtcars$cyl))
#> Warning in chisq.test(tab, correct = FALSE, ...): Chi-squared approximation
#> may be incorrect
mtcars$gear |
mtcars$cyl 4 |
6 |
8 |
Total |
---|---|---|---|---|
3 N Chi-square Row(%) Column(%) Total(%) |
1 3.3502 6.6667% 9.0909% 3.125% |
2 0.5003 13.3333% 28.5714% 6.250% |
12 4.5054 80.0000% 85.7143% 37.500% |
15 46.8750% |
4 N Chi-square Row(%) Column(%) Total(%) |
8 3.6402 66.6667% 72.7273% 25.000% |
4 0.7202 33.3333% 57.1429% 12.500% |
0 5.2500 0.0000% 0.0000% 0.000% |
12 37.5000% |
5 N Chi-square Row(%) Column(%) Total(%) |
2 0.0460 40.0000% 18.1818% 6.250% |
1 0.0080 20.0000% 14.2857% 3.125% |
2 0.0161 40.0000% 14.2857% 6.250% |
5 15.6250% |
Total |
11 34.375% |
7 21.875% |
14 43.75% |
32 |
library(tables, quietly = TRUE)
tab <- tabular( (Species + 1) ~ (n=1) + Format(digits=2)*
(Sepal.Length + Sepal.Width)*(mean + sd), data=iris )
pander(tab)
Species |
n |
Sepal.Length mean |
sd |
Sepal.Width mean |
sd |
---|---|---|---|---|---|
setosa | 50 | 5.01 | 0.35 | 3.43 | 0.38 |
versicolor | 50 | 5.94 | 0.52 | 2.77 | 0.31 |
virginica | 50 | 6.59 | 0.64 | 2.97 | 0.32 |
All | 150 | 5.84 | 0.83 | 3.06 | 0.44 |
In a nutshell, this is achieved by modification that whenever you call pander
inside of a knitr
document, instead of returning the markdown text to the standard output (as it used to happen), pander returns a knit_asis
class object, which renders fine in the resulting document — without the double comment chars, so rendering the tables in HTML, pdf or other document formats just fine.
If by any chance you actually want results of pander
not to be converted automatically, just specify knitr.auto.asis
to FALSE
either using panderOptions
:
panderOptions('knitr.auto.asis', FALSE)
pander(head(iris))
#>
#> -------------------------------------------------------------------
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> -------------- ------------- -------------- ------------- ---------
#> 5.1 3.5 1.4 0.2 setosa
#>
#> 4.9 3 1.4 0.2 setosa
#>
#> 4.7 3.2 1.3 0.2 setosa
#>
#> 4.6 3.1 1.5 0.2 setosa
#>
#> 5 3.6 1.4 0.2 setosa
#>
#> 5.4 3.9 1.7 0.4 setosa
#> -------------------------------------------------------------------
panderOptions('knitr.auto.asis', TRUE)
One question that is being asked a lot is how to use pander
with knitr
in a loop or with vectorized function. For example we have 3 tables that we want to render and we want to do it using lapply
:
dfs <- list(mtcars[1:3, 1:4], mtcars[4:6, 1:4], mtcars[7:9, 1:4])
lapply(dfs, pander)
#> [[1]]
#> [1] "\n-------------------------------------------\n mpg cyl disp hp \n------------------- ----- ----- ------ ----\n **Mazda RX4** 21 6 160 110 \n\n **Mazda RX4 Wag** 21 6 160 110 \n\n **Datsun 710** 22.8 4 108 93 \n-------------------------------------------\n\n"
#> attr(,"class")
#> [1] "knit_asis"
#> attr(,"knit_cacheable")
#> [1] TRUE
#>
#> [[2]]
#> [1] "\n-----------------------------------------------\n mpg cyl disp hp \n----------------------- ----- ----- ------ ----\n **Hornet 4 Drive** 21.4 6 258 110 \n\n **Hornet Sportabout** 18.7 8 360 175 \n\n **Valiant** 18.1 6 225 105 \n-----------------------------------------------\n\n"
#> attr(,"class")
#> [1] "knit_asis"
#> attr(,"knit_cacheable")
#> [1] TRUE
#>
#> [[3]]
#> [1] "\n----------------------------------------\n mpg cyl disp hp \n---------------- ----- ----- ------ ----\n **Duster 360** 14.3 8 360 245 \n\n **Merc 240D** 24.4 4 146.7 62 \n\n **Merc 230** 22.8 4 140.8 95 \n----------------------------------------\n\n"
#> attr(,"class")
#> [1] "knit_asis"
#> attr(,"knit_cacheable")
#> [1] TRUE
As you can see, this doesn’t work correctly, due to fact that when run inside knitr
, pander
tries to return knit_asis
class object, but for loops/vectorized functions this results in incorrect output.
Recommended way to solve this is to disable pander
trying to return knit_asis
class object by setting knitr.auto.asis
to FALSE
using panderOptions
. However, in that case to we also need to tell knitr
to convert table on the fly by specifying results='asis'
for knitr
chunk:
panderOptions('knitr.auto.asis', FALSE)
dfs <- list(mtcars[1:3, 1:4], mtcars[4:6, 1:4], mtcars[7:9, 1:4])
invisible(lapply(dfs, pander))
mpg | cyl | disp | hp | |
---|---|---|---|---|
Mazda RX4 | 21 | 6 | 160 | 110 |
Mazda RX4 Wag | 21 | 6 | 160 | 110 |
Datsun 710 | 22.8 | 4 | 108 | 93 |
mpg | cyl | disp | hp | |
---|---|---|---|---|
Hornet 4 Drive | 21.4 | 6 | 258 | 110 |
Hornet Sportabout | 18.7 | 8 | 360 | 175 |
Valiant | 18.1 | 6 | 225 | 105 |
mpg | cyl | disp | hp | |
---|---|---|---|---|
Duster 360 | 14.3 | 8 | 360 | 245 |
Merc 240D | 24.4 | 4 | 146.7 | 62 |
Merc 230 | 22.8 | 4 | 140.8 | 95 |