【问题标题】:Reorganize time related data by group and ID to a unique row per ID按组和 ID 将时间相关数据重新组织到每个 ID 的唯一行
【发布时间】:2021-09-16 21:22:32
【问题描述】:

我在 R 中遇到了一个古怪的数据形状,我无法有效地解决这个问题。实际上,我通过一些简单的连接找到了一些解决方案,但让我们假设我的数据非常大,并且这样做会显着增加我的内存使用量,这在这里不是最佳的。另一个解决方案,甚至不是最优的,将涉及循环遍历整个数据集,再次发现它的效率有点低。

数据集的想法是为给定的人群提供测试疾病的日期时间间隔以及与之相关的结果。数据按行组织,每一行是给定 id 和测试结果的时间间隔(在我的示例中,我只选择正面测试,但也可能是负面的)。通常这些时期是连续的,即第二个时期的开始是在第一个时期结束之后的一天(参见示例),但有时几个月可以在没有任何信息的情况下过去,然后执行另一个测试。

以下是数据示例:

library(tidyverse)
library(lubridate)

start <- c('2017-08-28', '2018-03-14', '2018-08-27', '2020-02-26', '2020-09-01')
finish <- c('2018-03-13', '2018-08-26', '2018-11-28', '2020-08-31', '2021-03-01')
id <- rep('a', 5)

df <- data.frame('start' = ymd(start),
                 'finish' = ymd(finish),
                 'id' = id,
                 'test' = rep('positif', 5))

这给出了:

> df
       start     finish id    test
1 2017-08-28 2018-03-13  a positif
2 2018-03-14 2018-08-26  a positif
3 2018-08-27 2018-11-28  a positif
4 2020-02-26 2020-08-31  a positif
5 2020-09-01 2021-03-01  a positif

经过一些调整,想法是计算每次测试之间的天间隔

dff <- df %>%
  mutate(finish_lag = lag(finish),
         interval = start - finish_lag
  )
  
> dff
       start     finish id    test finish_lag interval
1 2017-08-28 2018-03-13  a positif       <NA>  NA days
2 2018-03-14 2018-08-26  a positif 2018-03-13   1 days
3 2018-08-27 2018-11-28  a positif 2018-08-26   1 days
4 2020-02-26 2020-08-31  a positif 2018-11-28 455 days
5 2020-09-01 2021-03-01  a positif 2020-08-31   1 days

我想要的是,每行有一个 ID,持续一段时间,在这个例子中,我只有 2 行:第一个周期(第 1 到 3 行),然后是 455 天后的第二个周期到数据的末尾。通常我只会有 1 行,因为测试是连续的。

所以在这个例子中,期望的输出是:

       start     finish id    test
1 2017-08-28 2018-11-28  a positif
2 2020-02-26 2021-03-01  a positif

【问题讨论】:

    标签: r dataframe datetime


    【解决方案1】:

    我希望你想要这个?

    • 我假设,您的数据针对每个idtest 进行了排序。如果不是,则必须在应用以下代码之前对其进行排序
    • 只需修改lag 中的默认参数以适应我们的需要。在对idtest 进行分组之后,我在第一个开始日期前一天添加了日期(假设您希望您的结果像这样分组)。否则删除该 group_by
    • 创建了一个虚拟的grp 变量来分隔差异不是连续天数的数据。
    library(tidyverse)
    library(lubridate)
    
    df %>% group_by(id, test) %>%
      mutate(finish_lag = lag(finish, default = first(start) - 1),
             interval = start - finish_lag) %>%
      group_by(grp = cumsum(interval != 1) + 1, .add = T) %>%
      summarise(start = first(start),
                finish = last(finish), .groups = 'drop')
    
    #> # A tibble: 2 x 5
    #>   id    test      grp start      finish    
    #>   <chr> <chr>   <dbl> <date>     <date>    
    #> 1 a     positif     1 2017-08-28 2018-11-28
    #> 2 a     positif     2 2020-02-26 2021-03-01
    

    一旦你理解了代码/策略,这一切都可以简化为

    df %>% group_by(id, test, grp = cumsum((start - lag(finish, default = first(start) -1)) != 1) + 1) %>%
      summarise(start = first(start),
                finish = last(finish), .groups = 'drop')
    
    # A tibble: 2 x 5
      id    test      grp start      finish    
      <chr> <chr>   <dbl> <date>     <date>    
    1 a     positif     1 2017-08-28 2018-11-28
    2 a     positif     2 2020-02-26 2021-03-01
    

    【讨论】:

    • 真的很有帮助,cumsum 组很有趣,我必须进一步检查它才能完全理解(与第一个和最后一个汇总相同),但它工作得很好。是的,数据已排序,但感谢指出!我想知道,如果我想减少对测试间隔的限制,我可以用 (| != 2) 更改 cumsum(interval !=1) 以避免将 2 天的差异分组吗?我的问题可能有点令人困惑...
    • @Ovidiu,是的,如果您不想考虑间隔 1-2 天的间隔,那么您也可以使用 cumsum(interval &gt; n) 其中 n 是您想要的数字
    猜你喜欢
    • 1970-01-01
    • 2021-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-21
    • 2023-02-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多