Opciones para el almacenamiento en caché / memoria / hash en R

Estoy tratando de encontrar una forma simple de usar algo como las funciones hash de Perl en R (esencialmente caché), ya que tenía la intención de hacer hash al estilo Perl y escribir mi propia memoración de cálculos. Sin embargo, otros me han golpeado y tienen paquetes para la memorización. Cuanto más excavo, más encuentro, por ejemplo, memoise y R.cache , pero las diferencias no son claras. Además, no está claro cómo se pueden obtener hashes al estilo Perl (o diccionarios al estilo de Python) y escribir las propias memorias, que no sean el uso del paquete hash , que no parece respaldar los dos paquetes de memomentación.

Dado que no puedo encontrar información sobre CRAN o en otro lugar para distinguir entre las opciones, quizás esta debería ser una pregunta wiki de la comunidad en SO: ¿Cuáles son las opciones para la memorización y el almacenamiento en caché en R, y cuáles son sus diferencias?


Como base de comparación, aquí hay una lista de las opciones que he encontrado. Además, me parece que todos dependen del hashing, así que también anotaré las opciones de hash. El almacenamiento clave / valor está relacionado de alguna manera, pero abre una gran cantidad de gusanos con respecto a los sistemas DB (por ejemplo, BerkeleyDB, Redis, MemcacheDB y muchos otros ).

Parece que las opciones son:

Hashing

  • digest : proporciona hash para objetos arbitrarios.

Memoization

  • memoise – una herramienta muy simple para memorizar funciones.
  • R.cache : ofrece más funcionalidades para la memorización, aunque parece que algunas de las funciones carecen de ejemplos.

Almacenamiento en caché

  • hash : proporciona una funcionalidad de almacenamiento en caché similar a los valores hash de Perl y los diccionarios de Python.

Almacenamiento clave / valor

Estas son opciones básicas para el almacenamiento externo de objetos R.

  • stashr
  • filehash

Punto de referencia

  • cacher : esto parece ser más parecido a un punto de control .
  • CodeDepends : un proyecto OmegaHat que apuntala a cacher y proporciona algunas funcionalidades útiles.
  • DMTCP (no es un paquete R): parece ser compatible con los puntos de control en varios idiomas, y un desarrollador buscó recientemente ayuda para probar el punto de control DMTCP en R.

Otro

  • Base R admite: vectores y listas nombrados, nombres de fila y columna de marcos de datos y nombres de elementos en entornos. Me parece que usar una lista es un poco complicado. (También hay pairlist , pero está en desuso ).
  • El paquete data.table admite búsquedas rápidas de elementos en una tabla de datos.

Caso de uso

Aunque estoy más interesado en conocer las opciones, tengo dos casos de uso básicos que surgen:

  1. Almacenamiento en caché: conteo simple de cadenas. [Nota: Esto no es para NLP, sino para uso general, por lo que las bibliotecas de NLP son excesivas; las tablas son inadecuadas porque prefiero no esperar hasta que todo el conjunto de cadenas se haya cargado en la memoria. Los hash estilo Perl están en el nivel correcto de utilidad.]
  2. Memoración de cálculos monstruosos.

Realmente surgen porque estoy investigando el perfil de algunos códigos poco precisos y me gustaría simplemente contar cadenas simples y ver si puedo acelerar algunos cálculos mediante la memorización. Ser capaz de ajustar los valores de entrada, incluso si no memomo, me permitiría ver si la memorización puede ayudar.


Nota 1: La vista de tareas de CRAN en Reproducible Research enumera algunos de los paquetes ( cacher y R.cache ), pero no hay ninguna R.cache sobre las opciones de uso.

Nota 2: Para ayudar a otros a buscar código relacionado, aquí algunas notas sobre algunos de los autores o paquetes. Algunos de los autores usan SO. 🙂

  • Dirk Eddelbuettel: digest : muchos otros paquetes dependen de esto.
  • Roger Peng: cacher , filehash , stashR : abordan diferentes problemas de diferentes maneras; vea el sitio de Roger para más paquetes.
  • Christopher Brown: hash : parece ser un paquete útil, pero desafortunadamente los enlaces a ODG están bajos.
  • Henrik Bengtsson: R.cache y Hadley Wickham: memoise : memoise no está claro cuándo preferir un paquete sobre el otro.

Nota 3: Algunas personas usan memoise / memoisation y otras usan memoize / memoization. Solo una nota si estás buscando. Henrik usa “z” y Hadley usa “s”.

Para el conteo simple de cadenas (y no el uso de table o similares), una estructura de datos multiservicio parece una buena opción. El objeto de environment se puede usar para emular esto.

 # Define the insert function for a multiset msetInsert <- function(mset, s) { if (exists(s, mset, inherits=FALSE)) { mset[[s]] <- mset[[s]] + 1L } else { mset[[s]] <- 1L } } # First we generate a bunch of strings n <- 1e5L # Total number of strings nus <- 1e3L # Number of unique strings ustrs <- paste("Str", seq_len(nus)) set.seed(42) strs <- sample(ustrs, n, replace=TRUE) # Now we use an environment as our multiset mset <- new.env(TRUE, emptyenv()) # Ensure hashing is enabled # ...and insert the strings one by one... for (s in strs) { msetInsert(mset, s) } # Now we should have nus unique strings in the multiset identical(nus, length(mset)) # And the names should be correct identical(sort(ustrs), sort(names(as.list(mset)))) # ...And an example of getting the count for a specific string mset[["Str 3"]] # "Str 3" instance count (97) 

No tuve suerte con memoise porque daba too deep recursive problema too deep recursive a alguna función de un paquete que probé. Con R.cache tuve mejor suerte. Lo que sigue es un código más anotado que he adaptado de la documentación de R.cache . El código muestra diferentes opciones para hacer el almacenamiento en caché.

 # Workaround to avoid question when loading R.cache library dir.create(path="~/.Rcache", showWarnings=F) library("R.cache") setCacheRootPath(path="./.Rcache") # Create .Rcache at current working dir # In case we need the cache path, but not used in this example. cache.root = getCacheRootPath() simulate <- function(mean, sd) { # 1. Try to load cached data, if already generated key <- list(mean, sd) data <- loadCache(key) if (!is.null(data)) { cat("Loaded cached data\n") return(data); } # 2. If not available, generate it. cat("Generating data from scratch...") data <- rnorm(1000, mean=mean, sd=sd) Sys.sleep(1) # Emulate slow algorithm cat("ok\n") saveCache(data, key=key, comment="simulate()") data; } data <- simulate(2.3, 3.0) data <- simulate(2.3, 3.5) a = 2.3 b = 3.0 data <- simulate(a, b) # Will load cached data, params are checked by value # Clean up file.remove(findCache(key=list(2.3,3.0))) file.remove(findCache(key=list(2.3,3.5))) simulate2 <- function(mean, sd) { data <- rnorm(1000, mean=mean, sd=sd) Sys.sleep(1) # Emulate slow algorithm cat("Done generating data from scratch\n") data; } # Easy step to memoize a function # aslo possible to resassign function name. This would work with any functions from external packages. mzs <- addMemoization(simulate2) data <- mzs(2.3, 3.0) data <- mzs(2.3, 3.5) data <- mzs(2.3, 3.0) # Will load cached data # aslo possible to resassign function name. # but different memoizations of the same # function will return the same cache result # if input params are the same simulate2 <- addMemoization(simulate2) data <- simulate2(2.3, 3.0) # If the expression being evaluated depends on # "input" objects, then these must be be specified # explicitly as "key" objects. for (ii in 1:2) { for (kk in 1:3) { cat(sprintf("Iteration #%d:\n", kk)) res <- evalWithMemoization({ cat("Evaluating expression...") a <- kk Sys.sleep(1) cat("done\n") a }, key=list(kk=kk)) # expressions inside 'res' are skipped on the repeated run print(res) # Sanity checks stopifnot(a == kk) # Clean up rm(a) } # for (kk ...) } # for (ii ...) 

Relacionado con @biocyperman solution . R.cache tiene una función de envoltura para evitar la carga, el almacenamiento y la evaluación de la memoria caché. Ver la función modificada:

R.cache proporciona una envoltura para cargar, evaluar y guardar. Puedes simplificar tu código así:

 simulate <- function(mean, sd) { key <- list(mean, sd) data <- evalWithMemoization(key = key, expr = { cat("Generating data from scratch...") data <- rnorm(1000, mean=mean, sd=sd) Sys.sleep(1) # Emulate slow algorithm cat("ok\n") data}) } 
Intereting Posts