21
votes

When I purl/tangle a document to extract the R chunks into a script, is there any way to:

  • exclude an arbitrary chunk (by name say)?
  • if not, exclude a chunk if eval=F (or perhaps I can define a chunk hook/option include=F)?

For example, suppose I have the following Rmd:

```{r setup, echo=F}
library(MASS)
```
First, we perform the setup (assume for some reason I need to evaluate `setup`
silently before I wish to display the chunk to the user, hence the repetition)
```{r setup, eval=F}
```

Here's the function I've been explaining:
```{r function}
plus <- function (a, b) a + b
```

And here's an example of its use:
```{r example}
plus(1, 2)
```

The tangled script looks like this:

## @knitr setup, echo=F
library(MASS)   

## @knitr setup, eval=F
library(MASS)

## @knitr function
plus <- function (a, b) a + b

## @knitr example
plus(1, 2)

I have the idea that since I didn't want particular chunks to be evaluated, they at the very least should not appear in the output (in the example above, the second setup chunk).

Additionally it would be nice for me to mark some chunks as "invisible" with respect to the tangled output. I don't want the example chunk in my output script (it was nice in the Rmd for purposes of documentation, but I want to be able to tangle the Rmd and then just source('myfile.r') if I wish to use the plus function, without having to worry about these extra examples executing. Currently I tangle the Rmd and then manually edit out the chunks I don't want out of the script, which seems against the principle of just writing the one Rmd which will provide both documentation and script without extra effort.)

3
Note - marking a chunk with include=F will remove it from the purl'd output, but also from the Rmd (in the version of knitr from github that is; feature doesn't seem present on my computer's version 1.2), so this is not ideal for my example block (where I want it included in the Rmd and removed from the purl'd output) - mathematical.coffee
there is certainly a lot of room for improvement of purl(), which is less frequently used, hence less well developed - Yihui Xie
Is there no way to just extract r chunks from an Rmd? - KLDavenport
@KLDavenport to extract all R chunks from an Rmd into an R script, you use purl. It extracts all of the chunks, and I'm interested in extracting just some of them (or even extracting particular chunks to particular output files). - mathematical.coffee
I couldn't get a simple export of all chunks to work but I'll look over the documentation again. Bummer that you can set config per chunk for export in your case. - KLDavenport

3 Answers

26
votes

Since knitr 1.3, there is a new chunk option purl = TRUE/FALSE that allows one to include/exclude certain code chunks for purl().

```{r test, purl=FALSE}
library(MASS)
```
7
votes

The tangle processing block doesn't currently expand params, but we can make it do so...

# See `?trace` documentation for details.
bdy <- as.list( body( knitr:::process_tangle.block ) )
trace.at <- which( grepl(".*opts_chunk\\$merge.*",
                         as.list( body( knitr:::process_tangle.block ) ) ) )
tracer <- quote({
  # Code borrowed from normal chunk procesing.
  af = opts_knit$get('eval.after'); al = opts_knit$get('aliases')
  if (!is.null(al) && !is.null(af)) af = c(af, names(al[af %in% al]))
  for (o in setdiff(names(params), af)) params[o] = list(eval_lang(params[[o]]))
  # Omit this if using lastest knitr source from github.
  if( isFALSE( params$include ) ) {
    tmp <- knit_code$get();
    tmp[[params$label]] <- "";
    knit_code$restore(tmp)
  }
})

trace( knitr:::process_tangle.block, tracer=tracer, at=trace.at, print=FALSE )

Then purl() exclusion can be controlled using option arguments...

```{r setup, echo=TRUE, results='hide'}
library(MASS)
````

First, we perform the setup (assume for some reason I need to evaluate `setup`
silently before I wish to display the chunk to the user, hence the repetition)
```{r setup2, ref.label="setup", echo=FALSE, results='markup'}
```

Here's the function I've been explaining:
```{r function}
plus <- function (a, b) a + b
```

And here's an example of its use:
```{r example, eval=!opts_knit$get("tangle") }
plus(1, 2)
```

And here's another example of its use:
```{r example2, include=!opts_knit$get("tangle") }
plus(3, 3)
```
3
votes

Although this is more a trick than a solution, you can still modify the script resulting from purl with some regular expressions.

For example with the following function (maybe there is a simpler solution for the regex) :

dropchunks <- function(scriptname, what.to.drop){
    script <- readLines(scriptname)
    script <- do.call(paste, list(script, collapse = "\n") )
    subpattern = paste0("(", do.call(paste, list(what.to.drop, collapse="|")), ")")
    mainpattern <- paste('(?s)## @knitr ((?!## @knitr).)*?', subpattern, '.*?((?=## @knitr)|$)', sep="")
    script <- gsub(pattern = mainpattern, replacement = "", x = script, perl=TRUE)
    writeLines(text = script, con= scriptname)
}

You can then do this to remove all code chunk containing eval=F :

library(knitr)
purl("test.Rmd")
dropchunks("test.R", "eval=F")

You can do this to remove the chunk named "function" or "example" (and in fact it will remove any chunk that contains these words somewhere but this could be changed by changing the regex):

purl("test.Rmd")
dropchunks("test.R", c("function", "example"))