在 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 中,如何真正快速地遍历数据帧的行?的主要内容,如果未能解决你的问题,请参考以下文章