我正在更新一组以前只接受data.frame
个对象以使用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
一起运行。换句话说,我希望只需将someData <- read.table(...)
更改为someData <- fread(...)
即可使用该脚本。
答案 0 :(得分: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.table
或data.frame
对象d
,然后再将其传递给函数foo
,以检查是否有任何副本做了。
答案 1 :(得分: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]
通过引用对其进行修改,更改A
中的dt
列}。
第二个语句d$B <- 1
强制创建函数内部d
(也称为d
)范围的副本。然后,第三个陈述d[,C:=1]
,通过引用修改 (但不影响dt
),然后return(d)
返回副本。
如果更改第二个和第三个语句的顺序,则dt
上函数调用的效果会有所不同。