【问题标题】:dplyr - arrange, group, compute difference in datesdplyr - 排列、分组、计算日期差异
【发布时间】:2016-07-09 21:15:35
【问题描述】:

我有一个大型数据集,显示了孩子从“健康”事件到随后的“生病”事件的跟进情况

我正在尝试使用 dplyr 计算“健康”事件和第一个“生病”事件之间的时间

模拟数据集

 id <- c(1,1,1,1,1,1) 
event <- c("healthy","","","sick","sick","")
date_follow_up <- c("4/1/15", "4/2/15", "4/3/15", "4/4/15", "4/5/15", "4/6/15")

df1 <- data_frame(id, event, date_follow_up)

模拟输出数据集

id <- c(1,1,1,1,1,1) 
event <- c("healthy","","","sick","sick","")
date_follow_up <- c("4/1/15", "4/2/15", "4/3/15", "4/4/15", "4/5/15", "4/6/15")
diff_time <- c(3,"","","","","")

df1 <- data_frame(id, event, date_follow_up, diff_time)

我只能使用 dplyr 按“id”和“date_follow_up”对数据进行排序,然后按“id”分组:

df2 <- df1 %>% arrange(id, date_follow_up) %>% group_by(id)

请帮助计算日期差异并将其添加到每个人的“健康”事件行旁边:)

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    使用@akrun 的示例数据,这是使用 data.table 中的 rolling joins 的一种方法:

    require(data.table)
    dt = as.data.table(mydf)[, date_follow_up := as.Date(date_follow_up, format="%m/%d/%y")][]
    dt1 = dt[event == "healthy"]
    dt2 = dt[event == "sick"]
    
    idx = dt2[dt1, roll = -Inf, which = TRUE, on = c("id", "date_follow_up")]
    

    想法是:对于每个健康日期(dt1),获取第一个生病日期(dt2&gt;= 健康日期的索引。

    那么直接将两个日期相减即可得到最终结果。

    dt[event == "healthy", 
         diff := as.integer(dt2$date_follow_up[idx] - dt1$date_follow_up)]
    

    【讨论】:

    • 嗨,感谢您的反馈,在运行最后一行代码时,我收到错误:[.data.table(dt2, dt1, roll = -Inf, which = TRUE, on = 中的错误。 (id, : 找不到函数“。”
    【解决方案2】:

    我稍微修改了您的数据以彻底检查此案例。我的建议与 alistaire 的建议类似。我的建议可以为mydf 中的 id 2 生成 NA,而 alistaire 建议会创建 Inf。首先,我将您的日期(以字符形式)转换为 Date 对象。然后,我将数据按id 分组,并通过从第一天减去healthy 的第一天(即date_follow_up[event == "healthy"][1])来计算时差sick(即date_follow_up[event == "sick"][1])。最后,我将不相关行的时差替换为 NA。

       id   event date_follow_up
    1   1 healthy         4/1/15
    2   1                 4/2/15
    3   1                 4/3/15
    4   1    sick         4/4/15
    5   1    sick         4/5/15
    6   2                 4/1/15
    7   2 healthy         4/2/15
    8   2                 4/3/15
    9   2                 4/4/15
    10  2                 4/5/15
    11  3                 4/1/15
    12  3 healthy         4/2/15
    13  3    sick         4/3/15
    14  3                 4/4/15
    15  3                 4/5/15
    
    library(dplyr)
    mutate(mydf, date_follow_up = as.Date(date_follow_up, format = "%m/%d/%y")) %>%
    group_by(id) %>%
    mutate(foo = date_follow_up[event == "sick"][1] - date_follow_up[event == "healthy"][1],        
           foo = replace(foo, which(event != "healthy"), NA))
    
    
    Source: local data frame [15 x 4]
    Groups: id [3]
    
          id   event date_follow_up            foo
       <int>   <chr>         <date> <S3: difftime>
    1      1 healthy     2015-04-01         3 days
    2      1             2015-04-02        NA days
    3      1             2015-04-03        NA days
    4      1    sick     2015-04-04        NA days
    5      1    sick     2015-04-05        NA days
    6      2             2015-04-01        NA days
    7      2 healthy     2015-04-02        NA days
    8      2             2015-04-03        NA days
    9      2             2015-04-04        NA days
    10     2             2015-04-05        NA days
    11     3             2015-04-01        NA days
    12     3 healthy     2015-04-02         1 days
    13     3    sick     2015-04-03        NA days
    14     3             2015-04-04        NA days
    15     3             2015-04-05        NA days
    

    数据

    mydf <- structure(list(id = c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 
    3L, 3L, 3L, 3L, 3L), event = c("healthy", "", "", "sick", "sick", 
    "", "healthy", "", "", "", "", "healthy", "sick", "", ""), date_follow_up = c("4/1/15", 
    "4/2/15", "4/3/15", "4/4/15", "4/5/15", "4/1/15", "4/2/15", "4/3/15", 
    "4/4/15", "4/5/15", "4/1/15", "4/2/15", "4/3/15", "4/4/15", "4/5/15"
    )), .Names = c("id", "event", "date_follow_up"), row.names = c(NA, 
    -15L), class = "data.frame")
    

    【讨论】:

    • 您好 Jazzurro,就像 Akrun 的一样,我感谢您考虑到多个健康/疾病事件以及您如何满足其他 id 的需求。让我看看“真实”数据是如何响应的。干杯
    • @K.Kariuki Pleasure oto 帮助你。
    【解决方案3】:

    我们也可以使用data.table。将“data.frame”转换为“data.table”(setDT(mydf)),使用as.Date将“date_follow_up”的类更改为Date,按“id”分组,并通过获取的累积和创建的分组变量逻辑向量(event == "healthy"),我们得到第一个“生病”“事件”的“date_follow_up”与第一个“date_follow_up”(这将是“健康”)的区别ifany“生病”该特定组中的“事件”或else 返回“NA”。

    library(data.table)
    setDT(mydf)[, date_follow_up := as.Date(date_follow_up, "%m/%d/%y")
        ][, foo := if(any(event == "sick"))  
                      as.integer(date_follow_up[which(event=="sick")[1]] - 
                             date_follow_up[1] )
                    else NA_integer_ , 
         by = .(grp= cumsum(event == "healthy"), id)]
    

    然后,我们可以将所有不“健康”的“事件”的“foo”更改为“NA”。

    mydf[event!= "healthy", foo := NA_integer_]
    mydf
    #    id   event date_follow_up foo
    # 1:  1 healthy     2015-04-01   3
    # 2:  1             2015-04-02  NA
    # 3:  1             2015-04-03  NA
    # 4:  1    sick     2015-04-04  NA
    # 5:  1    sick     2015-04-05  NA
    # 6:  2             2015-04-01  NA
    # 7:  2 healthy     2015-04-02  NA
    # 8:  2             2015-04-03  NA
    # 9:  2             2015-04-04  NA
    #10:  2             2015-04-05  NA
    #11:  3             2015-04-01  NA
    #12:  3 healthy     2015-04-02   1
    #13:  3    sick     2015-04-03  NA
    #14:  3             2015-04-04  NA
    #15:  3             2015-04-05  NA
    #16:  4             2015-04-01  NA
    #17:  4 healthy     2015-04-02   3
    #18:  4             2015-04-03  NA
    #19:  4             2015-04-04  NA
    #20:  4    sick     2015-04-05  NA
    #21:  4    sick     2015-04-06  NA
    #22:  4             2015-04-07  NA
    #23:  4 healthy     2015-04-08   2
    #24:  4             2015-04-09  NA
    #25:  4    sick     2015-04-10  NA
    

    注意:在这里,我准备了数据,其中对于特定的“id”可能存在多个“健康/生病”“事件”。

    数据

    mydf <- structure(list(id = c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 
    3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4), event = c("healthy", "", 
    "", "sick", "sick", "", "healthy", "", "", "", "", "healthy", 
    "sick", "", "", "", "healthy", "", "", "sick", "sick", "", "healthy", 
    "", "sick"), date_follow_up = c("4/1/15", "4/2/15", "4/3/15", 
    "4/4/15", "4/5/15", "4/1/15", "4/2/15", "4/3/15", "4/4/15", "4/5/15", 
    "4/1/15", "4/2/15", "4/3/15", "4/4/15", "4/5/15", "4/1/15", "4/2/15", 
    "4/3/15", "4/4/15", "4/5/15", "4/6/15", "4/7/15", "4/8/15", "4/9/15", 
    "4/10/15")), .Names = c("id", "event", "date_follow_up"), row.names = c(NA, 
    25L), class = "data.frame")
    
    
     
    

    【讨论】:

    • 您进一步考虑了可能的情况。加一回给你。 :)
    • @jazzurro 谢谢,是的,我有点好奇我的第一个解决方案是否适用,但它没有。
    • @akrun 我相信 OP 有足够的东西来考虑他/她如何解决这个难题。
    • 您好 Akrun,感谢您考虑多个健康/疾病事件。非常感激。现在看看“真实”数据如何响应:)
    • 嗨@akrun,我已经在更大的数据集上进行了尝试,并且效果很好。但是,我需要做一些调整。到目前为止,该脚本会在每个人的每个“健康”事件之后寻找第一个“生病”事件。请协助我调整:对于每个人,按日期对条目进行排序,如果第一个条目是“健康的”,则找到随后的第一个生病事件并获得日期差异。它不应考虑随后发生的其他“健康”事件
    【解决方案4】:

    这是一种方法,但如果每个 ID 有多个“健康”事件,您可能需要对其进行调整以使其变得更加健壮:

            # turn dates into subtractable Date class
    df1 %>% mutate(date_follow_up = as.Date(date_follow_up, '%m/%d/%y')) %>% 
        group_by(id) %>%
               # Add new column. If there is a "healthy" event,
        mutate(diff_time = ifelse(event == 'healthy', 
                                  # subtract the date from the minimum "sick" date
                                  min(date_follow_up[event == 'sick']) - date_follow_up, 
                                  # else if it isn't a "healthy" event, return NA.
                                  NA))
    
    ## Source: local data frame [6 x 4]
    ## 
    ##      id   event date_follow_up diff_time
    ##   <dbl>   <chr>         <date>     <dbl>
    ## 1     1 healthy     2015-04-01         3
    ## 2     1             2015-04-02        NA
    ## 3     1             2015-04-03        NA
    ## 4     1    sick     2015-04-04        NA
    ## 5     1    sick     2015-04-05        NA
    ## 6     1             2015-04-06        NA
    

    【讨论】:

    • 嗨,Alistare。感谢您的反馈。 Akrun 和 Jazzurro 为多个“健康事件”提供服务。不过,非常感谢:)
    【解决方案5】:

    这是使用dplyr 的另一种方法(尽管与之前的解决方案相比要长一些)

    library(dplyr)
    df1$date_follow_up <- as.Date(df1$date_follow_up, "%m/%d/%y")
    
    df1 %>% group_by(id, event) %>%
            filter(event %in% c("healthy", "sick")) %>%
            slice(which.min(date_follow_up)) %>% group_by(id) %>%
            mutate(diff_time = lead(date_follow_up) - date_follow_up) %>% 
            right_join(df1, by = c("id", "event" , "date_follow_up"))
    
    # Output 
    
    Source: local data frame [6 x 4]
    Groups: id [?]
    
          id   event   date_follow_up       diff_time
         <dbl>   <chr>         <date>  <S3: difftime>
    1     1   healthy     2015-04-01         3 days
    2     1               2015-04-02        NA days
    3     1               2015-04-03        NA days
    4     1      sick     2015-04-04        NA days    
    5     1      sick     2015-04-05        NA days
    6     1               2015-04-06        NA days
    

    【讨论】:

    • 嗨 Sumedh,谢谢。我在我发布的原始数据集上对其进行了测试,效果很好。然后我在 Akrun 的数据集上进行了测试,该数据集类似于我拥有的“真实”数据(多个健康/生病事件),但失败并出现以下错误:警告消息:In right_join_impl(x, y, by$x, by$y ) :加入字符向量和因子,强制转换成字符向量。仍然非常感谢..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多