R中的缓存/备注/哈希选项


72

我试图找到一种简单的方法来在R中使用Perl的哈希函数(本质上是缓存),因为我打算同时进行Perl样式的哈希并编写自己的计算备忘录。但是,其他人却打败了我,并准备了备忘录。挖掘的越多,我发现的就越多,例如memoiseR.cache,但差异尚不明确。此外,除了使用该hash包之外,还不清楚如何获得Perl风格的哈希(或Python风格的字典)并编写自己的备忘录,这似乎并不能支撑这两个备忘录包。

由于我找不到有关CRAN或其他地方的信息来区分这些选项,因此这也许应该是关于SO的社区Wiki问题:R中用于记忆和缓存的选项是什么,它们之间有什么区别?


作为比较的基础,这是我找到的选项的列表。另外,在我看来,所有这些都依赖于哈希,因此我还要注意哈希选项。密钥/值存储在某种程度上是相关的,但是它打开了大量有关DB系统的蠕虫(例如BerkeleyDB,Redis,MemcacheDB以及许多其他蠕虫)。

看起来这些选项是:

散列

  • 摘要-为任意R对象提供哈希。

记忆化

  • memoise-一个非常简单的功能记忆工具。
  • R.cache-提供更多用于记忆的功能,尽管似乎其中一些功能缺少示例。

快取

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

键/值存储

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

检查点

其他

  • Base R支持:命名向量和列表,数据框的行和列名称以及环境中的项目名称。在我看来,使用列表有点麻烦。(也有pairlist,但已弃用。)
  • 所述data.table包支持在一个数据表元素的快速查找。

用例

尽管我对了解这些选项最感兴趣,但是我有两个基本用例:

  1. 缓存:简单的字符串计数。[注意:这不是用于NLP,而是用于一般用途,因此NLP库是过大的;表是不够的,因为我不想等到整个字符串集都加载到内存中后再进行操作。Perl样式的哈希值在实用程序中是正确的。]
  2. 记忆巨大的计算。

之所以会出现这些问题,是因为我正在研究一些简单的代码概要分析,并且我真的很想只计算简单的字符串,看看是否可以通过记忆来加快一些计算。即使我不做备忘录,也能够对输入值进行哈希处理,这会让我看看备忘录是否有帮助。


注意1:“可再生性研究CRAN任务视图”列出了几个软件包(cacherR.cache),但未详细说明用法选项。

注意2:为了帮助其他人查找相关代码,这里有一些作者或软件包的注意事项。有些作者使用SO。:)

  • Dirk Eddelbuettel:digest-许多其他软件包都依赖于此。
  • 罗杰·彭:cacherfilehashstashR-以不同的方式,这些地址不同的问题; 有关更多软件包,请参见Roger的网站
  • Christopher Brown:hash-似乎是一个有用的软件包,但不幸的是,与ODG的链接断开了。
  • 亨里克·本格森(Henrik Bengtsson):R.cache&哈德利·威克姆(Hadley Wickham):memoise-目前尚不清楚何时偏爱另一种包装。

注意3:有些人使用备忘录/记忆,其他人使用备忘录/记忆。如果您在四处搜寻,请注意。Henrik使用“ z”,Hadley使用“ s”。


添加一个或两个实际用例,以便可以比较这些方法可能会很好...
Tommy

@Tommy:谢谢,我会做到的!
Iterator

对您的评论感到困惑:环境。如果创建一个新环境,它将被散列。?environment例如,env.profile(new.env())$size #[1] 29
IRTFM,2011年

@DWin:您是正确的。我仅将其作为哈希功能的一个选项。
Iterator

2
这篇由“坚果中的R”的作者撰写的文章包括对几种用于查找对象的不同选项的速度测试,包括将它们放在环境中(查找使用哈希名称的环境)broadcast.oreilly.com/2010/03/lookup- performance-in-r.html。不知道它是否对您有用,但我想我会推荐给其他人。
乔什·奥布莱恩

Answers:


11

我没有运气,memoise因为它给too deep recursive我尝试过的某个包装的功能带来了问题。有了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 ...)

9

对于简单的字符串计数(而不使用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)

4

@biocyperman解决方案有关。R.cache具有包装功能,可避免加载,保存和评估缓存。请参阅修改后的函数:

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})
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.