【问题标题】:Count event occurrences per client based on multiple reference dates根据多个参考日期计算每个客户的事件发生次数
【发布时间】:2016-10-07 16:35:22
【问题描述】:

我是 R 的新手,我有一个包含客户编号和数千个事件日期的数据集。数据格式如下:

data <- data.frame("Client"=c(rep(1, 4), rep(2, 3), rep(3, 2)), "Date"=as.Date(c("2015-11-20", "2015-12-04", "2016-01-08", "2016-04-07", "2015-12-19", "2016-02-02", "2016-02-21", "2016-01-04", "2016-02-12")), "Event"=rep(1, 9))
data
  Client       Date Event
1      1 2015-11-20     1
2      1 2015-12-04     1
3      1 2016-01-08     1
4      1 2016-04-07     1
5      2 2015-12-19     1
6      2 2016-02-02     1
7      2 2016-02-21     1
8      3 2016-01-04     1
9      3 2016-02-12     1

给定一组参考日期,

 refdates <- as.Date(c("2016-01-01", "2016-03-01"))

我想计算每个客户发生的事件数 (1) 参考日期后 30 天,(2) 参考日期前 0-30 天,以及 (3) 参考日期前 31-60 天对于参考日期集。

我希望输出是如下所示的数据框:

  Client    RefDate post30 prior30 prior31.60
1      1 2016-01-01      1       1          1
2      1 2016-03-01      0       0          1
3      2 2016-01-01      0       1          0
4      2 2016-03-01      0       2          0
5      3 2016-01-01      1       0          0
6      3 2016-03-01      0       1          1

我觉得我应该能够使用 plyr 来做到这一点,但我感觉有点过头了。请有人指出我正确的方向吗?

【问题讨论】:

    标签: r date aggregate


    【解决方案1】:

    我在示例中使用了 dplyr。你说它只有几千行,所以只要参考日期的数量不太大,这不应该是计算量太大。

    require(dplyr)
    
    data <- data.frame("Client"=c(rep(1, 4), rep(2, 3), rep(3, 2)), "Date"=as.Date(c("2015-11-20", "2015-12-04", "2016-01-08", "2016-04-07", "2015-12-19", "2016-02-02", "2016-02-21", "2016-01-04", "2016-02-12")), "Event"=rep(1, 9))
    data
    
    refdates <- as.Date(c("2016-01-01", "2016-03-01"))
    
    data %>%
      merge(refdates, all = T) %>%
      rename(RefDate = y) %>%
      mutate(
        post30 = ifelse(between(Date - RefDate, 1, 31), 1, 0),
        prior30 = ifelse(between(Date - RefDate, -30, 0), 1, 0),
        prior30.60 = ifelse(between(Date - RefDate, -60, -31), 1, 0)
             ) %>%
       group_by(Client, RefDate) %>%
       summarise(post30 = sum(post30),
                prior30 = sum(prior30),
                prior30.60 = sum(prior30.60)
      )
    

    这产生了:

      Client    RefDate post30 prior30 prior30.60
       (dbl)     (date)  (dbl)   (dbl)      (dbl)
    1      1 2016-01-01      1       1          1
    2      1 2016-03-01      0       0          1
    3      2 2016-01-01      0       1          0
    4      2 2016-03-01      0       2          0
    5      3 2016-01-01      1       0          0
    6      3 2016-03-01      0       1          1
    

    【讨论】:

    • 感谢您的及时回复。虽然我没有完全按照所有步骤进行操作,但我期待深入了解更多信息!
    • 没问题!如果我可以帮助解释任何事情,请告诉我!
    【解决方案2】:

    使用dplyr

    library(dplyr)
    out <- data %>%
      merge(refdates) %>%
      rename(RefDate = y) %>%
      group_by(Client, RefDate) %>%
      mutate(Date.diff = Date - RefDate) %>%
      summarise(post30 = sum(Date.diff < 30 & Date.diff > 0),
                prior30 = sum(Date.diff < 0 & Date.diff > -30),
                prior31.60 = sum(Date.diff < -30 & Date.diff > -60))
    
    out
      Client    RefDate post30 prior30 prior31.60
       (dbl)     (date)  (int)   (int)      (int)
    1      1 2016-01-01      1       1          1
    2      1 2016-03-01      0       0          1
    3      2 2016-01-01      0       1          0
    4      2 2016-03-01      0       2          0
    5      3 2016-01-01      1       0          0
    6      3 2016-03-01      0       1          1
    

    【讨论】:

      【解决方案3】:

      这是一个基本的 R 方法。

      do.call(rbind, lapply(refdates, FUN=function(i) {
        aggregate(cbind("post30"=data$Date - i > -1 & data$Date - i < 31,
                        "prior30"=data$Date - i > -31 & data$Date - i < 0, 
                        "prior31.60"=data$Date - i > -61 & data$Date - i < -30),
                  list(data$Client), FUN=sum)
      }))
      

      这里是一个快速分解:

      • aggregate 函数对每个客户在特定参考日期的时间窗口内的逻辑值求和。
      • cbind 允许我们一次计算多个窗口以及在输出中添加名称。
      • lapply 贯穿参考日期并调用aggregate。这会返回我们正在寻找的列表。
      • 最后,do.call 接收这个 data.frames 列表,rbinds 它们创建一个单独的 data.frame。

      【讨论】:

      • 同理,为了避免多重比较,findInterval 可以很方便; do.call(rbind, lapply(refdates, function(d) do.call(rbind, tapply(cut(data$Date, d + c(-60, -30, 0, 30), labels = c("prior31.60", "prior30", "post30")), data$Client, table))))
      • 感谢@alexis_laz 的提示。我知道findInterval,但还没有洞察力/机会来使用它。我去看看。
      • 感谢您的快速回复!这是太棒了。我再次想起 R 语言是多么强大和简洁。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多