2012-08-23 14 views
36

Sto cercando di utilizzare knitr per generare un report che esegua lo stesso set di analisi su diversi sottoinsiemi di un set di dati. Il progetto contiene due file Rmd: il primo file è un documento master che imposta lo spazio di lavoro e il documento, il secondo file contiene solo blocchi che eseguono le analisi e generano figure associate.R knitr: è possibile modificare a livello di programmazione le etichette dei blocchi?

Quello che mi piacerebbe fare è lavorare a maglia il file master, che chiamerebbe quindi il secondo file per ogni sottoinsieme di dati e includerebbe i risultati in un singolo documento. Di seguito è un semplice esempio.

documento

Master:

# My report 

```{r} 
library(iterators) 
data(mtcars) 
``` 

```{r create-iterator} 
cyl.i <- iter(unique(mtcars$cyl)) 
``` 

## Generate report for each level of cylinder variable 
```{r cyl4-report, child='analysis-template.Rmd'} 
``` 

```{r cyl6-report, child='analysis-template.Rmd'} 
``` 

```{r cyl8-report, child='analysis-template.Rmd'} 
``` 

analisi-template.Rmd:

```{r, results='asis'} 
cur.cyl <- nextElem(cyl.i) 
cat("###", cur.cyl) 
``` 

```{r mpg-histogram} 
hist(mtcars$mpg[mtcars$cyl == cur.cyl], main = paste(cur.cyl, "cylinders")) 
``` 

```{r weight-histogam} 
hist(mtcars$wt[mtcars$cyl == cur.cyl], main = paste(cur.cyl, "cylinders")) 
``` 

Il problema è knitr non permette di etichette chunk non univoci, in modo da lavorare a maglia non riesce quando analysis-template.Rmd viene chiamato il secondo tempo. Questo problema potrebbe essere evitato lasciando i chunks senza nome poiché le etichette univoche verrebbero automaticamente generate. Questo non è l'ideale, tuttavia, perché mi piacerebbe utilizzare le etichette del blocco per creare nomi di file informativi per i grafici esportati.


Una possibile soluzione sarebbe utilizzando una semplice funzione che accoda il cilindro corrente all'etichetta chunk:

```r{paste('cur-label', cyl, sep = "-")} 
``` 

Ma non sembra che knitr calcolerà un'espressione nella posizione dell'etichetta pezzo .


Ho anche provato ad utilizzare una consuetudine chunk hook che ha modificato l'etichetta del pezzo corrente:

knit_hooks$set(cyl.suffix = function(before, options, envir) { 
    if (before) options$label <- "new-label" 
}) 

Ma cambiare l'etichetta pezzo non ha influenzato i nomi dei file per i grafici generati, quindi non ho la pensano knitr stava usando la nuova etichetta.


Qualche idea su come modificare le etichette dei blocchi in modo che lo stesso documento figlio possa essere chiamato più volte? O forse una strategia alternativa per realizzare questo?

risposta

35

Per chiunque incontrasse questo post, volevo sottolineare che @Yihui ha fornito un numero formal solution a questa domanda in knitr 1.0 con l'introduzione della funzione knit_expand(). Funziona alla grande e ha davvero semplificato il mio flusso di lavoro.

Ad esempio, il seguente elaborerà lo script per i modelli di seguito per ogni livello di mtcars$cyl, ogni volta sostituendo tutte le istanze di {{ncyl}} (nel modello), con il suo valore corrente:

# My report 

```{r} 
data(mtcars) 
cyl.levels <- unique(mtcars$cyl) 
``` 

## Generate report for each level of cylinder variable 
```{r, include=FALSE} 
src <- lapply(cyl.levels, function(ncyl) knit_expand(file = "template.Rmd")) 
``` 

`r knit(text = unlist(src))` 

Template:

```{r, results='asis'} 
cat("### {{ncyl}} cylinders") 
``` 

```{r mpg-histogram-{{ncyl}}cyl} 
hist(mtcars$mpg[mtcars$cyl == {{ncyl}}], 
    main = paste({{ncyl}}, "cylinders")) 
``` 

```{r weight-histogam-{{ncyl}}cyl} 
hist(mtcars$wt[mtcars$cyl == {{ncyl}}], 
    main = paste({{ncyl}}, "cylinders")) 
``` 
+0

Ho usato questo approccio ma ho notato che l'uso di 'echo = FALSE' nel modello ha come risultato che il codice non è stato elaborato. Hai notato lo stesso comportamento? –

+0

** Modifica **: ho utilizzato questo approccio insieme al pacchetto ** bookdown ** e ho notato che l'utilizzo di 'results = 'asis'' e' echo = FALSE' nel modello determina che il codice non è stato elaborato. La soluzione è di avere ogni output in un chunk di codice separato. –

14

Se si effettuano tutti i blocchi nel proprio ** senza nome, ovvero ```{r}, funziona. Questo, ovviamente, non è molto elegante, ma ci sono due problemi che ti impediscono di modificare l'etichetta del blocco corrente:

  1. Un file viene analizzato prima dell'esecuzione dei blocchi di codice. Il parser rileva già etichette duplicate, prima che venga eseguito qualsiasi codice o vengano chiamati hook personalizzati.
  2. Le opzioni di blocco (inclusa l'etichetta) vengono elaborate prima che venga richiamato l'hook (logico: è un'opzione che attiva un hook), quindi il gancio non può più modificare l'etichetta.

Il fatto che i blocchi senza nome funzionino è che internamente ricevono l'etichetta unnamed-chunk- + numero di blocco.

I blocchi non possono avere nomi duplicati come internamente li riferimenti a knitr per etichetta. Una correzione potrebbe essere quella di fare in modo che knitr aggiunga il numero del blocco a tutti i blocchi con nomi duplicati.O per fare riferimento a loro per numero di blocco anziché per etichetta, ma questo mi sembra un cambiamento molto più grande.

+7

La tua comprensione è assolutamente corretta, e questo è un caso convincente che knitr ha bisogno di alcuni cambiamenti. Sto guardando la tua richiesta di pull ora. Grazie! –

+0

@Rolo, la tua spiegazione dei meccanismi interni di knitr è stata di grande aiuto. E apprezzo molto il tempo dedicato a scrivere [il codice che implementa la tua soluzione] (https://github.com/yihui/knitr/issues/368). @Yihui, pensi che includerai questo cambiamento? Tratterebbe il 90% di ciò che stavo cercando di realizzare e non mi renderebbe necessario mantenere copie di file Rmd identici, ad eccezione delle etichette modificate. La soluzione ideale consentirebbe qualcosa come 'for (i in unique (mtcars $ cyl)) knit_child (" analysis-template.Rmd ", label.suffix = i)', se fosse plausibile. – aaronwolen

+1

Sì, penso che tendo ad accettare la richiesta di pull; dammi qualche altro minuto perché ho ancora un paio di soluzioni alternative. È facile risolvere questo problema, ma è difficile decidere quale soluzione utilizzare. –

Problemi correlati