在R中没有for循环的就地列表修改

时间:2016-03-21 17:10:43

标签: r

我想知道是否有办法在不使用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循环的情况下编写最终部分?

我收到的一些问题的答案:

  1. 是什么让我觉得会有副本?我不确定是否会制作副本。我正在处理的实际情况涉及整个ggplot图表(参见例如here)。由于它们相当大而且复杂,所以不能复制它是至关重要的。

  2. for循环的问题是什么?我只想直接在列表上迭代而不是引入计数器。我不喜欢专柜。

  3. 为什么不使用data.table?因为我实际上在操纵ggplot图,而不是数据框。这里提供的代码只是一个简化的例子。

1 个答案:

答案 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)

现在比较之前和之后的地址。请注意,yz列的地址未更改。此外,所有“之后”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本身。

相关问题