4

I'm writing a report in R Markdown in which I don't want to print any of my R code in the main body of the report – I just want to show plots, calculate variables that I substitute into the text inline, and sometimes show a small amount of raw R output. Therefore, I write something like this:

In the following plot, we see that blah blah blah:
```{r snippetName, echo=F}
plot(df$x, df$y)
```

Now...

That's all well and good. But I would also like to provide the R code at the end of the document for anybody curious to see how it was produced. Right now I have to manually write something like this:

Here is snippet 1, and a description of what section of the report
this belongs to and how it's used:
```{r snippetName, eval=F}
```
Here is snippet 2:
```{r snippetTwoName, eval=F}
```
<!-- and so on for 20+ snippets -->

This gets rather tedious and error-prone once there are more than a few code snippets. Is there any way I could loop over the snippets and print them out automatically? I'm hoping I could do something like:

```{r snippetName, echo=F, comment="This is snippet 1:"}
# the code for this snippet
```

and somehow substitute the following result into the document at a specified point when it's knitted:

This is snippet 1:
```{r snippetName, eval=F}
```

I suppose I could write some post-processing code to scan through the .Rmd file, find all the snippets, and pull out the code with a regex or something (I seem to remember there's some kind of options file you can use to inject commands into the pandoc process?), but I'm hoping there might be something simpler.

Edit: This is definitely not a duplicate – if you read my question thoroughly, the last code block shows me doing exactly what the answer to the linked question suggests (with a slight difference in syntax, which could have been the source of the confusion?). I'm looking for a way to not have to write out that last code block manually for all 20+ snippets in the document.

Soren Bjornstad
  • 1,292
  • 1
  • 14
  • 25
  • 1
    Possible duplicate of [Use rmarkdown/knitr to hold all code until the end](http://stackoverflow.com/questions/28458384/use-rmarkdown-knitr-to-hold-all-code-until-the-end) – kukushkin Feb 19 '16 at 21:19
  • you could potentially call `purl()` to generate an external R file with all code chunks, read it back in the document, perhaps after having redefined eval=FALSE as default and removing the last two chunks (the one for purl, and the one for display). – baptiste Feb 19 '16 at 21:39
  • actually, [purl can be turned off](http://stackoverflow.com/a/17246052/471093) for those last two chunks – baptiste Feb 19 '16 at 21:46
  • @baptiste: That sounds promising. This coming week I'll get a chance to give it a try, and I'll report back on how it goes. – Soren Bjornstad Feb 22 '16 at 03:43
  • won't work actually, I had a go and purl doesn't like to be called from the document itself (duplicated labels). I'm guessing you'll have to find the current list of parsed chunks from internal knitr functions. – baptiste Feb 22 '16 at 05:59

2 Answers2

6

This is do-able within knitr, no need to use pandoc. Based on an example posted by Yihui at https://github.com/yihui/knitr-examples/blob/master/073-code-appendix.Rnw

Set echo=FALSE throughout your document: opts_chunk$set(echo = FALSE)

Then put this chunk at the end to print all code:

 ```{r show-code, ref.label=all_labels(), echo = TRUE, eval=FALSE}

 ```

This will print code for all chunks. Currently they all show up in a single block; I'd love to figure out how to put in the chunk label or some other header... For now I start my chunks with comments (probably not a bad idea in any case).

Updated: to show only the chunks that were evaluated, use: ref.label = all_labels(!exists('engine')) - see question 40919201

DonJ
  • 923
  • 11
  • 18
  • 1
    Note that the function `all_labels()` is found in knitr, so you also have to load knitr using `library(knitr)` or reference it explicitly: `{r show-code, ref.label=knitr::all_labels(), echo = TRUE, eval=FALSE}`. – Frank Feb 26 '18 at 23:21
2

Since this is quite difficult if not impossible to do with knitr, we can take advantage of the next step, the pandoc compilation, and of pandoc's ability to manipulate content with filters. So we write a normal Rmd document with echo=TRUE and the code chunks are printed as usual when they are called.

Then, we write a filter that finds every codeblock of language R (this is how a code chunk will be coded in pandoc), removes it from the document (replacing it, here, with an empty paragraph) and storing it in a list. We then add the list of all codeblocks at the end of the document. For this last step, the problem is that there really is no way to tell a python filter to add content at the end of a document (there might be a way in haskell, but I don't know it). So we need to add a placeholder at the end of the Rmd document to tell the filter to add the R code at this point. Here, I consider that the placeholder will be a CodeBlock with code lastchunk.

Here is the filter, which we could save as postpone_chunks.py.

#!/usr/bin/env python

from pandocfilters import toJSONFilter, Str, Para, CodeBlock

chunks = []


def postpone_chunks(key, value, format, meta):
    if key == 'CodeBlock':
        [[ident, classes, keyvals], code] = value
        if "r" in classes:
            chunks.append(CodeBlock([ident, classes, keyvals], code))
            return Para([Str("")])
        elif code == 'lastchunk':
            return chunks

if __name__ == "__main__":
    toJSONFilter(postpone_chunks)

Now, we can ask knitr to execute it with pandoc_args. Note that we need to remember to add the placeholder at the end of the document.

---
title: A test
output:
  html_document: 
    pandoc_args: ["--filter", "postpone_chunks.py"]
---

Here is a plot.

```{r}
plot(iris)
```

Here is a table.

```{r}
table(iris$Species)
```

And here are the code chunks used to make them:

    lastchunk

There is probably a better way to write this in haskell, where you won't need the placeholder. One could also customize the way the code chunks are returned at the end to add a title before each one for instance.

scoa
  • 19,359
  • 5
  • 65
  • 80
  • 1
    Works great. The one thing I'd like to add would be a way to write a little bit of text above the code block describing what it goes with. As you say, that should be an easy customization when I have a little more free time to play with it. Also note that I had to do a `sudo pip install pandocfilters` before this worked -- it failed with a very cryptic error rather than the traceback the first time. – Soren Bjornstad Feb 27 '16 at 19:38