R中的caching/记忆/散列选项

我试图find一种简单的方法来在R(本质上是caching)中使用类似Perl的哈希函数,因为我打算同时执行Perl风格的哈希和写我自己的计算记忆。 然而,其他人已经打了我一拳,并为包装meemisation。 我挖的越多,我发现的越多,例如memoiseR.cache ,但差异不是很清楚。 另外,还不清楚除了使用hash包以外,还可以如何获得Perl风格的哈希(或Python风格的字典)并编写自己的记忆,这似乎并不支持这两个记忆包。

由于我没有find有关CRAN或其他地方的信息来区分这些选项,所以也许这应该是一个关于SO的社区维基问题:R中的记忆和caching有什么select,它们有什么区别?


作为比较的基础,这里是我find的选项列表。 另外,在我看来,所有依赖哈希,所以我会注意到哈希选项。 键/值存储有点相关,但是打开了一大堆关于数据库系统的蠕虫(例如BerkeleyDB,Redis,MemcacheDB和其他数十个蠕虫)。

它看起来像选项是:

哈希

  • 摘要 – 为任意R对象提供散列。

记忆化

  • 记忆 – 一个非常简单的记忆function的工具。
  • R.cache – 为memoization提供了更多的function,虽然它似乎有些function缺乏示例。

高速caching

  • 散列 – 提供类似Perl的散列和Python字典的cachingfunction。

键/值存储

这些是R对象外部存储的基本选项。

  • stashr
  • filehash

检查点

  • cacher – 这似乎更像是检查点 。
  • CodeDepends – 支持cacher并提供一些有用function的OmegaHat项目。
  • DMTCP (不是R软件包)似乎支持一堆语言的检查点, 最近一位开发人员在R中寻求协助testingDMTCP检查点 。

其他

  • Base R支持:命名向量和列表,数据框的行和列名称以及环境中的项目名称。 在我看来,使用列表是有点混乱。 (也有pairlist ,但不赞成 。)
  • data.table包支持快速查找数据表中的元素。

用例

虽然我最感兴趣的是了解选项,但我有两个基本用例:

  1. caching:简单的string计数。 [注意:这不是NLP,而是一般使用,所以NLP库是矫枉过正的; 表是不够的,因为我不希望等到整个string集被加载到内存中。 Perl风格的哈希处于正确的效用水平。]
  2. 记忆怪异的计算。

这真的是因为我正在挖掘一些slooooow代码的分析,我真的想只计算简单的string,看看我是否可以通过记忆加速一些计算。 能够散列input值,即使我不记忆,会让我看看memoization可以帮助。


注1:可重复研究的CRAN任务视图列出了几个包( cacherR.cache ),但没有详细说明使用选项。

注2:为了帮助其他人寻找相关的代码,这里有一些作者或包的笔记。 一些作者使用SO。 🙂

  • 德克Eddelbuettel: digest – 许多其他包依赖于此。
  • 罗杰·彭(Roger Peng): cacherfilehashstashR以不同的方式解决不同的问题; 请参阅罗杰的网站获取更多的软件包。
  • 克里斯托弗·布朗: hash – 似乎是一个有用的包,但不幸的是,与ODG的链接已经下降。
  • Henrik Bengtsson: R.cache &Hadley Wickham: memoise – 现在还不清楚什么时候更喜欢一个包。

注3:有些人使用memoise / memoisation他人使用memoize / memoization。 如果你正在四处搜寻,请注意。 Henrik使用“z”,Hadley使用“s”。

为了简单的计算string(而不是使用table或类似的), 多重数据结构似乎是一个很好的select。 environment对象可以用来模拟这个。

 # 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) 

我没有memoise运气,因为它给too deep recursive我尝试过的一些打包函数too deep recursive问题。 用R.cache我有更好的运气。 以下是我从R.cache文档改编的更多注释代码。 该代码显示了不同的caching选项。

 # 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 ...) 

与@biocyperman解决scheme相关。 R.cache具有包装function,可以避免加载,保存和评估caching。 看到修改的function:

R.cache为加载,评估和保存提供了一个包装。 你可以像这样简化你的代码:

 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}) }