【问题标题】:R - how to speed a for loop with vectorised operations. Pratical problemR - 如何使用矢量化操作加速 for 循环。实际问题
【发布时间】:2020-06-24 19:56:32
【问题描述】:

我试图创建一个最小的示例,抱歉。 有没有办法加快这个过程? 我的 procedures 表有 4M 行。我正在处理 15 个小时,它只填充了 150 万行。 可能是用mutate,我不知道。


library(tidyverse)
library(lubridate)

frequencies <- tibble(
  id = 1:3,
  date_hour_initial = c(
    dmy_hms('01/01/2020 13:00:00'),
    dmy_hms('01/01/2020 15:00:00'),
    dmy_hms('02/01/2020 20:00:00')
  ),
  date_hour_final= c(
    dmy_hms('01/01/2020 18:00:00'),
    dmy_hms('01/01/2020 22:00:00'),
    dmy_hms('03/01/2020 05:00:00')
  ),
  id_person = c("1", "2", "2"),
  type_service = c("1", "3", "4")
) %>%
  mutate(
    intervalo = interval(
      date_hour_initial,
      date_hour_final
    )
  )


procedures <- tibble(
  id = 1:3,
  date_hour = c(
    dmy_hms('01/01/2020 17:00:00'),
    dmy_hms('01/01/2020 22:00:00'),
    dmy_hms('03/01/2020 03:00:00')
  ),
  id_person = c("1", "1", "2")
)

procedures$type_service <- vector(
  "character",
  nrow(procedures)
  )


for(i in 1:nrow(procedures)) {

frequencies %>%
    filter(
      procedures$date_hour[i] %within% intervalo,
      id_person == procedures$id_person[i]
    ) %>% pull(type_service) %>% unique() -> response

  if(length(response) == 1){
    procedures$type_service[i] <- response
  } else {
    procedures$type_service[i] <- NA_character_
  }

}



【问题讨论】:

  • 您没有进行任何分组或任何需要dplyr 的操作。您是否考虑过为此使用基础 R?
  • @cory,不过baseR也可以做分组操作。
  • @cory 我正在创建一个新专栏。通常我用dplyr::mutate 来做这件事。也许有一种方法可以将我的所有操作封装在一个 mutate 调用中。我什么都想不出来。
  • 我认为您希望在两帧之间进行间隔合并。看data.table函数foverlapsrdocumentation.org/packages/data.table/versions/1.12.8/topics/…
  • 您也可以尝试在 foreach 循环中执行此操作并将其并行化。这是一些文档。 cran.r-project.org/web/packages/doParallel/vignettes/…

标签: r performance loops for-loop dplyr


【解决方案1】:

这是一个不使用循环的 dplyr 解决方案:

library(tidyverse)

 left_join(frequencies, procedures, by = "id_person") %>%
  mutate(type_service = ifelse(date_hour %within% intervalo, type_service.x, NA)) %>% 
  select(id = id.y, date_hour, id_person, type_service) %>% 
  group_by(id) %>%
  arrange(type_service) %>%
  filter(!duplicated(id)) %>%
  ungroup() %>%
  arrange(id)
#> # A tibble: 3 x 4
#>      id date_hour           id_person type_service
#>   <int> <dttm>              <chr>     <chr>       
#> 1     1 2020-01-01 17:00:00 1         1           
#> 2     2 2020-01-01 22:00:00 1         NA          
#> 3     3 2020-01-03 03:00:00 2         4    

【讨论】:

    【解决方案2】:

    这是在data.table 中使用非等连接的选项:

    procedures[, type_service := 
        frequencies[procedures, on=.(id_person, date_hour_initial<=date_hour, date_hour_final>=date_hour),
            by=.EACHI, if (length(x.type_service) == 1L) x.type_service]$V1
    ]
    

    输出:

       id           date_hour id_person type_service
    1:  1 2020-01-01 17:00:00         1            1
    2:  2 2020-01-01 22:00:00         1         <NA>
    3:  3 2020-01-03 03:00:00         2            4
    

    数据:

    library(data.table)
    frequencies <- data.table(id = 1:3,
        date_hour_initial = as.POSIXct(c('01/01/2020 13:00:00','01/01/2020 15:00:00','02/01/2020 20:00:00'), format="%d/%m/%Y %T"),
        date_hour_final= as.POSIXct(c('01/01/2020 18:00:00','01/01/2020 22:00:00','03/01/2020 05:00:00'), format="%d/%m/%Y %T"),
        id_person = c("1", "2", "2"),
        type_service = c("1", "3", "4"))
    
    procedures <- data.table(id = 1:3, 
        date_hour = as.POSIXct(c('01/01/2020 17:00:00','01/01/2020 22:00:00','03/01/2020 03:00:00'), format="%d/%m/%Y %T"),
        id_person = c("1", "1", "2"))
    

    我的猜测是 4 mio 行大约需要一分钟?

    【讨论】:

    • 我确实设法使用 data.table::foverlaps() 在 2.76 秒内实现了结果。但是您的解决方案也很酷。非常感谢。
    【解决方案3】:

    这是一个使用模糊连接包的解决方案。第一步是按人员 ID 拆分频率和程序数据帧。这是把大问题分解成许多小问题。我没有添加任何错误检查以确保两个数据帧之间的 person_id 之间存在相应的匹配。

    一旦数据帧被拆分,循环遍历每个人的 id 并使用left_fuzzy_join 函数来匹配过程中的“data_hour”和频率中的“间隔”

    library(lubridate)
    library(dplyr)
    #divide and conquer
    #split the data frame down to list by person_id
    sfreq<-split(frequencies, frequencies$id_person)
    sprocedures <- split(procedures, procedures$id_person)
    
    library(fuzzyjoin)
    #define function for the matching
    matfun<-function(x, y){
      x %within% y
    }
    
    #define empty answer list
    answer<-list()
    #loop thru all of the split groups
    for (id in names(sfreq)) {
      print(id)
      #perfrom a fuzzy join with data_hour in procedures and the interval in frequencies
      answer[[id]]<-fuzzy_left_join(sprocedures[[id]], sfreq[[id]],  by= c("date_hour" ="intervalo"), match_fun=matfun)
    }
    
    #Combine all of the subsets into the final answer
    finalanswer<-bind_rows(answer)
    

    【讨论】:

      猜你喜欢
      • 2015-05-17
      • 2020-06-03
      • 2021-04-10
      • 2012-12-10
      • 1970-01-01
      • 2020-05-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多