2014-04-01 25 views
6

我正在更新以前只接受data.frame对象与data.table参数一起使用的一组函数。当函数返回一个data.table时是否进行复制?

我决定使用R的方法调度来实现函数,以便使用data.frame的旧代码仍然可以与更新的函数一起工作。在我的一个功能中,我输入data.frame作为输入,修改它,然后返回修改后的data.frame。我也创建了data.table实现。例如:

# The functions 
foo <- function(d) { 
    UseMethod("foo") 
} 

foo.data.frame <- function(d) { 
    <Do Something> 
    return(d) 
} 

foo.data.table <- function(d) { 
    <Do Something> 
    return(d) 
} 

我知道data.table作品进行修改,而不复制,我实现了foo.data.table同时保持这一点。但是,我在函数结尾处返回data.table对象,因为我希望我的旧脚本能够与新的data.table对象一起使用。这是否会制作data.table的副本?我该如何检查?根据文件,必须非常明确地创建一个data.table的副本,但我不确定在这种情况下。

我想要回东西的时候,我没有与data.tables原因:

我的老段子是这样的

someData <- read.table(...) 
... 
someData <- foo(someData) 

我想脚本能够通过与data.table s到运行只是改变数据摄取线。换句话说,我希望脚本能够通过将someData <- read.table(...)更改为someData <- fread(...)来工作。

+1

只有':='和'set *'函数通过引用修改'data.table'。因此,如果您在函数中使用':='或任何'set *'函数,如果您不想通过引用修改'd',则可能需要处理'copy(d)' 。例如:如果你这样做:'ans < - d [,lapply(.SD,sum),by = cols]',那么这里的引用并没有改变。 – Arun

+0

@阿伦抱歉,如果我的问题不清楚。我确实希望通过引用修改'd',所以我确实使用了':='和'set *'函数来修改'foo.data.table'函数中的'data.table'。如果我通过引用在函数内修改'd',那么'return(d)'返回'd'的副本吗? – ialm

+1

不,它不..你可以使用'tracemem'来检查这些东西。例如:'foo < - function(x){x [,bar:= 1L]; return(x)}; x = data.table(a = 1:5,b = 6:10); tracemem(X); FOO(X)'。如果有任何复制,它会在'foo(x)'后产生一些详细信息。或者,你可以使用'address()'函数来测试'x'的列在函数前后是否具有相同的地址。如果是,则不进行复制。 – Arun

回答

5

感谢Arun在评论中的回答。我会在他的评论中用他的例子来回答这个问题。

一个可以检查,如果副本正在使用tracemem功能来跟踪R.对象从功能,?tracemem的帮助文件所做的描述说:

此功能标志着一个对象,每当内部代码复制对象时都会打印一条消息。它是一个重要原因难以预测R.

例如存储器使用:

# Using a data.frame 
df <- data.frame(x=1:5, y=6:10) 
tracemem(df) 
## [1] "<0x32618220>" 
df$y[2L] <- 11L 
## tracemem[0x32618220 -> 0x32661a98]: 
## tracemem[0x32661a98 -> 0x32661b08]: $<-.data.frame $<- 
## tracemem[0x32661b08 -> 0x32661268]: $<-.data.frame $<- 
df 
## x y 
## 1 1 6 
## 2 2 11 
## 3 3 8 
## 4 4 9 
## 5 5 10 

# Using a data.table 
dt <- data.table(x=1:5, y=6:10) 
tracemem(dt) 
## [1] "<0x5fdab40>" 
set(dt, i=2L, j=2L, value=11L) # No memory output! 
address(dt) # Verify the address in memory is the same 
## [1] "0x5fdab40" 
dt 
## x y 
## 1: 1 6 
## 2: 2 11 
## 3: 3 8 
## 4: 4 9 
## 5: 5 10 

似乎在data.frame改变一个元件时data.frame对象被复制两次,而data.table被修改,但没有复制!

从我的问题,我就可以跟踪data.tabledata.frame对象,d,将它传递给函数,foo,以检查是否有拷贝才制成。

+2

非常好!你也可以检查地址(dt $ x)'。 – Arun

+0

也可能想要检查返回值何时被存储,所以someData = foo(someData) –

3

不知道这增加了什么,但作为一个警世故事注意以下行为:

library(data.table) 
foo.data.table <- function(d) { 
    d[,A:=4] 
    d$B <- 1 
    d[,C:=1] 
    return(d) 
} 
set.seed(1) 
dt  <- data.table(A=rnorm(5),B=runif(5),C=rnorm(5)) 
dt 
#    A   B   C 
# 1: -0.6264538 0.2059746 -0.005767173 
# 2: 0.1836433 0.1765568 2.404653389 
# 3: -0.8356286 0.6870228 0.763593461 
# 4: 1.5952808 0.3841037 -0.799009249 
# 5: 0.3295078 0.7698414 -1.147657009 
result <- foo.data.table(dt) 
dt 
# A   B   C 
# 1: 4 0.2059746 -0.005767173 
# 2: 4 0.1765568 2.404653389 
# 3: 4 0.6870228 0.763593461 
# 4: 4 0.3841037 -0.799009249 
# 5: 4 0.7698414 -1.147657009 
result 
# A B C 
# 1: 4 1 1 
# 2: 4 1 1 
# 3: 4 1 1 
# 4: 4 1 1 
# 5: 4 1 1 

因此,显然,dt通过引用传递到foo.data.table(...)和第一条语句,d[,A:=4],参照修改它,更换dtA

第二条语句d$B <- 1强制创建d(也被命名为d)作用域的内部函数的副本。然后请第三条语句d[,C:=1]修改作为参考(但不影响dt),然后return(d)然后返回副本。

如果更改第二条和第三条语句的顺序,函数调用对dt的影响是不同的。

+2

我理解这个问题的方式是,假设直到返回(。)的地方的函数没有导致一个'copy',只是声明'return(。)'结果仍然在一个副本中... – Arun

+1

我想你提出了一个重要的观点。我的函数生成的代码有点模糊,因为我编码时带有副作用,并且不需要'<-'运算符,因为我正在修改'd'(即'foo(d)'就足够了,但我用'd <-foo(d)')。我只是想要符合我的旧代码库的特殊情况。我很小心地使用'data.table'函数修改值并在我的函数中创建列,以便在返回点之前不复制任何副本。我只是不确定返回'data.table'对象的行为。 – ialm

相关问题