13
votes

I am knitting a .Rmd file and want to have two outputs: the html and a purl'ed R script each time I run knit. This can be done with the following Rmd file:

---
title: "Purl MWE"
output: html_document
---

```{r}
## This chunk automatically generates a text .R version of this script when     running within knitr.
input  = knitr::current_input()  # filename of input document
output = paste(tools::file_path_sans_ext(input), 'R', sep = '.')
knitr::purl(input,output,documentation=1,quiet=T)
```

```{r}
x=1
x
```

If you do not name the chunk, it works fine and you get html and .R output each time you run knit() (or click knit in RStudio).

However, if you name the chunk it fails. For example:


title: "Purl MWE"
output: html_document
---

```{r}
## This chunk automatically generates a text .R version of this script when     running within knitr.
input  = knitr::current_input()  # filename of input document
output = paste(tools::file_path_sans_ext(input), 'R', sep = '.')
knitr::purl(input,output,documentation=1,quiet=T)
```


```{r test}
x=1
x
```

It fails with:

Quitting from lines 7-14 (Purl.Rmd) 
Error in parse_block(g[-1], g[1], params.src) : duplicate label 'test'
Calls: <Anonymous> ... process_file -> split_file -> lapply -> FUN -> parse_block
Execution halted

If you comment out the purl() call, it will work with the named chunk. So there is something about how the purl() call is also naming chunks which causes knit() to think there are duplicate chunk names even when there are no duplicates.

Is there a way to include a purl() command inside a .Rmd file so both outputs (html and R) are produced? Or is there a better way to do this? My ultimate goal is to use the new rmarkdown::render_site() to build a website that updates the HTML and R output each time the site is compiled.

3
Did you ever figure this out?Jordan
I have this problem too... very frustrating. I'd like to name my chunks and I can't currently because of this.Nova

3 Answers

6
votes

You can allow duplicate labels by including options(knitr.duplicate.label = 'allow') within the file as follows:

title: "Purl MWE"
output: html_document
---

```{r GlobalOptions}
options(knitr.duplicate.label = 'allow')
```


```{r}
## This chunk automatically generates a text .R version of this script when     running within knitr.
input  = knitr::current_input()  # filename of input document
output = paste(tools::file_path_sans_ext(input), 'R', sep = '.')
knitr::purl(input,output,documentation=1,quiet=T)
```


```{r test}
x=1
x
```

This code isn't documented on the knitr website, but you can keep track with the latest changes direct from Github: https://github.com/yihui/knitr/blob/master/NEWS.md

0
votes

You can avoid this error with a bash chunk that calls purl in a separate R session. That way there's no need to allow duplicate labels.

An example use case is an Rmd file where the code is run (and not echo'd) throughout the report and then all the code chunks are shown with chunks names and code comments in an Appendix. If you don't require that additional functionality then you would only need up until the bash chunk.

The idea is that report_end signifies where to stop purl such that the appendix code isn't considered "report code". Then read_chunk reads the entire R file into one code chunk which can then be echo'd with syntax highlighting if required.

---
title: "Purl MWE"
output: html_document
---

These code chunks are used in the background of the report however
their source is not shown until the Appendix.

```{r test1, echo=FALSE}
x <- 1
x
```

```{r test2, echo=FALSE}
x <- x + 1
x
```

```{r test3, echo=FALSE}
x <- x + 1
x
```

# Appendix

```{r, eval=TRUE}
report_end <- "^# Appendix"
temp <- tempfile(fileext = ".R")
Sys.setenv(PURL_IN = shQuote("this_file.Rmd"), # eg. knitr::current_input()
           PURL_OUT = shQuote(temp),
           PURL_END = shQuote(report_end))
```


```{bash, include=FALSE}
Rscript -e "lines <- readLines($PURL_IN, warn = FALSE)" \
        -e "knitr::purl(text = lines[1:grep($PURL_END, lines)], output = $PURL_OUT, documentation = 1L)"
```

```{r, include=FALSE}
knitr::read_chunk(temp, labels = "appendix")
unlink(temp)
```

```{r appendix, eval=FALSE, echo=TRUE}
```
0
votes

A related approach to @ruaridhw solution would be to wrap the knitr::purl() in callr::r(). See function below that saves the R chunks from a specified R markdown file to a temporary .R file:

# RMD to local R temp file
# inspiration: https://gist.github.com/noamross/a549ee50e8a4fd68b8b1
rmd_chunks_to_r_temp <- function(file){

  temp <- tempfile(fileext=".R")

  # needed callr so can use when knitting -- else can bump into "duplicate chunk
  # label" errors when running when knitting
  callr::r(function(file, temp){
    knitr::purl(file, output = temp)
  },
  args = list(file, temp))
}

This function also exists in funspotr:::rmd_chunks_to_r_temp() at brshallo/funspotr.