在 R 中没有复制的情况下修改函数列表的最佳方法
Optimal way to modify a list in a function without copy in R
我正在编写一个包,我需要通过一系列函数修改一个大列表。
实现这一目标的可能方法是什么?
我附上了我的实现,但不确定这是否是最佳的。
##' @export
test <- function(param = TRUE){
x <- list("a"= data.frame(a1 = c(1,2), a2 = c(1,1)),
"b"= data.frame(b1 = c(2,3), b2 = c(1,2)))
message(paste("in test() function, references to x[[1]]:", inspect(x)[["children"]][[1]][["address"]]))
message(paste("in test() function, references to x[[2]]:", inspect(x)[["children"]][[2]][["address"]]))
for(name in names(x)) updateList(x, name)
message(paste("in test() function, post update references to x[[1]]:", inspect(x)[["children"]][[1]][["address"]]))
message(paste("in test() function, post update references to x[[2]]:", inspect(x)[["children"]][[2]][["address"]]))
x
}
updateList <- function(x, name){
message(paste("updateList() references to x[[1]]:", inspect(x)[["children"]][[1]][["address"]]))
message(paste("updateList() references to x[[2]]:", inspect(x)[["children"]][[2]][["address"]]))
newdf <- rbind(x[[name]], c(4,4))
assign("temp", newdf, envir = parent.frame(n = 1))
with(parent.frame(n = 1), x[[name]] <- temp)
invisible(NULL)
}
在Console
,当我运行test()
> library(pryr)
> test()
in test() function, references to x[[1]]: 0x55d66ce9dd98
in test() function, references to x[[2]]: 0x55d670954508
updateList() references to x[[1]]: 0x55d66ce9dd98
updateList() references to x[[2]]: 0x55d670954508
updateList() references to x[[1]]: 0x55d66fca1688
updateList() references to x[[2]]: 0x55d670954508
in test() function, post update references to x[[1]]: 0x55d66fca1688
in test() function, post update references to x[[2]]: 0x55d66ffb8208
$a
a1 a2
1 1 1
2 2 1
3 4 4
$b
b1 b2
1 2 1
2 3 2
3 4 4
有没有办法确保 R 没有复制?如何知道它是否在中间创建了副本?
正如@len-greski 所建议的,我们可以看到每个元素的地址,我们可以看到在每次迭代中只有一个数据帧被复制,其余的则没有。
您可以确定 R 是否正在使用 pryr::address()
和 pryr::refs()
复制对象。这里我们将添加message()
函数来检查test()
和updateList()
中x
的地址,以表明正在复制对象。
library(pryr)
test <- function(param = TRUE){
x <- list("a"= data.frame(a1 = c(1,2), a2 = c(1,1)),
"b"= data.frame(b1 = c(2,3), b2 = c(1,2)))
message(paste("in test() function, references to x:",c(address(x)," ",refs(x))))
for(name in names(x)) updateList(x, name)
message(paste("in test() function, post update references to x:",c(address(x)," ",refs(x))))
x
}
updateList <- function(x, name){
message(paste("updateList() references to x:",c(address(x)," ",refs(x))))
newdf <- rbind(x[[name]], c(4,4))
assign("temp", newdf, envir = parent.frame(n = 1))
with(parent.frame(n = 1), x[[name]] <- temp)
invisible(NULL)
}
...和输出。
> test()
in test() function, address of x: 0x7fbbcfaf02c8 references: 1
updateList() address of x: 0x7fbbcf696730 references: 0
updateList() address of x: 0x7fbbcf6846c0 references: 0
in test() function, post update address of x: 0x7fbbcfa3d6c8 references: 1
$a
a1 a2
1 1 1
2 2 1
3 4 4
$b
b1 b2
1 2 1
2 3 2
3 4 4
>
从pre-update和post-update之间x
地址的变化可以看出是在复制。
在 R 中,修改列表将导致复制整个列表。
您可能需要考虑使用可变的 R6
对象。这里有一些资源:
使用 environment
是一个不错的选择。
k <- lapply(1:100000, identity)
names(k) <- as.character(1:length(k))
f <- function(x, i){x[[i]] <- x[[i]]*2; x}
system.time(for(i in 1:length(k)) f(k, as.character(i)))
这在我的机器上大约需要 2.5 秒。
e2 <- list2env(k, hash = FALSE)
system.time(for(i in 1:length(k)) f(e2, as.character(i)))
environment
需要 0.3 秒。快10倍!!使用 hash = TRUE
,速度提高 100 倍。
我正在编写一个包,我需要通过一系列函数修改一个大列表。 实现这一目标的可能方法是什么?
我附上了我的实现,但不确定这是否是最佳的。
##' @export
test <- function(param = TRUE){
x <- list("a"= data.frame(a1 = c(1,2), a2 = c(1,1)),
"b"= data.frame(b1 = c(2,3), b2 = c(1,2)))
message(paste("in test() function, references to x[[1]]:", inspect(x)[["children"]][[1]][["address"]]))
message(paste("in test() function, references to x[[2]]:", inspect(x)[["children"]][[2]][["address"]]))
for(name in names(x)) updateList(x, name)
message(paste("in test() function, post update references to x[[1]]:", inspect(x)[["children"]][[1]][["address"]]))
message(paste("in test() function, post update references to x[[2]]:", inspect(x)[["children"]][[2]][["address"]]))
x
}
updateList <- function(x, name){
message(paste("updateList() references to x[[1]]:", inspect(x)[["children"]][[1]][["address"]]))
message(paste("updateList() references to x[[2]]:", inspect(x)[["children"]][[2]][["address"]]))
newdf <- rbind(x[[name]], c(4,4))
assign("temp", newdf, envir = parent.frame(n = 1))
with(parent.frame(n = 1), x[[name]] <- temp)
invisible(NULL)
}
在Console
,当我运行test()
> library(pryr)
> test()
in test() function, references to x[[1]]: 0x55d66ce9dd98
in test() function, references to x[[2]]: 0x55d670954508
updateList() references to x[[1]]: 0x55d66ce9dd98
updateList() references to x[[2]]: 0x55d670954508
updateList() references to x[[1]]: 0x55d66fca1688
updateList() references to x[[2]]: 0x55d670954508
in test() function, post update references to x[[1]]: 0x55d66fca1688
in test() function, post update references to x[[2]]: 0x55d66ffb8208
$a
a1 a2
1 1 1
2 2 1
3 4 4
$b
b1 b2
1 2 1
2 3 2
3 4 4
有没有办法确保 R 没有复制?如何知道它是否在中间创建了副本?
正如@len-greski 所建议的,我们可以看到每个元素的地址,我们可以看到在每次迭代中只有一个数据帧被复制,其余的则没有。
您可以确定 R 是否正在使用 pryr::address()
和 pryr::refs()
复制对象。这里我们将添加message()
函数来检查test()
和updateList()
中x
的地址,以表明正在复制对象。
library(pryr)
test <- function(param = TRUE){
x <- list("a"= data.frame(a1 = c(1,2), a2 = c(1,1)),
"b"= data.frame(b1 = c(2,3), b2 = c(1,2)))
message(paste("in test() function, references to x:",c(address(x)," ",refs(x))))
for(name in names(x)) updateList(x, name)
message(paste("in test() function, post update references to x:",c(address(x)," ",refs(x))))
x
}
updateList <- function(x, name){
message(paste("updateList() references to x:",c(address(x)," ",refs(x))))
newdf <- rbind(x[[name]], c(4,4))
assign("temp", newdf, envir = parent.frame(n = 1))
with(parent.frame(n = 1), x[[name]] <- temp)
invisible(NULL)
}
...和输出。
> test()
in test() function, address of x: 0x7fbbcfaf02c8 references: 1
updateList() address of x: 0x7fbbcf696730 references: 0
updateList() address of x: 0x7fbbcf6846c0 references: 0
in test() function, post update address of x: 0x7fbbcfa3d6c8 references: 1
$a
a1 a2
1 1 1
2 2 1
3 4 4
$b
b1 b2
1 2 1
2 3 2
3 4 4
>
从pre-update和post-update之间x
地址的变化可以看出是在复制。
在 R 中,修改列表将导致复制整个列表。
您可能需要考虑使用可变的 R6
对象。这里有一些资源:
使用 environment
是一个不错的选择。
k <- lapply(1:100000, identity)
names(k) <- as.character(1:length(k))
f <- function(x, i){x[[i]] <- x[[i]]*2; x}
system.time(for(i in 1:length(k)) f(k, as.character(i)))
这在我的机器上大约需要 2.5 秒。
e2 <- list2env(k, hash = FALSE)
system.time(for(i in 1:length(k)) f(e2, as.character(i)))
environment
需要 0.3 秒。快10倍!!使用 hash = TRUE
,速度提高 100 倍。