【问题标题】:Mutate new column based on moving window of fixed date interval size, in R基于固定日期间隔大小的移动窗口在R中改变新列
【发布时间】:2021-09-20 06:51:54
【问题描述】:

我在 R 中有一位患者的数据,其中显示了他们在特定条件下检测呈阳性的日期。数据如下所示:

      date  positive
2005-02-22      yes
2005-04-26       no
2005-08-02      yes
2005-10-04       no
2005-12-06      yes
2006-03-14       no
2006-06-06       no
2006-09-12      yes
2006-12-19      yes
2007-03-27      yes

现在我介绍一个新定义。如果“当前检测结果为阳性,并且 >=50% 的前 365 天检测结果为阳性”,则该患者的状况被定义为“慢性阳性”。 所以我想创建一个输出数据集,告诉我患者在每个日期是否长期呈阳性。例如,输出应如下所示(例如,在 2006-09-12,它们是“阳性”,但不是“慢性阳性”,因为过去 365 天的 4 次访问中有 3 次是阴性的):

      date  positive  chronic
2005-02-22      yes        no
2005-04-26       no        no
2005-08-02      yes       yes
2005-10-04       no        no
2005-12-06      yes       yes
2006-03-14       no        no
2006-06-06       no        no
2006-09-12      yes        no
2006-12-19      yes        no
2007-03-27      yes       yes

我该怎么做?在感兴趣的每一行,我需要能够查看之前的行(在过去 365 天内)并评估其中有多少是积极的。我想我可以结合使用lead/lag 函数和dplyr,但我希望能举个例子说明如何做到这一点。

原始数据可以通过以下方式复制:

dat <- structure(list(date = structure(c(12836, 12899, 12997, 13060, 13123, 13221, 13305, 13403, 13501, 13599), class = "Date"), 
                      positive = c("yes", "no", "yes", "no", "yes", "no", "no", "yes", "yes", "yes")), 
                 row.names = c(NA, 10L), class = "data.frame")

【问题讨论】:

    标签: r dplyr conditional-statements lag rolling-computation


    【解决方案1】:

    您可以使用slider 库进行此类滚动计算。语法解释-

    • slide_index_lgl 同时作用于向量 .x 和索引 .i 并产生逻辑向量输出。
    • .x 用作positive 向量
    • .i 用作date 向量
    • .before.after 不言自明(包括前 365 天,不包括当前日期)
    • .f 很简单,用于检查过去 365 天的测试阳性
    • 此输出与另一个条件相结合,即positive == 'yes' 我使用了这个公式(sum(.x == 'yes') / length(.x)) &gt;= 0.5
    • 1 被添加到这个逻辑输出,给我们1FALSE2TRUE
    • 这个完整的输出被用作输出向量的索引c('No', 'Yes') so that you'll get YesforTRUEandNoforFALSE`
    library(tidyverse)
    
    df <- read.table(header = TRUE, text = 'date  positive
    2005-02-22      yes
    2005-04-26       no
    2005-08-02      yes
    2005-10-04       no
    2005-12-06      yes
    2006-03-14       no
    2006-06-06       no
    2006-09-12      yes
    2006-12-19      yes
    2007-03-27      yes')
    
    df$date <- as.Date(df$date)
    
    library(slider)
    library(lubridate)
    
    df %>%
      mutate(chronic = c('No', "Yes")[1 + (positive == 'yes' &  slide_index_lgl(positive, date, 
                                  ~ (sum(.x == 'yes') / length(.x)) >= 0.5  , 
                                  .before = days(365), 
                                  .after = days(-1)))])
    
    #>          date positive chronic
    #> 1  2005-02-22      yes    <NA>
    #> 2  2005-04-26       no      No
    #> 3  2005-08-02      yes     Yes
    #> 4  2005-10-04       no      No
    #> 5  2005-12-06      yes     Yes
    #> 6  2006-03-14       no      No
    #> 7  2006-06-06       no      No
    #> 8  2006-09-12      yes      No
    #> 9  2006-12-19      yes      No
    #> 10 2007-03-27      yes     Yes
    

    在 baseR 中使用 runner::runner() 的替代策略

    dat <- structure(list(date = structure(c(12836, 12899, 12997, 13060, 13123, 13221, 13305, 13403, 13501, 13599), class = "Date"), 
                          positive = c("yes", "no", "yes", "no", "yes", "no", "no", "yes", "yes", "yes")), 
                     row.names = c(NA, 10L), class = "data.frame")
    
    library(runner)
    
    dat$chronic <- ifelse(runner(dat$positive, idx = dat$date, lag = '1 day',
                                 k = '365 days',
                                 f = \(.x) (sum(.x == 'yes')/length(.x)) >= 0.5) & dat$positive == 'yes', 'yes', 'no')
    dat
    #>          date positive chronic
    #> 1  2005-02-22      yes    <NA>
    #> 2  2005-04-26       no      no
    #> 3  2005-08-02      yes     yes
    #> 4  2005-10-04       no      no
    #> 5  2005-12-06      yes     yes
    #> 6  2006-03-14       no      no
    #> 7  2006-06-06       no      no
    #> 8  2006-09-12      yes      no
    #> 9  2006-12-19      yes      no
    #> 10 2007-03-27      yes     yes
    

    【讨论】:

      【解决方案2】:

      data.table 中使用非等连接的另一个选项:

      library(data.table)
      setDT(dat)[, yrago := date - 365L]
      dat[, chronic := fifelse(
          .SD[.SD, on=.(date>=yrago, date<date), 
              by=.EACHI, .N>0 & i.positive=="yes" & sum(x.positive=="yes")/.N >= 0.5]$V1,
          "yes", "no")
      ]
      dat[, yrago := NULL][]
      

      输出:

                date positive chronic
       1: 2005-02-22      yes      no
       2: 2005-04-26       no      no
       3: 2005-08-02      yes     yes
       4: 2005-10-04       no      no
       5: 2005-12-06      yes     yes
       6: 2006-03-14       no      no
       7: 2006-06-06       no      no
       8: 2006-09-12      yes      no
       9: 2006-12-19      yes      no
      10: 2007-03-27      yes     yes
      

      【讨论】:

        【解决方案3】:

        如果您不想使用滚动功能,也可以使用此解决方案:

        library(dplyr)
        library(purrr)
        library(lubridate)
        
        map(df %>% 
              filter(positive == "yes") %>% 
              pull(date), ~ df %>% filter(date %within% interval(.x - days(365), .x))) %>% 
          map_dfr(~ .x %>% 
                summarise(date = last(date),
                          chronic = (sum(positive == "yes")-1)/ (n()-1) >= 0.5)) %>%
          right_join(df, by = "date") %>%
          arrange(date) %>%
          mutate(chronic = if_else(is.na(chronic) | !chronic, "no", "yes"))
        
        # A tibble: 10 x 3
           date       chronic positive
           <chr>      <chr>   <chr>   
         1 2005-02-22 no      yes     
         2 2005-04-26 no      no      
         3 2005-08-02 yes     yes     
         4 2005-10-04 no      no      
         5 2005-12-06 yes     yes     
         6 2006-03-14 no      no      
         7 2006-06-06 no      no      
         8 2006-09-12 no      yes     
         9 2006-12-19 no      yes     
        10 2007-03-27 yes     yes 
        

        【讨论】:

          【解决方案4】:

          这是一种方法-

          library(dplyr)
          library(purrr)
          
          dat %>%
            mutate(chronic = map_chr(row_number(), ~{
              inds <- between(date, date[.x] - 365, date[.x] - 1)
              if(positive[.x] == "yes" && any(inds) && mean(positive[inds] == 'yes') >= 0.5) 'yes' else 'no'
              }))
          
          #         date positive chronic
          #1  2005-02-22      yes      no
          #2  2005-04-26       no      no
          #3  2005-08-02      yes     yes
          #4  2005-10-04       no      no
          #5  2005-12-06      yes     yes
          #6  2006-03-14       no      no
          #7  2006-06-06       no      no
          #8  2006-09-12      yes      no
          #9  2006-12-19      yes      no
          #10 2007-03-27      yes     yes
          

          【讨论】:

          • 这真的很有帮助,而且效果很好!你能帮我理解语法吗?只是一般,但为什么需要any(inds) 条件?
          • 如果过去 1 年没有值,即如果 inds 全部为 FALSEmean(positive[inds] == 'yes') 返回 NA,这是一个错误。这就是为什么我要检查any(inds)
          • 你绝对是正确的,但我不明白这在逻辑上是如何解决这个错误的——即使any(inds) 在它之前,mean(positive[inds] == 'yes') 是否仍会评估为NA?更新:any(inds) 评估为 FALSE 是否在此处结束条件检查,所以 mean(positive[inds] == 'yes') 不会被评估?这是有道理的
          • 是的,完全正确。因为我们使用的是&amp;&amp;,所以只要前面的条件是FALSE,它就会停止评估参数。因此,如果any(inds)FALSE,则不会检查mean(positive[inds] == 'yes')。 @鲍勃
          • 在我看来,这是最简单的答案,也很容易实现,只需要 dplyrpurrrmap 功能 - 谢谢你的帮助!
          猜你喜欢
          • 2021-09-10
          • 1970-01-01
          • 2021-05-04
          • 2018-10-05
          • 1970-01-01
          • 2011-08-03
          • 2012-04-08
          • 2019-02-09
          • 2019-03-02
          相关资源
          最近更新 更多