2015-12-28 12 views
11

knitr rende PDF fuori codice che è una combinazione di (nel mio caso) R e LaTEX. Si può assemblare un documento dai documenti figli.Come si nascondono e si passano variabili in documenti child knitr?

Come lo uso in questo momento, il documento è assemblato fuori delle variabili globali, passati in e fuori di ogni documento secondario. Questo facilita la produzione di codice spaghetti.

C'è un modo per rendere le variabili R "locale" ad un doc bambino? Che ne dite di rendere esplicita l'esportazione di variabili?

potevo NULL fuori ogni variabile locale alla fine di un documento figlio, ma mi chiedo se c'è qualche ragionevole meccanismo formale per allentare l'accoppiamento codice tra documenti figli.

+0

Se non si vuole il documento principale e i documenti figlio per interagire, perché usi i documenti figli? Se si desidera combinare diversi file di origine (Rnw) in un documento LaTeX, è possibile 'knit' i file Rnw separatamente e quindi includerli nel documento principale tramite' \ include {} '. –

+0

Voglio che interagiscano, ma in modo controllato.In un normale linguaggio di programmazione, si fanno funzioni con parametri e un valore di ritorno. Ciò consente chiarezza sull'accoppiamento. – dfrankow

risposta

11

knitr valuta tutti i blocchi in un ambiente comune (restituito da knit_global()). Questo è di design; proprio come tutto il codice in un file sorgente viene eseguito nello stesso ambiente, tutti i blocchi vengono eseguiti in un ambiente comune. Lo stesso vale per child documents perché sono (in linea di principio, non tecnicamente) solo una parte del documento principale, esternalizzata ad un altro file.

Questo non porta necessariamente al codice spaghetti: Nulla impedisce agli utenti di utilizzare funzioni e altri oggetti per organizzare codice/dati nei documenti knitr. Ma probabilmente pochi utenti fanno ...

Quindi la ragione per la quale non vi sono meccanismi di incapsulamento per chunks documenti/bambino è che essi sono suppone di condividere un ambiente comune in quanto sono parte di una documento (principale).

Tuttavia, lo è possibile includere documenti figlio in un modo che consenta all'utente il controllo degli oggetti documenti figlio e della condivisione documento principale. La soluzione è basata sulla funzione knit_child() che è molto simile allo chunk optionchild. Il vantaggio di chiamare knit_child() direttamente (vs implicitamente tramite l'opzione child) è la possibilità di impostare l'argomento envir che definisce "l'ambiente in cui i pezzi di codice devono essere valutati" (da ?knit).

Intorno knit_child(), ho scritto l'involucro IsolatedChild per semplificare le cose:

IsolatedChild <- function(input, ...) { 

    evaluationEnv <- list2env(x = list(...), parent = as.environment(2)) 
    cat(asis_output(knit_child(input = input, envir = evaluationEnv, quiet = TRUE))) 
    return(evaluationEnv) 
} 

Argomenti passati al ... saranno disponibili nel documento bambino. (Assegnare loro un nome, vedere l'esempio seguente). La funzione restituisce l'ambiente in cui è stato valutato il documento figlio.

Specificare parent in list2env è fondamentale e ho scelto as.environment(2) secondo this answer. In caso contrario, sarebbe parent default parent.frame(), esponendo così gli oggetti in knit_global() al documento bambino.

assign può essere utilizzato per rendere gli oggetti restituiti da IsolatedChild disponibili nell'ambiente globale.

Nota la cat(asis_output()) costruzione attorno knit_child che assicura che l'uscita dal documento bambino è incluso correttamente nel documento principale, indipendentemente dall'impostazione results nel chunk corrente.

Prima di passare alla esempio, due osservazioni finali:

  • Se il bambino e il documento principale non sono tenuti a condividere eventuali oggetti, questo approccio è eccessivamente complesso. Semplicemente knit il documento figlio e utilizzare \include{} per includerlo nel documento principale.
  • Questo approccio potrebbe presentarsi con alcune insidie. Soprattutto l'ambiente che racchiude il "bambino isolato" richiede cautela perché il percorso di ricerca potrebbe apparire diverso dal previsto. Si noti che il documento principale e secondario condivide le opzioni knitr. Inoltre, entrambi i documenti potrebbero interagire tramite effetti collaterali (options(), par(), dispositivi aperti, ...).

Qui di seguito un esempio completo/demo:

  • Il pezzo inputNormal non fa nulla di speciale, è solo una dimostrazione del comportamento normale. inputHidden dimostra l'uso di IsolatedChild(), passando due variabili al documento figlio.
  • IsolatedChild() restituisce questi due valori insieme a un terzo oggetto creato nel figlio.
  • check dimostra che gli oggetti passati/creati nel "figlio isolato" non inquinano l'ambiente globale.
  • import mostra come assign può essere utilizzato per "importare" un oggetto dal "figlio isolato" nell'ambiente globale.

main.Rnw:

\documentclass{article} 
\begin{document} 

<<setup>>= 
library(knitr) 

objInMain <- TRUE 

IsolatedChild <- function(input, ...) { 

    evaluationEnv <- list2env(x = list(...), parent = as.environment(2)) 
    cat(asis_output(knit_child(input = input, envir = evaluationEnv, quiet = TRUE))) 
    return(evaluationEnv) 
} 

@ 

<<inputNormal, child="child_normal.Rnw">>= 
@ 

<<inputHidden, results = "asis">>= 

returned <- IsolatedChild(input = "child_hidden.Rnw", 
          passedValue = 42, 
          otherPassedValue = 3.14) 
cat(sprintf("Returned from hidden child: \\texttt{%s}", 
      paste(ls(returned), collapse = ", "))) 
@ 

<<check, results = "asis">>= 
cat(sprintf("In global evaluation environment: \\texttt{%s}", 
      paste(ls(), collapse = ", "))) 
@ 

<<import, results = "asis">>= 
assign("objInChildHidden", returned$objInChildHidden) 
cat(sprintf("In global evaluation environment: \\texttt{%s}", 
      paste(ls(), collapse = ", "))) 
@ 
\end{document} 

child_normal.Rnw:

<<inChildNormal>>= 
objInChildNormal <- TRUE # visible in main.Rnw (standard behaviour) 
@ 

child_hidden.Rnw:

Text in \texttt{child\_hidden.Rnw}. 

<<inChildHidden>>= 
objInChildHidden <- TRUE 

print(sprintf("In hidden child: %s", 
       paste(ls(), collapse = ", "))) 


# Returns FALSE. 
# Would be TRUE if "parent" weren't specifiet in list2env(). 
exists("objInMain", inherits = TRUE) 
@ 

main.pdf:

main.pdf

+2

Questo è fantastico, grazie! Ho appena iniziato a conoscere gli ambienti oggi. Sembrano il meccanismo giusto per passare le cose. – dfrankow

+0

Grazie per questo fantastico post. Mi piacerebbe poter condividere l'ambiente del documento genitore con i bambini, ma voglio isolare i bambini dagli ambienti degli altri; è questo l'approccio corretto? Prima di eseguire 'IsolatedChild', dovrei fare una copia dell'ambiente globale e passarlo come' parent' in 'list2env'? – user5359531

+0

@ user5359531 Penso che questo dipenda: cosa ti aspetti che succeda se un bambino modifica oggetti nell'ambiente del documento genitore? Ma probabilmente dovresti fare una nuova domanda per quel problema, collegandoti a questo. –

Problemi correlati