在 R 中对 6000 万行数据运行 for 循环 - 有没有更快的方法?

Running a for loop in R on 60 million rows of data - is there a faster way?

提问人:azimm 提问时间:9/16/2023 最后编辑:Ben Bolkerazimm 更新时间:9/16/2023 访问量:53

问:

我目前正在 R 中运行一个 for 循环,该循环试图为数据集中的“行程”创建唯一 ID,但有时行程会在数据集中拆分为两行或多行。for 循环包含定义行程是仅由一行还是多行表示所需的逻辑。这意味着 for 循环需要查看它正在评估的当前行和上一行,评估条件语句,然后应用与前一行相同的 ID 或将值递增 1 以创建新 ID。

我还必须在 for 循环中添加一个特定的检查,该循环将第一行默认为值 1,因为没有上一行可以比较,并且如果没有此添加,我就会收到错误。

这在 100 行的测试数据集上运行良好,并为我提供了我想要的输出,但现在我在 6000 万行上运行它,到目前为止它已经运行了超过 24 小时。有没有办法加快这个过程并使其更快?我无法弄清楚如何将 lapply 或其他 apply 函数与这种条件复合体一起使用,但可能误解了这些函数的工作原理。我也不确定这是否会加快处理速度。另一种选择可能是将表拆分为单独的表并分块运行我的脚本,但感觉脚本应该只查看每一行及其前面的行,所以我不确定这是否真的可以节省时间(但会提供中间保存点的故障保护,以防我的服务器崩溃, 等)。

欢迎对此进行任何改进!

这是当前脚本:

Oct19Subset <- Oct19%>%
  arrange(media_uuid, `Entry Date`) %>% 
  mutate(prev_mediaUUID = c(0, head(media_uuid, -1)))%>% 
  mutate(linked_trip_id = 1)

for (i in 1:nrow(Oct19Subset)){
  if (i == 1) {
    Oct19Subset$linked_trip_id[i] <- 1
  } else {
    if (Oct19Subset$`Ride Type`[i] == "B" | 
         Oct19Subset$media_uuid[i] != Oct19Subset$prev_mediaUUID[i]){
      Oct19Subset$linked_trip_id[i] <- Oct19Subset$linked_trip_id[i-1] + 1
    } else Oct19Subset$linked_trip_id[i] <- Oct19Subset$linked_trip_id[i-1]
  }
} 
R 性能 for-loop lapply

评论

2赞 Ben Bolker 9/16/2023
欢迎来到 Stack Overflow!您能否编辑您的问题以包含一个最小的可重复示例,即 足够多的人可以尝试潜在的解决方案?理想情况下,您的代码输出也运行在该小子集上?dput()Oct19Subset
0赞 Ben Bolker 9/16/2023
类似的东西可能会起作用......cumsum(media_uuid != prev_mediaUUID | Ride_type == "B")
0赞 Jon Spring 9/16/2023
我建议你看看这个。noamross.net/archives/2014-04-16-vectorization-in-r-why。R 可以非常快,但如果你要求它翻译相同的命令 6000 万次,就不能了。@jblood94使用 dplyr 提供了一个答案,该 dplyr 将代码矢量化,以便在一秒钟内计算出 600 万行。
0赞 azimm 9/16/2023
cumsum解决方案效果很好!为了清楚起见,我还将更新我的帖子,以包括 MRE 示例数据。

答:

1赞 jblood94 9/16/2023 #1

试试这个:

system.time(
  Oct19Subset <- Oct19%>%
    arrange(media_uuid, `Entry Date`) %>% 
    mutate(prev_mediaUUID = c(media_uuid[1] - 1, head(media_uuid, -1)))%>% 
    mutate(linked_trip_id = cumsum(`Ride Type` == "B" | media_uuid != prev_mediaUUID))
)
#>    user  system elapsed 
#>    0.74    0.14    0.87

数据:

library(dplyr)

N <- 6e6

Oct19 <- setNames(
  data.frame(
   sample(1e5, N, 1),
    as.Date(sample(19000:19600, N, 1)),
    sample(LETTERS, N, 1)
  ), c("media_uuid", "Entry Date", "Ride Type")
)

或者,如果其他地方不需要帮助程序列,可以跳过它:

Oct19Subset <- Oct19%>%
  arrange(media_uuid, `Entry Date`) %>% 
  mutate(
    linked_trip_id = cumsum(
      `Ride Type` == "B" |
        media_uuid != c(media_uuid[1] - 1, head(media_uuid, -1))
    )
  )

评论

0赞 azimm 9/16/2023
使用 cumsum 几乎可以立即完美地工作