vto*_*ola 6 r pass-by-reference dataframe
我将data.frameas参数传递给想要更改内部数据的函数:
x <- data.frame(value=c(1,2,3,4))
f <- function(d){
for(i in 1:nrow(d)) {
if(d$value[i] %% 2 == 0){
d$value[i] <-0
}
}
print(d)
}
Run Code Online (Sandbox Code Playgroud)
当我执行时,f(x)我可以看到data.frame内部如何被修改:
> f(x)
value
1 1
2 0
3 3
4 0
Run Code Online (Sandbox Code Playgroud)
但是,data.frame我通过的原件未经修改:
> x
value
1 1
2 2
3 3
4 4
Run Code Online (Sandbox Code Playgroud)
通常我通过返回修改后的一个克服了这个:
f <- function(d){
for(i in 1:nrow(d)) {
if(d$value[i] %% 2 == 0){
d$value[i] <-0
}
}
d
}
Run Code Online (Sandbox Code Playgroud)
然后调用重新分配内容的方法:
> x <- f(x)
> x
value
1 1
2 0
3 3
4 0
Run Code Online (Sandbox Code Playgroud)
但是,我想知道这个行为在一个非常大的行为中会产生什么影响data.frame,是一个为方法执行而增长的新行为?R-ish这样做的方法是什么?
有没有办法修改原始的,而不在内存中创建另一个?
实际上在R(几乎)中,每个修改都在先前数据的副本上执行(写时复制行为).
因此,例如在您的函数内部,当您d$value[i] <-0实际执行某些副本时.您通常不会注意到,因为它已经过优化,但您可以使用tracemem函数进行跟踪.
话虽这么说,如果你的data.frame不是很大,你可以坚持使用你的函数返回修改过的对象,因为它只是一个副本.
但是,如果您的数据集非常大并且每次都进行复制可能非常昂贵,那么您可以使用data.table,它允许就地修改,例如:
library(data.table)
d <- data.table(value=c(1,2,3,4))
f <- function(d){
for(i in 1:nrow(d)) {
if(d$value[i] %% 2 == 0){
set(d,i,1L,0) # special function of data.table (see also ?`:=` )
}
}
print(d)
}
f(d)
print(d)
# results :
> f(d)
value
1: 1
2: 0
3: 3
4: 0
>
> print(d)
value
1: 1
2: 0
3: 3
4: 0
Run Code Online (Sandbox Code Playgroud)
NB
在这种特定情况下,循环可以用"矢量化"和更有效的版本替换,例如:
d[d$value %% 2 == 0,'value'] <- 0
Run Code Online (Sandbox Code Playgroud)
但也许你真正的循环代码更复杂,不能轻易地进行矢量化.