【问题标题】:Avoiding copies by using environments使用环境避免复制
【发布时间】:2015-04-23 17:04:16
【问题描述】:

在 Hadley 的 Advanced R 书中,有一节关于将环境用作“大型物体的容器”(见下文)。我正在寻找一些示例来说明这种方法的最佳实践。我查看了here,但没有找到任何明确解决此问题的内容。

如果相关,我的应用程序是一个闪亮的应用程序,其中应用程序环境中的数据与导出的函数(在 R/ 中)共享。另请参阅question

用于管理数据流的函数是herer_env 是闪亮的环境。如果在应用程序外部调用分析函数,则该函数会在用户退出应用程序或在全局环境中查找转储到 R(studio) 的数据。

http://adv-r.had.co.nz/Environments.html#explicit-envs

"避免复制 由于环境具有引用语义,因此您永远不会意外创建副本。这使它成为大型物体的有用容器。对于经常需要管理大型基因组对象的生物导体封装来说,这是一种常用技术。 "

编辑

我预计数据传递给函数的方式会有所不同。使用几种不同的方法,差异似乎很小。 Hadley 所指的(未命名的)生物导体包中使用的方法有什么不同?

library(ggplot2)
library(microbenchmark)
dat <- diamonds
dataset <- "dat"
r_env <- new.env()
r_env$dat <- diamonds

reg1 <- function(dataset) 
    lm(price ~ carat + color, data = get(dataset))

reg2 <- function(dataset) 
    lm(price ~ carat + color, data = r_env[[dataset]])

reg3 <- function(dat) 
    lm(price ~ carat + color, data = dat)

microbenchmark(times = 100,
  reg1(dataset),
  reg2(dataset),
  reg3(dat)
)

Unit: milliseconds
          expr      min       lq     mean   median       uq      max neval
 reg1(dataset) 75.52479 85.50742 87.80560 87.57180 89.59216 96.34956   100
 reg2(dataset) 83.98896 85.51443 87.40334 87.00544 88.84889 94.01787   100
     reg3(dat) 61.00551 86.01789 88.15627 88.13501 90.48899 95.05454   100

【问题讨论】:

  • 关于避免复制的段落的后半部分是:“对 R 3.1.0 的更改已大大降低了这种使用的重要性,因为修改列表不再产生深层复制。以前,修改单个元素如果某些元素很大,则列表中的每个元素都会被复制,这是一项昂贵的操作。现在,修改列表可以有效地重用现有向量,从而节省大量时间。”
  • 列表有引用语义吗?将列表传递给函数进行分析与传递引用一样有效吗?如果是这样,很高兴看到一个例子。
  • 如果你想了解 R 和性能,我强烈推荐 this 开源书籍,特别是 performancememory 上的部分

标签: r shiny bioconductor


【解决方案1】:

对于列表和函数的引用语义,这里有一个修改列表元素的函数

f = function (l) {
    l[[1]][1] = 2
    l
}

这是一个列表,它是应用到它的函数之前和之后的内部表示

> l = list(a=1:5, b=1:5)
> .Internal(inspect(l))
@b3baa80 19 VECSXP g0c2 [NAM(1),ATT] (len=2, tl=0)
  @a4133a8 13 INTSXP g0c3 [] (len=5, tl=0) 1,2,3,4,5
  @a4133f0 13 INTSXP g0c3 [] (len=5, tl=0) 1,2,3,4,5
ATTRIB:
  @894b670 02 LISTSXP g0c0 [] 
    TAG: @1406d18 01 SYMSXP g1c0 [MARK,NAM(2),LCK,gp=0x6000] "names" (has value)
    @b3baab8 16 STRSXP g0c2 [] (len=2, tl=0)
      @15c8f68 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "a"
      @17f47e8 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "b"
> .Internal(inspect(f(l)))
@b2da518 19 VECSXP g0c2 [NAM(1),ATT] (len=2, tl=0)
  @6d6b3f0 14 REALSXP g0c4 [] (len=5, tl=0) 2,2,3,4,5
  @a4133f0 13 INTSXP g0c3 [NAM(2)] (len=5, tl=0) 1,2,3,4,5
ATTRIB:
  @85031c8 02 LISTSXP g0c0 [] 
    TAG: @1406d18 01 SYMSXP g1c0 [MARK,NAM(2),LCK,gp=0x6000] "names" (has value)
    @b3baab8 16 STRSXP g0c2 [NAM(2)] (len=2, tl=0)
      @15c8f68 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "a"
      @17f47e8 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "b"

函数调用前,这部分

@b3baa80 19 VECSXP g0c2 [NAM(1),ATT] (len=2, tl=0)
  @a4133a8 13 INTSXP g0c3 [] (len=5, tl=0) 1,2,3,4,5
  @a4133f0 13 INTSXP g0c3 [] (len=5, tl=0) 1,2,3,4,5

是列表(VECSXP)和整数向量(INTSXP)。 @ 是数据的内存地址。在函数调用之后我们有

@b2da518 19 VECSXP g0c2 [NAM(1),ATT] (len=2, tl=0)
  @6d6b3f0 14 REALSXP g0c4 [] (len=5, tl=0) 2,2,3,4,5
  @a4133f0 13 INTSXP g0c3 [NAM(2)] (len=5, tl=0) 1,2,3,4,5

整个列表和被修改元素的地址已经改变,但重要的是第二个元素的地址没有。

人们会在环境中看到类似的行为,但整体 SXP 不会改变。当然还有人会引入引用语义(原始环境会改变,即使函数调用的返回值被返回到不同的符号),这可能是非常不可取的。

【讨论】:

    猜你喜欢
    • 2020-03-10
    • 2012-07-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-24
    • 2020-11-09
    • 2023-03-27
    • 2021-12-01
    • 2018-02-09
    相关资源
    最近更新 更多