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.tableDT1
的列时,可以通过以下方式完成:
DT1[DT2, col := i.val]
其中,i.
是指用于匹配行的DT2
(i
参数)的val
列中的值。此语法允许非常高效地执行此操作,而不必首先连接整个结果,然后更新所需的列
总而言之,引用更新可以节省大量时间,而且速度很快。但人们有时喜欢不就地更新对象,并愿意为此牺牲速度/内存。除了已经存在的引用更新之外,我们还试图找出提供此功能的最佳方法
希望这能有所帮助。这已经是一个相当长的答案。我将把您可能留下的任何问题留给其他人或您来解决(除了此答案中任何明显的误解)
DT1[DT2, col := i.val]