Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/r/77.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
R &引用;参考更新“;vs浅拷贝_R_Data.table - Fatal编程技术网

R &引用;参考更新“;vs浅拷贝

R &引用;参考更新“;vs浅拷贝,r,data.table,R,Data.table,函数set或[.data.table中的表达式:=允许用户通过引用更新data.tables。此行为与将操作结果重新指定给原始data.frame有何不同 keepcols<-function(DF,cols){ eval.parent(substitute(DF<-DF[,cols,with=FALSE])) } keeprows<-function(DF,i){ eval.parent(substitute(DF<-DF[i,])) } 数据中的kee

函数
set
[.data.table
中的表达式
:=
允许用户通过引用更新data.tables。此行为与将操作结果重新指定给原始data.frame有何不同

keepcols<-function(DF,cols){
  eval.parent(substitute(DF<-DF[,cols,with=FALSE]))  
}
keeprows<-function(DF,i){
   eval.parent(substitute(DF<-DF[i,]))
}

数据中的
keepcols表
:=
和所有的
set*
函数通过引用更新对象。这是在2012年IIRC前后引入的。此时,基本R不是浅复制,而是深度复制。浅复制是从3.1.0开始引入的


这是一个冗长的回答,但我认为这回答了你的前两个问题:

此base R方法与等效的data.table方法有何不同?差异是否仅与速度或内存使用有关

在base R v3.1.0+中,当我们执行以下操作时:

DF1 = data.frame(x=1:5, y=6:10, z=11:15)
DF2 = DF1[, c("x", "y")]
DF3 = transform(DF2, y = ifelse(y>=8L, 1L, y))
DF4 = transform(DF2, y = 2L)
  • DF1
    DF2
    ,这两列仅被浅拷贝
  • DF2
    DF3
    必须单独复制/重新分配列
    y
    ,但是
    x
    再次被复制
  • DF2
    DF4
    ,与(2)相同
  • 也就是说,只要列保持不变,就可以对列进行浅拷贝—在某种程度上,除非绝对必要,否则拷贝会被延迟


    data.table
    中,我们修改了In-place.means,这意味着即使在
    DF3
    DF4
    y
    期间也不会被复制

    DT2[y >= 8L, y := 1L] ## (a)
    DT2[, y := 2L]
    
    在这里,由于
    y
    已经是一个整数列,我们正在按整数修改它,通过引用,这里根本没有新的内存分配

    当您希望通过引用(上面标记为(a))进行子赋值时,这也特别有用。这是我们非常喜欢的
    data.table
    中的一个便捷功能

    另一个免费的优势(我从我们的互动中了解到)是,当我们必须(比如)将data.table的所有列转换为
    数值型
    类型时,例如,
    字符型
    类型:

    DT[, (cols) := lapply(.SD, as.numeric), .SDcols = cols]
    
    在这里,因为我们是通过引用更新的,所以每个字符列都会被引用替换为它的数字对应项。在替换之后,前面的字符列就不再需要了,可以进行垃圾收集了。但是如果要使用base R执行此操作:

    DF[] = lapply(DF, as.numeric)
    
    所有列都必须转换为数字,并且必须保存在一个临时变量中,然后最后将分配回
    DF
    。这意味着,如果有10列,每列都有1亿行,每列都是字符类型,那么
    DF
    占用的空间为:

    10 * 100e6 * 4 / 1024^3 = ~ 3.7GB
    
    由于
    numeric
    类型的大小是原来的两倍,我们总共需要
    7.4GB+3.7GB
    的空间来使用base R进行转换

    但请注意,
    data.table
    DF1
    期间复制到
    DF2
    。即:

    DT2 = DT1[, c("x", "y")]
    
    结果是一个副本,因为我们不能在浅层副本上通过引用进行子分配。它将更新所有克隆

    如果我们能够无缝集成浅层复制功能,但跟踪特定对象的列是否有多个引用,并尽可能按引用进行更新,那就太好了。R升级的引用计数功能在这方面可能非常有用。无论如何,我们正在努力实现这一点


    关于你的最后一个问题:

    “什么时候差异最大?”

  • 仍然有人不得不使用旧版本的R,而深度拷贝是无法避免的

  • 这取决于要复制多少列,因为您对其执行的操作。最坏的情况当然是您已经复制了所有列

  • 有些情况下,浅层复制不会带来好处

  • 如果要为每个组更新data.frame的列,并且组太多

  • 当您希望基于与另一个data.table的联接来更新data.table
    DT1
    的列时,可以通过以下方式完成:

    DT1[DT2, col := i.val]
    
    其中,
    i.
    是指用于匹配行的
    DT2
    i
    参数)的
    val
    列中的值。此语法允许非常高效地执行此操作,而不必首先连接整个结果,然后更新所需的列

  • 总而言之,引用更新可以节省大量时间,而且速度很快。但人们有时喜欢不就地更新对象,并愿意为此牺牲速度/内存。除了已经存在的引用更新之外,我们还试图找出提供此功能的最佳方法

    希望这能有所帮助。这已经是一个相当长的答案。我将把您可能留下的任何问题留给其他人或您来解决(除了此答案中任何明显的误解)

    DT1[DT2, col := i.val]