How to Put All Your Code in the Appendix in R Markdown

Yihui Xie 2018-09-18

My previous long post on the notebook war scratched the surface of literate programming in knitr and R Markdown. I don’t often talk about the “literate programming” aspect of knitr, but it is very interesting. I guess people mostly use knitr for the purpose of report generation from a linear combination of text and code. The power of “programming code chunks” is probably seriously underestimated. Note that here “program” is a verb.

The force is strong!

Why code in the appendix

Last week Alex Hayes showed an interesting trick on how to put all your code in the appendix of a document. This can be very useful when you don’t want to distract your readers with program code in the body of your report, but want to give those who care about the computational details a chance to know the details. An appendix will be the natural place to go for these code (especially when your output format is PDF or other formats to be printed).

The magical ninja Alex’s method

Alex combined the chunk option code with knitr::purl():

# Code Appendix

```{r code = readLines(knitr::purl("path/to/file.Rmd", documentation = 1)), echo = T, eval = F}

For those who don’t know the code option well, it is one way to provide the content of the code chunk. For example, the code chunk

```{r code=c('1 + 1', 'print("Hello woRld")')}

is equivalent to

1 + 1
print("Hello woRld")

So the code option can be highly useful when the code of a chunk is dynamic. For example, you can read the code dynamically from an external R script.

What knitr::purl() does is to extract all code chunks from a document and write them to an external file (an R script).

Then with chunk options eval = F (don’t evaluate the code in this chunk again because the individual previous code chunks have been executed) and echo = T (display code of this chunk in the output document), you will have a code appendix.

In Alex’s method, path/to/file.Rmd doesn’t have to be hard-coded, and can actually be replaced by knitr::current_input(), which gives you the path to the current input file.

The cool hacker Lucy’s method

Although Alex’s method definitely works, it is not very efficient, because every time you compile the document, knitr has to parse the document twice (once for knitr::knit() and once for knitr::purl()).

Among the replies to Alex’s original tweet, the very cool hacker1, Lucy D’Agostino McGowan, pointed out the efficient way:

```{r ref.label=knitr::all_labels(), echo = T, eval = F}

After knitr parses a document, all code chunks and their labels are stored internally. You have access to these labels via knitr::all_labels(). With the ref.label option, you can pull all code chunks into this code chunk.

Another cool hacker in a parallel universe

(Ten months later.)

Ariel Muldoon independently discovered all_labels(), and also tried to filter the chunk labels programmatically:

```{r getlabels, echo = FALSE}
labs = knitr::all_labels()
labs = labs[!labs %in% c("setup", "toc", "getlabels", "allcode")]

```{r allcode, ref.label = labs, eval = FALSE}

Clever! You got it.

But calling a single function doesn’t sound like programming

Some people may say that the taste of programming in “literate programming” seems to be too light. Don’t judge code by its length. Simplicity is definitely a merit. However, if your task is complicated enough, you can certainly program a knitr document in any way you like. The sky is your limit when you can program something.2

Here is a slightly advanced example, in which you can extract certain attributes of code chunks, and print them in the end of a document. For example, you can create an appendix with the names of all theorems in your document.

The fact that knitr’s code chunk options are evaluated like function arguments (meaning that you can write arbitrary R code in chunk options) and knitr gives you a hook system (knitr::knit_hooks) to manipulate pretty much everything means infinite possibilities to those who want to customize and program knitr documents. There was only one thing upon which I hesitated when deciding whether I should give users the access. That is knitr:::knit_code. Here the triple-colon is obviously a danger sign. When you can even modify the content of a code chunk, I have no idea what can happen. Evil or creative? I’ll leave it to you to think about.

  1. I wish I had time to write the “cool hacker” series of posts about those awesome R users. ↩︎

  2. Practically, your toddler’s mood is your limit. The sky doesn’t matter at all. ↩︎