R:通过引用函数传递 data.frame
R: Pass data.frame by reference to a function
我将 data.frame
作为参数传递给想要更改内部数据的函数:
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)
}
当我执行f(x)
时,我可以看到里面的data.frame
是如何被修改的:
> f(x)
value
1 1
2 0
3 3
4 0
但是我传的原data.frame
是没有修改的:
> x
value
1 1
2 2
3 3
4 4
通常我通过返回修改后的来克服这个问题:
f <- function(d){
for(i in 1:nrow(d)) {
if(d$value[i] %% 2 == 0){
d$value[i] <-0
}
}
d
}
然后调用重新分配内容的方法:
> x <- f(x)
> x
value
1 1
2 0
3 3
4 0
但是,我想知道这种行为在一个非常大的 data.frame
中有什么影响,是否会为方法执行生成一个新行为?哪种是 R 风格的做法?
有没有办法在不在内存中创建另一个的情况下修改原始的?
实际上在 R 中(几乎)每次修改都是在先前数据的副本上执行的(copy-on-writing 行为)。
因此,例如在您的函数内部,当您执行 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
N.B.
在这种特定情况下,循环可以替换为 "vectorized" 和更高效的版本,例如:
d[d$value %% 2 == 0,'value'] <- 0
但也许您真正的循环代码要复杂得多,无法轻松矢量化。
我将 data.frame
作为参数传递给想要更改内部数据的函数:
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)
}
当我执行f(x)
时,我可以看到里面的data.frame
是如何被修改的:
> f(x)
value
1 1
2 0
3 3
4 0
但是我传的原data.frame
是没有修改的:
> x
value
1 1
2 2
3 3
4 4
通常我通过返回修改后的来克服这个问题:
f <- function(d){
for(i in 1:nrow(d)) {
if(d$value[i] %% 2 == 0){
d$value[i] <-0
}
}
d
}
然后调用重新分配内容的方法:
> x <- f(x)
> x
value
1 1
2 0
3 3
4 0
但是,我想知道这种行为在一个非常大的 data.frame
中有什么影响,是否会为方法执行生成一个新行为?哪种是 R 风格的做法?
有没有办法在不在内存中创建另一个的情况下修改原始的?
实际上在 R 中(几乎)每次修改都是在先前数据的副本上执行的(copy-on-writing 行为)。
因此,例如在您的函数内部,当您执行 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
N.B.
在这种特定情况下,循环可以替换为 "vectorized" 和更高效的版本,例如:
d[d$value %% 2 == 0,'value'] <- 0
但也许您真正的循环代码要复杂得多,无法轻松矢量化。