2014-04-01 28 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

相關問題