R 中的缓存/制表/哈希选项

我试图找到一种简单的方法来在 R 中使用类似 Perl 的 hash 函数(本质上是缓存) ,因为我打算既进行 Perl 风格的哈希,又编写自己的计算制表。然而,其他人已经抢在我前面,拿到了记忆套餐。我挖掘得越多,就发现越多,例如 memoiseR.cache,但差异并不明显。此外,除了使用 hash包之外,我们还不清楚如何获得 Perl 风格的散列(或 Python 风格的字典)并编写自己的制表,因为 hash包似乎并不支持这两个制表包。

因为我找不到任何关于 CRAN 或其他地方的信息来区分选项,也许这应该是一个关于 SO 的社区 wiki 问题: R 中的制表和缓存选项是什么,它们的区别是什么?


作为比较的基础,下面是我发现的选项列表。另外,在我看来,所有这些都依赖于散列,所以我也要注意散列选项。键/值存储在某种程度上是相关的,但是它打开了关于 DB 系统的大量蠕虫(例如 BerkeleyDB、 Redis、 MemcacheDB 和 还有很多其他人)。

看起来选项有:

哈希

纪念

缓存

  • Hash -提供类似于 Perl 散列和 Python 字典的缓存功能。

键/值存储

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

检查点

其他

  • 基础 R 支持: 命名向量和列表、数据框架的行名和列名以及环境中项目的名称。在我看来,使用一个列表有点像是一个组件。(还有 pairlist,但是 这是不被推崇的。)
  • Data.table包支持快速查找数据表中的元素。

用例

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

  1. 缓存: 字符串的简单计数。[注意: 这不是用于 NLP 的,而是一般用途,所以 NLP 库是多余的; 表是不够的,因为我不喜欢等到整个字符串集都加载到内存中。Perl 样式的散列处于正确的实用级别。]
  2. 记住可怕的计算。

这些都是因为我是 深入研究一些缓慢代码的分析,我想只计算简单的字符串,看看我是否可以通过制表来加快一些计算。能够对输入值进行哈希处理,即使我不使用制表,也会让我看到制表是否有帮助。


注1: 再现性研究的任务视角列出了几个包(cacherR.cache) ,但是没有详细说明使用选项。

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

  • Dirk Eddelbuettel: digest-很多其他软件包都依赖于此。
  • Roger Peng: cacherfilehashstashR-这些以不同的方式解决不同的问题; 请参阅 罗杰的网站了解更多的软件包。
  • 克里斯托弗布朗: hash-似乎是一个有用的软件包,但 ODG 的链接是下来,不幸的。
  • Henrik Bengtsson: R.cache & Hadley Wickham: memoise——现在还不清楚什么时候应该更喜欢一个包裹而不是另一个。

注3: 有些人使用制表/制表,有些人使用制表/制表。如果你在附近找的话,给你个便条。亨里克用“ z”,哈德利用“ s”。

12134 次浏览

对于简单的字符串计数(不使用 table或类似的) ,多集数据结构似乎是一个很好的选择。可以使用 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时运气不佳,因为它给我试用过的一个包的某些函数带来了“太深的递归”问题。对于 R.cache,我的运气更好。下面是我从 R.cache文档中改编的更多带注释的代码。代码显示了执行缓存的不同选项:

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

@ 生化人溶液有关。高速缓存具有包装功能,可以避免高速缓存的加载、保存和计算。查看修改后的函数:

高速缓存为加载、计算和保存提供了一个包装器。您可以像这样简化代码:

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