【问题标题】:R: Aggregating History by ID and By Specified DataR:按 ID 和指定数据聚合历史
【发布时间】:2017-01-08 19:56:34
【问题描述】:

之前问过类似的问题,得到了很大的帮助:R: Aggregating History By ID By Date

不同之处在于,对于前一篇文章,我有兴趣汇总所有历史信息,但现在我希望仅指定 90 天前的信息。

以下是我的数据的外观示例:

strDates <- c("09/09/16", "5/7/16", "5/6/16", "2/13/16", "2/11/16","1/7/16",
          "11/8/16","6/8/16", "5/8/16","2/13/16","1/3/16", "1/1/16")
Date<-as.Date(strDates, "%m/%d/%y")
ID <- c("A", "A", "A", "A","A", "A", "B","B","B","B","B", "B")
Event <- c(1,0,1,0,1,1, 0,1,1,1,0, 1)
sample_df <- data.frame(Date,ID,Event)

以及输出:

背景信息

我想保留每次遭遇的所有附加信息,然后按 id 将以下历史信息汇总回 90 天。

  1. 过去 90 天内的先前遭遇次数
  2. 过去 90 天内的先前事件数

示例

以第 2 行为例。

第 2 行是 ID A,因此我将引用第 3-6 行(发生在第 2 行遭遇之前)。在这组行中,我们看到第 3、4、5 行以及所有行都发生在过去 90 天内,第 6 行发生在感兴趣的时间之外。

过去 90 天内从第 2 行开始的遭遇次数:3 次遭遇

第 2 行过去 90 天内的先前事件数:2 个事件(2016 年 5 月 6 日和 2016 年 2 月 11 日)

期望的输出

理想情况下,我会得到以下输出:

【问题讨论】:

    标签: r data.table dplyr


    【解决方案1】:

    这是一个替代的data.table 解决方案,应该非常有效。这利用了 v 1.10.0 中引入的新的 non-equi 连接以及 by = .EACHI,它允许您在 连接时对每个连接进行计算

    library(data.table) #v1.10.0
    setDT(sample_df)[, Date2 := Date - 90] # Set range (Maybe in future this could be avoided)
    sample_df[sample_df, # Binary join with itself
              .(Enc90D = .N, Ev90D = sum(Event, na.rm = TRUE)), # Make calculations
              on = .(ID = ID, Date < Date, Date > Date2), # Join by
              by = .EACHI] # Do calculations per each match
    #     ID       Date       Date Enc90D Ev90D
    #  1:  A 2016-09-09 2016-06-11      0     0
    #  2:  A 2016-05-07 2016-02-07      3     2
    #  3:  A 2016-05-06 2016-02-06      2     1
    #  4:  A 2016-02-13 2015-11-15      2     2
    #  5:  A 2016-02-11 2015-11-13      1     1
    #  6:  A 2016-01-07 2015-10-09      0     0
    #  7:  B 2016-11-08 2016-08-10      0     0
    #  8:  B 2016-06-08 2016-03-10      1     1
    #  9:  B 2016-05-08 2016-02-08      1     1
    # 10:  B 2016-02-13 2015-11-15      2     1
    # 11:  B 2016-01-03 2015-10-05      1     1
    # 12:  B 2016-01-01 2015-10-03      0     0
    

    【讨论】:

      【解决方案2】:

      部分矢量化的dplyr 解决方案,您可以在其中组合do(循环组)和rowwise 操作(以便您可以将Date 称为每一行的Date,并将.$Date 称为整体Date每组内的列):

      sample_df %>% 
          group_by(ID) %>% 
          do(rowwise(.) %>% 
              mutate(PrevEnc90D = sum(Date - .$Date < 90 & Date - .$Date > 0), 
                     PrevEvent90D = sum(.$Event[Date - .$Date < 90 & Date - .$Date > 0])))
      
      #Source: local data frame [12 x 5]
      #Groups: ID [2]
      
      #         Date     ID Event PrevEnc90D PrevEvent90D
      #       <date> <fctr> <dbl>      <int>        <dbl>
      #1  2016-09-09      A     1          0            0
      #2  2016-05-07      A     0          3            2
      #3  2016-05-06      A     1          2            1
      #4  2016-02-13      A     0          2            2
      #5  2016-02-11      A     1          1            1
      #6  2016-01-07      A     1          0            0
      #7  2016-11-08      B     0          0            0
      #8  2016-06-08      B     1          1            1
      #9  2016-05-08      B     1          1            1
      #10 2016-02-13      B     1          2            1
      #11 2016-01-03      B     0          1            1
      #12 2016-01-01      B     1          0            0
      

      【讨论】:

      • 我很欣赏这个解决方案,很快就会试用!我应该补充一点,我确实有一个非常小的数据集(
      【解决方案3】:

      一个相当冗长的 dplyr 解决方案,它使用了比实际需要更多的行。这个想法是为每个日期创建一个完全连接的表,然后使用窗口函数。如果需要不同的窗口计算,这可能很有用。

      library(dplyr)
      
      dates <- data.frame(Date = seq(from = -90 + min(sample_df$Date), to = max(sample_df$Date), by=1)) 
      extended_df <- data.frame(ID = unique(sample_df$ID)) %>%
        merge(dates) %>% 
        left_join(sample_df, by=(c("ID", "Date"))) %>% 
        arrange(ID, desc(Date)) %>%
        mutate(Encounter = as.integer(!is.na(Event)),
               Event = ifelse(is.na(Event), 0, Event)) %>%
        group_by(ID) %>%
        mutate(PrevEnc90D   = rollsum(lead(Encounter), k=90, fill=0, align="left"),
              PrevEvent90D  = rollsum(lead(Event),     k=90, fill=0, align="left")) %>%
        inner_join(sample_df[,c("ID", "Date")]) %>%
        arrange(ID, desc(Date))
      
      extended_df
      

      来源:本地数据框 [12 x 6] 组:ID [2]

             ID       Date Event Encounter PrevEnc90D PrevEvent90D
         <fctr>     <date> <dbl>     <int>      <dbl>        <dbl>
      1       A 2016-09-09     1         1          0            0
      2       A 2016-05-07     0         1          3            2
      3       A 2016-05-06     1         1          2            1
      4       A 2016-02-13     0         1          2            2
      5       A 2016-02-11     1         1          1            1
      6       A 2016-01-07     1         1          0            0
      7       B 2016-11-08     0         1          0            0
      8       B 2016-06-08     1         1          1            1
      9       B 2016-05-08     1         1          1            1
      10      B 2016-02-13     1         1          2            1
      11      B 2016-01-03     0         1          1            1
      12      B 2016-01-01     1         1          0            0
      

      【讨论】:

        【解决方案4】:

        另一个想法是尽可能避免重复求和和关系运算:

        do.call(rbind, 
                lapply(split(sample_df, sample_df$ID), 
                       function(x) {
                           i = nrow(x) - findInterval(x$Date - 90, rev(x$Date))
                           cs = cumsum(x$Event)
                           cbind(x, PrevEnc90D = i - (1:nrow(x)), PrevEvent90D = cs[i] - cs)
                       }))
        #           Date ID Event PrevEnc90D PrevEvent90D
        #A.1  2016-09-09  A     1          0            0
        #A.2  2016-05-07  A     0          3            2
        #A.3  2016-05-06  A     1          2            1
        #A.4  2016-02-13  A     0          2            2
        #A.5  2016-02-11  A     1          1            1
        #A.6  2016-01-07  A     1          0            0
        #B.7  2016-11-08  B     0          0            0
        #B.8  2016-06-08  B     1          1            1
        #B.9  2016-05-08  B     1          1            1
        #B.10 2016-02-13  B     1          2            1
        #B.11 2016-01-03  B     0          1            1
        #B.12 2016-01-01  B     1          0            0
        

        以上假设“日期”在每个“ID”中的顺序是递减的(如果不是这种情况,这很简单)。这里的主要思想是 (i) 定位每个日期的前 90 天,(ii) 预先计算一次累积和,(iii) 减去相应的索引/cumsums 以获得输出。我在这里使用split/lapply 路由按“ID”分组,但我想,它很容易转移到任何首选工具。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-06-16
          • 1970-01-01
          • 2020-11-26
          • 1970-01-01
          • 2019-07-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多