我想知道是否有办法在不使用for
循环的情况下对列表中的对象进行就地修改。例如,如果列表中的单个对象很大且很复杂,那么这将非常有用,因此我们希望避免制作整个对象的临时副本。例如,考虑以下代码,该代码创建三个数据帧的列表,然后计算一列数据的所有三个数据帧的最大值向量,然后将该向量分配给每个原始数据帧。 (在ggplot2中对齐图时需要这样的代码。)
data_list <- lapply(1:3, function(x) data.frame(x=rnorm(10), y=rnorm(10), z=rnorm(10)))
max_x <- do.call(pmax, lapply(data_list, function(d){d$x}))
for( i in 1:length(data_list))
{
data_list[[i]]$x <- max_x
}
有没有办法在没有for
循环的情况下编写最终部分?
我收到的一些问题的答案:
是什么让我觉得会有副本?我不确定是否会制作副本。我正在处理的实际情况涉及整个ggplot图表(参见例如here)。由于它们相当大而且复杂,所以不能复制它是至关重要的。
for
循环的问题是什么?我只想直接在列表上迭代而不是引入计数器。我不喜欢专柜。
为什么不使用data.table
?因为我实际上在操纵ggplot图,而不是数据框。这里提供的代码只是一个简化的例子。
答案 0 :(得分:4)
Base R数据结构是通过共享进行复制修改的。以带有三个数字列的data.frame为例。每个data.frame是一个长度为3的“列表”向量,每个向量包含对基础列的数字向量的引用。如果我们修改/替换第一列,则R创建一个新的长度为3的data.frame“list”,其中包含对新(已修改)列和其他两个未修改列的引用。
让我们看一下使用address
函数*
set.seed(1)
data_list <- lapply(1:3, function(x) data.frame(x=rnorm(10), y=rnorm(10), z=rnorm(10)))
before <- rapply(data_list,address)
现在您要用
替换第一列max_x <- do.call(pmax, lapply(data_list, function(d){d$x}))
你如何做到这一点并不重要,但这里有一种没有明确循环计数器的方法
data_list <- lapply(data_list,`[<-`,"x",value=max_x)
after <- rapply(data_list,address)
现在比较之前和之后的地址。请注意,y
和z
列的地址未更改。此外,所有“之后”x
列都具有相同的地址 - max_x
的地址!
address(max_x)
[1] "05660600"
cbind(before,after)
before after
x "0565F530" "05660600"
y "0565F400" "0565F400"
z "05660AC0" "05660AC0"
x "05660A28" "05660600"
y "05660990" "05660990"
z "05660860" "05660860"
x "056607C8" "05660600"
y "05660730" "05660730"
z "05660698" "05660698"
这意味着您不必担心可能考虑对大型数据结构进行更改。通常,只需要替换修改的部分和数据结构的骨架。在这个例子中,无论如何都必须创建max_x
向量,因此唯一的开销是创建一个新的3单元data.frame“list”并用3个引用填充它**。但是,如果您反复“敲击”更改或使用子向量而不是整个列,这可能会开始变得低效。这些是data.table
的用例,不适用于此示例。
*此处使用的address
函数是从data.table
包中导出的。
**当然,在这个例子中,3个单元格外部列表“list”包含3个data.frames本身。