【问题标题】:Memoize and vectorize a custom function记忆和矢量化自定义函数
【发布时间】:2012-05-11 09:09:43
【问题描述】:

我想知道如何在 R 中对自定义函数进行矢量化和记忆。似乎 我的思维方式与 R 的操作方式不一致。所以,我很高兴 欢迎任何指向好的阅读材料的链接。例如,R inferno 是一个不错的 资源,但它并没有帮助弄清楚 R 中的记忆。

更一般地说,您能否提供memoise 的相关用法示例 还是R.cache 包?

我还没有找到关于这个主题的任何其他讨论。搜索 r-bloggers.com 上的“memoise”或“memoize”返回零结果。搜索 对于http://r-project.markmail.org/ 处的那些关键字不返回有用 讨论。我通过电子邮件发送了邮件列表,但没有收到完整的 回答。

我不仅仅对记忆 GC 函数感兴趣,而且我知道 Bioconductor和各种包装 那里有。

这是我的数据:

seqs <- c("","G","C","CCC","T","","TTCCT","","C","CTC")

缺少一些序列,所以它们是空白的""

我有一个计算GC含量的函数:

> GC <- function(s) {
    if (!is.character(s)) return(NA)
    n <- nchar(s)
    if (n == 0) return(NA)
    m <- gregexpr('[GCSgcs]', s)[[1]]
    if (m[1] < 1) return(0)
    return(100.0 * length(m) / n)
}

有效:

> GC('')
[1] NA
> GC('G')
[1] 100
> GC('GAG')
[1] 66.66667
> sapply(seqs, GC)
                  G         C       CCC         T               TTCCT           
       NA 100.00000 100.00000 100.00000   0.00000        NA  40.00000        NA 
        C       CTC 
100.00000  66.66667

我想记住它。然后,我想对其进行矢量化。

显然,我使用memoiseR.cacheR 包:

> system.time(dummy <- sapply(rep(seqs,100), GC))
   user  system elapsed
  0.044   0.000   0.054
>
> library(memoise)
> GCm1 <- memoise(GC)
> system.time(dummy <- sapply(rep(seqs,100), GCm1))
   user  system elapsed
  0.164   0.000   0.173
>
> library(R.cache)
> GCm2 <- addMemoization(GC)
> system.time(dummy <- sapply(rep(seqs,100), GCm2))
   user  system elapsed
 10.601   0.252  10.926

请注意,记忆函数要慢几个数量级。

我尝试了hash 包,但事情似乎在后面发生 场景,我不明白输出。序列C 应该有一个 100 的值,而不是 NULL

请注意,使用 has.key(s, cache) 而不是 exists(s, cache) 结果 在同一个输出中。此外,使用 cache[s] &lt;&lt;- result 而不是 cache[[s]] &lt;&lt;- result 产生相同的输出。

> cache <- hash()
> GCc <- function(s) {
    if (!is.character(s) || nchar(s) == 0) {
        return(NA)
    }
    if(exists(s, cache)) {
        return(cache[[s]])
    }
    result <- GC(s)
    cache[[s]] <<- result
    return(result)
}
> sapply(seqs,GCc)
[[1]]
[1] NA

$G
[1] 100

$C
NULL

$CCC
[1] 100

$T
NULL

[[6]]
[1] NA

$TTCCT
[1] 40

[[8]]
[1] NA

$C
NULL

$CTC
[1] 66.66667

至少我想出了如何矢量化:

> GCv <- Vectorize(GC)
> GCv(seqs)
                  G         C       CCC         T               TTCCT           
       NA 100.00000 100.00000 100.00000   0.00000        NA  40.00000        NA 
        C       CTC 
100.00000  66.66667 

相关的 stackoverflow 帖子:

【问题讨论】:

  • 您无视nchargregexpr 已经矢量化这一事实的任何特殊原因?

标签: r vectorization memoization


【解决方案1】:

这并没有明确回答您的问题,但此功能比您的快约 4 倍。

GC2 <- function(s) {
  if(!is.character(s)) stop("'s' must be character")
  n <- nchar(s)
  m <- gregexpr('[GCSgcs]', s)
  len <- sapply(m, length)
  neg <- sapply(m, "[[", 1)
  len <- len*(neg > 0)
  len/n
}

【讨论】:

  • 在我的系统 (x86_64 linux-gnu R 2.15.0) 上,您的功能慢了约 3 倍。我正在测试这样的速度:system.time(sapply(rep(seqs, 1000), GC)) 经过 0.418 秒。 system.time(sapply(rep(seqs, 1000), GC2)) 经过 1.401 秒。
  • @KamilSlowikowski 那是因为您使用错误的功能。 Joshua 对其进行了矢量化处理,因此您应该将其与 GC2(rep(seqs,1000)) 进行比较,在这种情况下,它确实快了约 4 倍。
  • @KamilSlowikowski 虽然我认为他不是故意设置fixed = TRUE
  • 确实,设置fixed=T 会导致所有[[1]] 元素为-1。约书亚,感谢您的代码!我现在知道如何从列表中的每个项目以及1 * TRUE == 11 * FALSE == 0 中取出一个元素。我现在觉得很傻,意识到ncharsubstr 可以处理项目的向量(“列表”),尤其是因为?nchar 的帮助明确地说明了这一点。
  • @joran:感谢您指出这一点。这是我测试的一部分,因为?gregexpr 说使用fixed=TRUE 可以更快......但它不应该出现在我的答案中。
【解决方案2】:

虽然这不会让您在通话中记忆,但如果有相当多的重复,您可以使用因子来更快地进行单个通话。例如使用 Joshua 的 GC2(尽管我必须删除 fixed=T 才能使其工作):

GC2 <- function(s) {
  if(!is.character(s)) stop("'s' must be character")
  n <- nchar(s)
  m <- gregexpr('[GCSgcs]', s)
  len <- sapply(m, length)
  neg <- sapply(m, "[[", 1)
  len <- len*(neg > 0)
  100.0 * len/n
}

可以轻松定义如下包装器:

GC3 <- function(s) {
  x <- factor(s)
  GC2(levels(x))[x]
}

system.time(GC2(rep(seqs, 50000)))
# user  system elapsed 
# 8.97    0.00    8.99 
system.time(GC3(rep(seqs, 50000)))
# user  system elapsed 
# 0.06    0.00    0.06 

【讨论】:

  • 这很棒。谢谢!记忆将从这里节省我相对较少的时间。
猜你喜欢
  • 2018-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-06
相关资源
最近更新 更多