R 中的 For 循环是邪恶的吗?

Are For loops evil in R?

我听说您并不是要将过程式编程风格强加到 R 上。我发现这很难。我刚刚用 for 循环解决了一个问题。这是错误的吗?有没有更好、更"R-style"的解决方案?

问题:我有两列:Col1 和 Col2。 Col1 包含以自由格式输入的职务。我想使用 Col2 将这些职位分类(因此 "Junior Technician"、"Engineering technician" 和 "Mech. tech." 都列为 "Technician"。

我是这样做的:

jobcategories<-list(
"Junior Technician|Engineering technician|Mech. tech." = "Technician",
"Manager|Senior Manager|Group manager|Pain in the ****" = "Manager",
"Admin|Administrator|Group secretary" = "Administrator")

for (currentjob in names(jobcategories)) {
  df$Col2[grep(currentjob,data$Col1)] <- jobcategories[[currentjob]]
}

这产生了正确的结果,但我无法摆脱(由于我的程序经验)我没有正确使用 R 的感觉。 R 专家可以让我摆脱痛苦吗?

编辑

我被要求提供原始数据。不幸的是,我不能提供它,因为里面有机密信息。它基本上是两列。第一列包含 400 多行不同的职位名称(以及奇怪的个人姓名)。这 400 个标题可以分为大约 20 个不同的类别。第二列以 NA 开头,然后在 运行 for 循环之后填充。

for 循环在 R 中不是 'evil',但与基于向量的方法相比它们通常较慢并且通常不是最佳可用解决方案,但是它们易于实施且易于理解,您不应低估其中任何一个的价值。

因此,在我看来,如果您需要快速完成某件事并且找不到更好的方法,您应该使用 for 循环 and 速度不用太担心

你通常会发现有一种非 'for-loop' 的做事方式。

例如:

如果您创建一个简单的 table 将旧作业映射到新作业:

job_map <- data.frame(
  current = c("Junior Technician", "Engineering technician", "Mech. tech.",
              "Manager", "Senior Manager", "Group manager", "Pain in the ****",
              "Admin", "Administrator", "Group secretary"),
  new = c(rep("Technician",3), rep("Manager",4), rep("Administrator",3))
)

并且您有 table 个职位需要重新分类:

my_df <- data.frame(job_name = sample(job_map$current, 50, replace = TRUE))

匹配命令将帮助您:

my_df$new <- job_map$new[match(my_df$job_name, job_map$current)]    
my_df

你是对的,在 R 中经常不鼓励使用 for 循环,根据我的经验,这主要有两个原因:

增长对象

正如 R inferno 的圈子 2 中雄辩地描述的那样,一次增加一个对象的一个​​元素可能是极其低效的,这在 for 循环中常常是一种诱惑。例如,这是一个非常常见但效率低下的工作流程,因为它会重新分配 output 循环的每次迭代:

output <- c()
for (idx in indices) {
  scalar <- compute.new.scalar(idx)
  output <- c(output, scalar)
}

可以通过将 output 预分配到适当的大小并使用 for 循环或使用像 sapply.

这样的函数来消除这种低效率

错过更快的矢量化替代方案

效率低下的第二个来源是在存在向量化替代方案时对快速操作执行 for 循环。例如,考虑以下代码:

s <- 0
for (elt in x) {
  s <- s + elt
}

这是一个用于非常快速操作(将两个数字相加)的 for 循环,与向量化 sum 函数相比,循环的开销将是显着的,后者将向量中的所有元素相加. sum 函数很快,因为它是用 C 实现的,所以执行 s <- sum(x) 比使用 for 循环更有效(更不用说更少的输入)。有时需要更多的创造力来弄清楚如何用向量化的替代方法替换具有快速内部的 for 循环(cumsumdiff 经常出现),但它可以显着提高效率。如果您有一个快速循环内部但无法弄清楚如何使用矢量化函数来实现同样的事情,我发现使用 Rcpp 包重新实现循环可以产生更快的替代方法。

总而言之...

如果对象增长不正确,或者循环内部速度非常快,则 For 循环可能会很慢,并且可以用矢量化操作替换整个循环。否则你可能不会损失太多效率,因为 apply 函数族也在内部执行 for 循环。