2012-06-10 25 views
47

A menudo tengo un archivo R Markdown principal o un archivo knitr LaTeX donde I source algún otro archivo R (por ejemplo, para el procesamiento de datos). Sin embargo, estaba pensando que en algunos casos sería beneficioso que estos archivos de origen sean sus propios documentos reproducibles (por ejemplo, un archivo R Markdown que no solo incluye comandos para el procesamiento de datos, sino que también produce un documento reproducible que explica el procesamiento de datos). decisiones).Cómo crear un archivo R de marcación como `source ('myfile.r')`?

Por lo tanto, me gustaría tener un comando como source('myfile.rmd') en mi archivo principal de R Markdown. que extraería y fuente todo el código R dentro de los fragmentos de código R de myfile.rmd. Por supuesto, esto da lugar a un error.

Los siguientes comando funciona:

```{r message=FALSE, results='hide'} 
knit('myfile.rmd', tangle=TRUE) 
source('myfile.R') 
``` 

donde results='hide' podrían omitirse si se desea salida. Es decir, knitr genera el código R de myfile.rmd en myfile.R.

Sin embargo, no parece perfecta:

  • da lugar a la creación de un archivo extra
  • tiene que aparecer en su propio trozo de código si se requiere el control de pantalla.
  • No es tan elegante como simple source(...).

Por lo tanto mi pregunta: ¿Hay una forma más elegante de abastecimiento del código R de un archivo de rebajas R?

+0

estoy realmente tener un momento muy difícil entender su pregunta (leí varias veces). Puede poner otros scripts R fácilmente en un archivo 'Rmd'. ¿Pero también desea buscar en otros archivos 'markdown' en un archivo que se está tejiendo? – Maiasaura

+3

Quiero obtener el código R dentro de los fragmentos de código R en los archivos R Markdown (es decir, * .rmd)? He editado la pregunta un poco para tratar de aclarar las cosas. –

+0

Algo parecido a 'incluir' en látex. Si markdown admite la inclusión de otros documentos de rebajas, debería ser relativamente fácil crear dicha función. –

Respuesta

24

Parece que está buscando un trazador de líneas. ¿Qué le parece poner esto en su .Rprofile?

ksource <- function(x, ...) { 
    library(knitr) 
    source(purl(x, output = tempfile()), ...) 
} 

Sin embargo, no entiendo por qué quiere source() el código en el Rmd propio archivo. Me refiero a knit() ejecutará todo el código en este documento, y si extrae el código y lo ejecuta en un trozo, todo el código se ejecutará dos veces cuando knit() este documento (se ejecuta dentro de usted). Las dos tareas deben estar separadas.

Si realmente desea ejecutar todo el código, RStudio ha hecho esto bastante fácil: Ctrl + Shift + R.Básicamente llama a purl() y source() detrás de la escena.

+3

Hola @Yihui Creo que es útil porque a veces tu análisis puede estar organizado en pequeños scripts, pero en tu informe quieres tener el código para todo el canal. – lucacerone

+4

Por lo tanto, el caso de uso aquí es que desea escribir todo el código y tenerlo bien documentado y explicado, pero el código lo ejecuta otro script. –

+2

@BrashEquilibrium Se trata de usar 'source()' o 'knitr :: knit()' para ejecutar el código. Sé que la gente está menos familiarizada con este último, pero 'purl()' no es confiable. Usted ha sido advertido: https://github.com/yihui/knitr/pull/812#issuecomment-53088636 –

2

Si son sólo después de que el código Creo que algo en este sentido debería funcionar:

  1. leer el archivo de rebajas/R con readLines
  2. Uso grep para encontrar los trozos de código, en busca de líneas que comenzar con <<< por ejemplo
  3. Take subconjunto del objeto que contiene las líneas originales para obtener sólo el código
  4. volcado a un archivo temporal utilizando writeLines
  5. Fuente este archivo en su sesión de R

Envolver esto en una función debe darle lo que necesita.

+1

Gracias, supongo que eso funcionaría. Sin embargo, los primeros cuatro puntos suenan como lo que Stangle ya hace de manera confiable para Sweave y lo que 'knit ('myfile.rmd', maraña = VERDADERO)' hace en knitr. Supongo que estoy buscando un trazador de líneas que enrede y fuentes y, idealmente, no crea archivos. –

+0

Una vez que lo envuelve en una función, se convierte en un delineador;). Lo que podría hacer es usar 'textConnection' para imitar un archivo, y la fuente de eso. Esto evitaría que se cree un archivo. –

+0

Sí. 'textConnection' podría ser el lugar para buscar. –

13

Factor el código común a cabo en un archivo de R por separado, y luego se usa como fuente el archivo de R en cada Rmd archivo que desea en.

así por ejemplo vamos a decir que tengo dos informes que tengo que hacer, Brotes de gripe y Guns vs Butter Analysis. Naturalmente, crearía dos documentos Rmd y terminaría con eso.

Supongamos que el jefe llega y quiere ver las variaciones de los brotes de gripe frente a los precios de la mantequilla (controlando la munición de 9 mm).

  • se copia y pega el código para analizar los informes en el nuevo informe es una mala idea para la reutilización de código, etc.
  • quiero que se vea bonito.

Mi solución fue factorizar el proyecto en estos archivos:

  • Flu.Rmd
    • flu_data_import.R
  • Guns_N_Butter.Rmd
    • guns_data_import.R
    • butter_data_import.R

dentro de cada archivo Rmd tendría algo como:

```{r include=FALSE} 
source('flu_data_import.R') 
``` 

El problema aquí es que perdemos la reproducibilidad. Mi solución a eso es crear un documento secundario común para incluir en cada archivo Rmd. Así que al final de cada archivo Rmd creo, añado esto:

```{r autodoc, child='autodoc.Rmd', eval=TRUE} 
``` 

Y, por supuesto, autodoc.Rmd:

Source Data & Code 
---------------------------- 
<div id="accordion-start"></div> 

```{r sourcedata, echo=FALSE, results='asis', warnings=FALSE} 

if(!exists(autodoc.skip.df)) { 
    autodoc.skip.df <- list() 
} 

#Generate the following table: 
for (i in ls(.GlobalEnv)) { 
    if(!i %in% autodoc.skip.df) { 
    itm <- tryCatch(get(i), error=function(e) NA) 
    if(typeof(itm)=="list") { 
     if(is.data.frame(itm)) { 
     cat(sprintf("### %s\n", i)) 
     print(xtable(itm), type="html", include.rownames=FALSE, html.table.attributes=sprintf("class='exportable' id='%s'", i)) 
     } 
    } 
    } 
} 
``` 
### Source Code 
```{r allsource, echo=FALSE, results='asis', warning=FALSE, cache=FALSE} 
fns <- unique(c(compact(llply(.data=llply(.data=ls(all.names=TRUE), .fun=function(x) {a<-get(x); c(normalizePath(getSrcDirectory(a)),getSrcFilename(a))}), .fun=function(x) { if(length(x)>0) { x } })), llply(names(sourced), function(x) c(normalizePath(dirname(x)), basename(x))))) 

for (itm in fns) { 
    cat(sprintf("#### %s\n", itm[2])) 
    cat("\n```{r eval=FALSE}\n") 
    cat(paste(tryCatch(readLines(file.path(itm[1], itm[2])), error=function(e) sprintf("Could not read source file named %s", file.path(itm[1], itm[2]))), sep="\n", collapse="\n")) 
    cat("\n```\n") 
} 
``` 
<div id="accordion-stop"></div> 
<script type="text/javascript"> 
```{r jqueryinclude, echo=FALSE, results='asis', warning=FALSE} 
cat(readLines(url("http://code.jquery.com/jquery-1.9.1.min.js")), sep="\n") 
``` 
</script> 
<script type="text/javascript"> 
```{r tablesorterinclude, echo=FALSE, results='asis', warning=FALSE} 
cat(readLines(url("http://tablesorter.com/__jquery.tablesorter.js")), sep="\n") 
``` 
</script> 
<script type="text/javascript"> 
```{r jqueryuiinclude, echo=FALSE, results='asis', warning=FALSE} 
cat(readLines(url("http://code.jquery.com/ui/1.10.2/jquery-ui.min.js")), sep="\n") 
``` 
</script> 
<script type="text/javascript"> 
```{r table2csvinclude, echo=FALSE, results='asis', warning=FALSE} 
cat(readLines(file.path(jspath, "table2csv.js")), sep="\n") 
``` 
</script> 
<script type="text/javascript"> 
    $(document).ready(function() { 
    $('tr').has('th').wrap('<thead></thead>'); 
    $('table').each(function() { $('thead', this).prependTo(this); }); 
    $('table').addClass('tablesorter');$('table').tablesorter();}); 
    //need to put this before the accordion stuff because the panels being hidden makes table2csv return null data 
    $('table.exportable').each(function() {$(this).after('<a download="' + $(this).attr('id') + '.csv" href="data:application/csv;charset=utf-8,'+encodeURIComponent($(this).table2CSV({delivery:'value'}))+'">Download '+$(this).attr('id')+'</a>')}); 
    $('#accordion-start').nextUntil('#accordion-stop').wrapAll("<div id='accordion'></div>"); 
    $('#accordion > h3').each(function() { $(this).nextUntil('h3').wrapAll("<div>"); }); 
    $('#accordion').accordion({ heightStyle: "content", collapsible: true, active: false }); 
</script> 

NB, esto está diseñado para la Rmd -> flujo de trabajo html . Esto será un desastre si vas con latex o cualquier otra cosa. Este documento Rmd busca en el entorno global todos los archivos fuente() e incluye su fuente al final de su documento. Incluye jquery ui, tablesorter y configura el documento para usar un estilo de acordeón para mostrar/ocultar los archivos de origen. Es un trabajo en progreso, pero puede adaptarlo a sus propios usos.

No es un trazador de líneas, lo sé. Espero que te dé algunas ideas al menos :)

1

Probablemente uno debería empezar a pensar diferente. Mi problema es el siguiente: Escribe cada código que normalmente tendrías en un fragmento .Rmd en un archivo .R. Y para el documento Rmd utiliza a tejer es decir, un HTML, sólo han dejado

```{R Chunkname, Chunkoptions} 
source(file.R) 
``` 

De esta manera es probable que va a crear un montón de archivos .R y se pierde la ventaja de procesar todo el código "trozo after chunk "usando ctrl + alt + n (o + c, pero normalmente esto no funciona). Pero, leí el libro sobre investigación reproducible por el Sr. Gandrud y me di cuenta de que definitivamente usa knitr y.Archivos Rmd únicamente para crear archivos html. El análisis principal en sí es un archivo .R. Creo que los documentos .Rmd crecen demasiado rápido si comienzas a hacer todo tu análisis dentro.

0

Recomendaría mantener el código principal de análisis y cálculo en el archivo .R e importar los fragmentos según sea necesario en el archivo .Rmd. He explicado el proceso here.

1

este truco funcionó bien para mí:

library(readr) 
library(stringr) 
source_rmd <- function(file_path) { 
    stopifnot(is.character(file_path) && length(file_path) == 1) 
    .tmpfile <- tempfile(fileext = ".R") 
    .con <- file(.tmpfile) 
    on.exit(close(.con)) 
    full_rmd <- read_file(file_path) 
    codes <- str_match_all(string = full_rmd, pattern = "```(?s)\\{r[^{}]*\\}\\s*\\n(.*?)```") 
    stopifnot(length(codes) == 1 && ncol(codes[[1]]) == 2) 
    codes <- paste(codes[[1]][, 2], collapse = "\n") 
    writeLines(codes, .con) 
    flush(.con) 
    cat(sprintf("R code extracted to tempfile: %s\nSourcing tempfile...", .tmpfile)) 
    source(.tmpfile) 
} 
Cuestiones relacionadas