在 R 中,如何真正快速地遍历数据帧的行?

Posted

技术标签:

【中文标题】在 R 中,如何真正快速地遍历数据帧的行?【英文标题】:In R, how do you loop over the rows of a data frame really fast? 【发布时间】:2011-03-21 05:37:06 【问题描述】:

假设您有一个包含多行多列的数据框。

列有名称。您想按数字访问行,按名称访问列。

例如,一种(可能很慢)循环遍历行的方法是

for (i in 1:nrow(df)) 
  print(df[i, "column1"])
  # do more things with the data frame...

另一种方法是为单独的列创建“列表”(如column1_list = df[["column1"]),并在一个循环中访问这些列表。这种方法可能很快,但如果您想访问许多列,也很不方便。

是否有一种快速循环数据框行的方法?其他一些数据结构是否更适合快速循环?

【问题讨论】:

这与 df[, "column1"] 有何不同?另请参阅 ?apply with margin = 1。 这个例子不是我真正想做的。我想在数据框中写一些值作为 javascript 文件中的数据。为“申请”中的“保证金”信息+1。 在特定情况下,我需要遍历行来移动列中的值。我被提醒在 R 中执行此操作的更好方法是:***.com/questions/7746567/… 【参考方案1】:

我认为我需要给出一个完整的答案,因为我发现 cmets 更难跟踪,而且我已经失去了对此的评论......nullglob 的一个例子展示了 for 和 apply 家族函数之间的差异比其他例子好得多。当一个函数使得它非常慢时,这就是所有速度都被消耗的地方,你不会发现循环变化之间的差异。但是当你让函数变得微不足道时,你就会看到循环对事物的影响有多大。

我还想补充一点,apply 系列的一些成员在其他示例中未探索过,它们具有有趣的性能属性。首先,我将在我的机器上显示 nullglob 的相关结果的复制。

n <- 1e6
system.time(for(i in 1:n) sinI[i] <- sin(i))
  user  system elapsed 
 5.721   0.028   5.712 

lapply runs much faster for the same result
system.time(sinI <- lapply(1:n,sin))
   user  system elapsed 
  1.353   0.012   1.361 

他还发现 sapply 慢得多。这是其他一些未经测试的。

普通旧适用于数据的矩阵版本...

mat <- matrix(1:n,ncol =1),1,sin)
system.time(sinI <- apply(mat,1,sin))
   user  system elapsed 
  8.478   0.116   8.531 

因此,apply() 命令本身比 for 循环慢很多。 (如果我使用 sin(mat[i,1]),for 循环不会明显减慢。

另一个似乎没有在其他帖子中测试过的是tapply。

system.time(sinI <- tapply(1:n, 1:n, sin))
   user  system elapsed 
 12.908   0.266  13.589 

当然,人们永远不会以这种方式使用 tapply,而且它的实用性在大多数情况下远远超出任何此类速度问题。

【讨论】:

+1 用于引用 nullglob。他的帖子引用了 Uwe Ligges 和 John Fox 于 2008 年 5 月在“R News”中发表的文章“我如何避免此循环或使其更快?”。感谢您撰写有关 apply 函数的文章。【参考方案2】:

最快的方法是不循环(即向量化操作)。您需要循环的唯一实例之一是存在依赖关系(即一个迭代依赖于另一个迭代)。否则,请尝试在循环外进行尽可能多的矢量化计算。

如果您确实需要循环,那么使用for 循环基本上与其他任何方法一样快(lapply 可能会快一点,但other apply functions tend to be around the same speed as for)。

【讨论】:

也许没有办法避免我想做的事情的循环——请参阅我对上面 Greg 评论的回复。 “差不多一样的速度”?你读过所有的答案吗?在我的回答中,我表明使用 vapply 比 for 循环快 3 倍(例如)... 在算法效率方面,它们在速度上非常相似:algorithmic efficiency 矢量化版本确实比 for-loop 版本更高效,感谢@Shane【参考方案3】:

利用 data.frames 本质上是列向量列表这一事实,可以使用 do.call 应用一个函数,该函数具有 data.frame 每一列上的列数(类似于“压缩”在其他语言的列表中)。

do.call(paste, data.frame(x=c(1,2), z=c("a","b"), z=c(5,6)))

【讨论】:

但这不是循环。 很好的答案,OP 清楚地询问“你如何遍历行”。这是迄今为止最有效的方法,并且根本不需要for。我有一个for 循环,现在采用这种方法,将速度提高了大约 40 倍!

以上是关于在 R 中,如何真正快速地遍历数据帧的行?的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地计算数据帧的行数? [复制]

更快地遍历一个 DataFrame 的行以将列添加到第二个 DataFrame

累积总和数据帧的条件计数 - 遍历列

循环遍历熊猫中的行[重复]

遍历隐藏那些不包含数组中列出的值的行

遍历数据框,其中每次迭代都有效地依赖于 R 中的前一项