【问题标题】:Duplicate rows with conditions in dataframe without looping在数据框中复制带有条件的行而不循环
【发布时间】:2021-12-23 05:59:45
【问题描述】:

我正在处理一个大型数据集(超过 100 万行),例如两列日期和一个延迟编号。

   ID    col1    Date         Delay
1:  A     100    2021-05-01    1
2:  B     200    2018-04-03    3
3:  C     300    2020-02-17    2

我想根据延迟量复制表中的行,同时增加新列中每一行的日期:

   ID    col1    Date         Delay   New_Date
1:  A     100    2021-05-01    1      2021-05-02
2:  B     200    2018-04-03    3      2018-04-04
3:  B     200    2018-04-03    3      2018-04-05
4:  B     200    2018-04-03    3      2018-04-06
5:  C     300    2020-02-17    2      2020-02-18
6:  C     300    2020-02-17    2      2020-02-19

我目前正在使用 for each 循环执行此操作,效率极低且需要大量时间。

for(row in 1:nrow(df)) {
  delay <- as.numeric(df[row, "Delay"])
  tempdf <- df[0,]
    
  for(numberDelay in 1:delay) {
    tempdf[numberDelay, ] <- df[row, ]
    tempdf[numberDelay, "New_Date"] <- as.Date.character(tempdf[numberDelay, "Date"] + as.numeric(numberDelay), 
    tryFormats = "%Y-%m-%d")
  }
  result <- rbind(result, tempdf)
}

上下文:这将允许我通过进一步将新日期与列入黑名单的日期列表进行比较来确定周末或国定假日的延误。 在 R 中有没有一种有效的方法来做到这一点?

浣熊

【问题讨论】:

    标签: r dataframe for-loop conditional-statements


    【解决方案1】:

    你可以试试dplyrtidyr

    library(dplyr)
    library(tidyr)
    
    df %>% 
      rowwise() %>% 
      mutate(New_Date = list(seq.Date(Date + 1, Date + Delay, by = "day"))) %>% 
      unnest(New_Date)
    #> # A tibble: 6 x 5
    #>   ID     col1 Date       Delay New_Date  
    #>   <chr> <int> <date>     <int> <date>    
    #> 1 A       100 2021-05-01     1 2021-05-02
    #> 2 B       200 2018-04-03     3 2018-04-04
    #> 3 B       200 2018-04-03     3 2018-04-05
    #> 4 B       200 2018-04-03     3 2018-04-06
    #> 5 C       300 2020-02-17     2 2020-02-18
    #> 6 C       300 2020-02-17     2 2020-02-19
    

    但是,考虑到您解释的上下文,我认为这样的事情对您来说可能更有效:

    # example of vector of blacklisted days
    blacklist_days <- as.Date(c("2020-02-18", "2018-04-04", "2018-04-05"))
    df %>% 
      rowwise() %>% 
      mutate(Dates = list(seq.Date(Date + 1, Date + Delay, by = "day"))) %>% 
      mutate(n_bl = sum(Dates %in% blacklist_days)) %>% 
      ungroup()
    #> # A tibble: 3 x 6
    #>   ID     col1 Date       Delay Dates       n_bl
    #>   <chr> <int> <date>     <int> <list>     <int>
    #> 1 A       100 2021-05-01     1 <date [1]>     0
    #> 2 B       200 2018-04-03     3 <date [3]>     2
    #> 3 C       300 2020-02-17     2 <date [2]>     1
    

    这样可以避免行重复,这可能会影响您的性能。

    【讨论】:

    • 哇,非常感谢您,先生!我的每种方法都需要 1 个多小时,而你的方法甚至不需要 10 秒!!!
    【解决方案2】:

    您可以单独创建重复数据框,然后将它们与原始数据框组合。这使用循环遍历Delay 的不同值。

    > dat <- data.frame(ID = LETTERS[1:3], col1 = 1:3 * 100,
    +                   date = as.Date(c('2021-05-01', '2018-04-03', '2020-02-17')),
    
    +                   delay = c(1, 3, 2))
    > dat
      ID col1       date delay
    1  A  100 2021-05-01     1
    2  B  200 2018-04-03     3
    3  C  300 2020-02-17     2
    > dat$sk <- 1:nrow(dat)
    > ddup <- data.frame()
    > for (i in 2:3) {
    +
      dd <- dat[dat$delay >= i, ]
    +   dd <- dat[dat$delay >= i, ]
    +   dd$date <- dd$date + 1
    
    +   ddup <- rbind(ddup, dd)
    }
    +
    + }
    > dat <- rbind(dat, ddup)
    > dat <- dat[order(dat$sk, dat$date), ]
    > dat
       ID col1       date delay sk
    1   A  100 2021-05-01     1  1
    2   B  200 2018-04-03     3  2
    22  B  200 2018-04-04     3  2
    21  B  200 2018-04-04     3  2
    3   C  300 2020-02-17     2  3
    31  C  300 2020-02-18     2  3
    >
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-22
      • 2018-08-17
      • 1970-01-01
      • 2021-05-08
      • 2014-03-01
      • 1970-01-01
      相关资源
      最近更新 更多