rknitrrnw

With knitr, preserve chunk options when purling chunks into separate files


For teaching purposes, I would like to purl the chunks of my .Rnw file into separate files. This answer explains how to do it:

How to purl each chunks in .Rmd file to multiple .R files using Knitr

BUT the method does not preserve the chunk options. Since the chunks I have produce plots, it's important to preserve the fig.width and fig.height options. Ideally I would like a chunk that looks like this:

<<plot, fig.width = 3, fig.height = 5, outwidth = '.75\\textwidth'>>=
plot (1,1)
@

to become a file named plot.R that looks like this:

#+ fig.width = 3, fig.height = 5
plot (1,1)

That is, turn the chunk options fig.width and fig.height into a format that will be recognized by spin(), as purl() does, and get rid of the chunk options that are irrelevant, or create problems for spin() into Word, such as out.width. All in the spirit of creating code files that are user-friendly.


Solution

  • Since the answer you refer to doesn't copy the header line from the results of purl, you lose everything besides the chunk name. While you could adapt it to paste in the headers, it's actually not hard to build a function to parse the output of purl—much easier than trying to parse a Rmd or Rnw document, anyway, and easier than sorting out exactly how knitr does so.

    purl_chunks <- function(input_file){
      purled <- knitr::purl(input_file)    # purl original file; save name to variable
      lines <- readLines(purled)    # read purled file into a character vector of lines
      starts <- grep('^## ----.*-+', lines)    # use grep to find header row indices
      stops <- c(starts[-1] - 1L, length(lines))   # end row indices
      # extract chunk names from headers
      names <- sub('^## ----([^-]([^,=]*[^,=-])*)[,-][^=].*', '\\1', lines[starts])
      names <- ifelse(names == lines[starts], '', paste0('_', names)) # clean if no chunk name
      # make nice file names with chunk number and name (if exists)
      file_names <- paste0('chunk_', seq_along(starts), names, '.R')
      for(chunk in seq_along(starts)){    # loop over header rows
        # save the lines in the chunk to a file
        writeLines(lines[starts[chunk]:stops[chunk]], con = file_names[chunk])
      }
      unlink(purled)    # delete purled file of entire document
    }
    

    A couple notes: