yihui results pos name false cache asp r knitr

results - ¿Cómo se pueden usar los resultados en caché de knitr para reproducir el entorno en un fragmento determinado?



knitr results (5)

¿Qué tal agregar el siguiente fragmento de código en la parte inferior de su archivo de rebajas?

```{r save_workspace_if_not_saved_yet, echo=FALSE} if(!file.exists(''knitr_session.RData'')) { save.image(file = ''knitr_session.RData'') } ```

La primera vez que se teje, el estado del área de trabajo al final del proceso se guardará (suponiendo que el proceso no produzca ningún error). Cada vez que desee la última versión de su área de trabajo, simplemente elimine el archivo en su directorio de trabajo.

tl; dr

Mi pregunta: dentro de una sesión R, ¿hay alguna manera de usar los resultados almacenados en caché de knitr para avanzar rápidamente hacia el entorno (es decir, el conjunto de objetos) disponible en un bloque de código dado, en el mismo sentido que knit() sí hace?

Preparar:

El almacenamiento en caché integrado de Knitr de fragmentos de código es una de sus características principales.

Es especialmente útil cuando algunos fragmentos contienen cálculos que requieren mucho tiempo. A menos que se modifiquen (o un fragmento del que dependen), los cálculos solo se deben realizar la primera vez que se knit el documento: en todas las llamadas subsiguientes a knit , los objetos creados por el fragmento solo se cargarán desde el caché.

Aquí hay un ejemplo mínimo, un archivo llamado "lotsOfComps.Rnw" :

/documentclass{article} /begin{document} The calculations in this chunk take a looooong time. <<slowChunk, cache=TRUE>>= Sys.sleep(30) ## Stands in for some time-consuming computation x <- sample(1:10, size=2) @ I wish I could `fast-forward'' to this chunk, to view the cached value of /texttt{x} <<interestingChunk>>= y <- prod(x)^2 y @ /end{document}

Tiempos necesarios para tejer y TeXify "lotsOfComps.Rnw" :

## First time system.time(knit2pdf("lotsOfComps.Rnw")) ## user system elapsed ## 0.07 0.02 31.81 ## Second (and subsequent) runs system.time(knit2pdf("lotsOfComps.Rnw")) ## user system elapsed ## 0.03 0.02 1.28

Mi pregunta:

Dentro de una sesión R, ¿hay alguna manera de usar los resultados almacenados en caché de knitr para "avanzar rápidamente" al entorno (es decir, el conjunto de objetos) disponibles en un bloque de código dado, en el mismo sentido que lo hace knit() ?

Hacer purl("lotsOfComps.Rnw") y luego ejecutar el código en "lotsOfComps.R" no funciona, porque todos los objetos a lo largo del camino deben volver a calcularse.

Idealmente, sería posible hacer algo como esto para terminar en el entorno que existe al principio de <<interestingChunk>>= :

spin("lotsOfComps.Rnw", chunk="interestingChunk") ls() # [1] "x" x # [1] 3 8

Dado que spin() no está (¿todavía?) Disponible, ¿cuál es la mejor manera de obtener el resultado equivalente?


Aquí hay una solución, que todavía es un poco incómoda pero funciona. La idea es agregar una opción de fragmento llamada mute que toma NULL de forma predeterminada, pero también puede tomar una expresión R, por ejemplo, mute_later() continuación. Cuando knitr evalúa las opciones de fragmentos, mute_later() puede evaluarse y se devuelve NULL ; al mismo tiempo, hay efectos secundarios en opts_chunk (configurando las opciones de fragmentos globales como eval = FALSE ).

Ahora, lo que debe hacer es poner mute=mute_later() en el fragmento, después de lo cual desea omitir el resto de los fragmentos, por ejemplo, puede mover esta opción del example-a al example-b . Debido a que mute_later() devuelve NULL que es el valor predeterminado de las opciones de mute , la memoria caché no se romperá, incluso si mueve esta opción.

/documentclass{article} /begin{document} <<setup, include=FALSE, cache=FALSE>>= rm(list = ls(all.names = TRUE), envir = globalenv()) opts_chunk$set(cache = TRUE) # enable cache to make it faster opts_chunk$set(eval = TRUE, echo = TRUE, include = TRUE) # set global options to mute later chunks mute_later = function() { opts_chunk$set(cache = FALSE, eval = FALSE, echo = FALSE, include = FALSE) NULL } # a global option mute=NULL so that using mute_later() will not break cache opts_chunk$set(mute = NULL) @ <<example-a, mute=mute_later()>>= x = rnorm(4) Sys.sleep(5) @ <<example-b>>= y = rpois(10,5) Sys.sleep(5) @ <<example-c>>= z = 1:10 Sys.sleep(3) @ /end{document}

Es incómodo en el sentido de que tienes que cortar y pegar , mute=mute_later() . Idealmente, deberías poner la etiqueta de trozo como la esencia que escribí para Barry.

La razón por la que mi esencia original no funcionó es porque los ganchos de fragmentos se ignoran cuando un fragmento se almacena en caché. La segunda vez que knit() el archivo, se saltó el checkpoint de checkpoint gancho de trozos, por example-a , por lo tanto eval=TRUE para el resto de los trozos, y vio que se evaluaron todos los trozos. En comparación, las opciones de trozos siempre se evalúan dinámicamente.


Este tiene que ser uno de los kludges más feos que he escrito en mucho tiempo ...

La idea básica es escanear el archivo .Rnw en busca de fragmentos, extraer sus nombres, detectar cuáles se almacenan en la memoria caché y luego determinar cuáles se deben cargar. Una vez que lo hagamos, escaneamos paso a paso cada nombre de trozo que se necesita cargar, detectamos el nombre de la base de datos de la carpeta de caché y lo lazyLoad usando lazyLoad . Después de cargar todos los trozos necesitamos forzar la evaluación. Feo y estoy seguro de que hay algunos errores, pero parece funcionar en el ejemplo simple que dio y algunos otros ejemplos mínimos que creé. Esto supone que el archivo .Rnw está en el directorio de trabajo actual ...

load_cache_until <- function(file, chunk, envir = parent.frame()){ require(knitr) # kludge to detect chunk names, which come before the chunk of # interest, and which are cached... there has to be a nicer way... text <- readLines(file) chunks <- grep("^<<.*>>=", text, value = T) chunknames <- gsub("^<<([^,>]*)[,>]*.*", "//1", chunks) #detect unnamed chunks tmp <- grep("^//s*$", chunknames) chunknames[tmp] <- paste0("unnamed-chunk-", seq_along(tmp)) id <- which(chunk == chunknames) previouschunks <- chunknames[seq_len(id - 1)] cachedchunks <- chunknames[grep("cache//s*=//s*T", chunks)] # These are the names of the chunks we want to load extractchunks <- cachedchunks[cachedchunks %in% previouschunks] oldls <- ls(envir, all = TRUE) # For each chunk... for(ch in extractchunks){ # Detect the file name of the database... pat <- paste0("^", ch, ".*//.rdb") val <- gsub(".rdb", "", dir("cache", pattern = pat)) # Lazy load the database lazyLoad(file.path("cache", val), envir = envir) } # Detect the new objects added newls <- ls(envir, all = TRUE) # Force evaluation... There is probably a better way # to do this too... lapply(setdiff(newls, oldls), get) invisible() } load_cache_until("lotsOfComps.Rnw", "interestingChunk")

Hacer el código más robusto se deja como un ejercicio para el lector.


Son como cualquier archivo de datos producido por save . Si obtiene el ejemplo de caché de knitr desde su nueva ubicación , es simplemente:

> library(knitr) > knit("./005-latex.Rtex") > load("cache/latex-my-cache_d9835aca7e54429f59d22eeb251c8b29.RData") > ls() [1] "x"


Yihui gist.github.com/yihui/2629886#file-knitr-checkpoint-rnw idea que se acerca a hacer exactamente lo que pedí.

En respuesta a una pregunta de Barry Rowlingson (también conocido como Spacedman), Yihui construyó un gancho de "punto de control" que le permite al usuario establecer el nombre del último fragmento que será procesado por una llamada para tejer. Para procesar los trozos a través de un example-a llamado example-a , simplemente haga opts_chunk$set(checkpoint = ''example-a'') en algún lugar de un trozo inicial de ''configuración''.

La solución funciona a la perfección: la primera vez que se ejecuta con un punto de control determinado. La segunda y las siguientes, desafortunadamente, knit aparentemente ignora el punto de control y procesa todos los trozos. (Discuto una solución a continuación, pero no es lo ideal).

Aquí hay una versión ligeramente resumida de gist.github.com/yihui/2629886#file-knitr-checkpoint-rnw de gist.github.com/yihui/2629886#file-knitr-checkpoint-rnw :

/documentclass{article} /begin{document} <<setup, include=FALSE>>= rm(list = ls(all.names = TRUE), envir = globalenv()) opts_chunk$set(cache = TRUE) # enable cache to make it faster opts_chunk$set(eval = TRUE, echo = TRUE, include = TRUE) # Define hook that will skip all chunks after the one named in checkpoint knit_hooks$set(checkpoint = function(before, options, envir) { if (!before && options$label == options$checkpoint) { opts_chunk$set(cache = FALSE, eval = FALSE, echo = FALSE, include = FALSE) } }) ## Set the checkpoint opts_chunk$set(checkpoint = ''example-a'') # restore objects up to example-a @ <<example-a>>= x = rnorm(4) @ <<example-b>>= y = rpois(10,5) @ <<example-c>>= z = 1:10 @ /end{document}

Debido a que checkpoint="example-a" , la secuencia de comandos anterior debe ejecutarse a través del segundo fragmento, y luego suprimir todos los fragmentos adicionales, incluidos los que crean y y z . Intentemos eso un par de veces para ver qué pasa:

library(knitr) ## First time, works like a charm knit("checkpoint.Rnw") ls() [1] "x" ## Second time, Oops!, runs right past the checkpoint knit("checkpoint.Rnw") ls() [1] "x" "y" "z"

La solución que mencioné anteriormente es, después del primer análisis, para

  1. Edite checkpoint.Rnw para establecer otro checkpoint (haciendo, por ejemplo, opts_chunk$set(checkpoint = ''example-b'') )
  2. Ejecutar knit("checkpoint.Rnw") ,
  3. Edite checkpoint.Rnw para volver a establecer el punto de control en ''example-a , (al hacer, opts_chunk$set(checkpoint = ''example-a'') )
  4. Ejecute knit("checkpoint.Rnw) una vez más. Esto volverá a procesar todos los fragmentos hasta el example-a .

Esto puede ser mucho más rápido que volver a calcular todos los objetos en los trozos, por lo que es bueno saberlo, incluso si no es lo ideal.