【问题标题】:R dplyr aggregate count or amount within time interval in minsR dplyr 时间间隔内的聚合计数或数量(以分钟为单位)
【发布时间】:2018-06-26 19:37:08
【问题描述】:

我有一个由 date_time、account(都是字符)和金额(数字)组成的数据集,如下所示:

sample data:
    date_time <- as.character(c('2018-01-22 18:18:00','2018-01-22 18:18:05','2018-01-22 18:18:19','2018-01-22 18:18:00','2018-01-22 18:30:12','2018-01-22 18:18:11'))
    account <- as.character(c('a0001','a0001','a0001','b0001','b0001','c0001'))
    amount <- c(1000,200,300,10000,400,10000)
    df.sample <- data.frame(date_time, account, amount)

如果每个账户在 1 分钟内的总计数 >= 2 且总金额 >= 12000 的交易,我想返回 TRUE,否则返回 FALSE。

我用 dplyr 写了一个函数如下:

simulation <- function(df, v.acct, v.date.time) {

  # v.acct <- '5408044133161021'
  # v.date.time <- as.POSIXct('2018-01-22 18:18:11')
  #time.interval <- 120

  #subset
  df2 <- df %>% 
          mutate(date.time=as.POSIXct(date_time),
                 diff.time=difftime(v.date.time, date.time, units=c('mins'))) %>%
          filter(account %in% v.acct,  diff.time <= time.interval, diff.time > 0) 

  df.summary <- df2 %>% 
                  group_by(account) %>%
                  summarise(agg.cnt=n(),
                            agg.amt=sum(amount))

  nrow <- df.summary %>% filter(agg.cnt>=agg.count, agg.amt>=agg.amount) %>% nrow()

  result <- ifelse(nrow==0, FALSE, TRUE)

  return(result)

}

将返回包含 TRUE 或 FALSE 的向量:

time.interval <- 10
agg.count <- 10
agg.amount <- 20000
v.result <- apply(df[,c(1,2)],1,function(x) simulation(x[2],x[1]))

问题: 上面的代码能够返回结果,而如果数据集超过 90,000 次观察,计算时间会很长。有没有替代方法?谢谢

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    假设 OP 不介意 data.table 解决方案,您可以使用非 equi 自连接来查找每个事务 1 分钟内的实例(by=.EACHI 告诉 data.table 为每一行执行连接i=df 中的数据。请参阅 ?data.table 以了解 i.EACHI 的含义)。

    然后检查count是否大于等于agg.count,总金额是否大于等于agg.amount

    数据:

    date_time <- as.character(c('2018-01-22 18:18:00','2018-01-22 18:18:05','2018-01-22 18:18:19','2018-01-22 18:18:00','2018-01-22 18:30:12','2018-01-22 18:18:11'))
    account <- c('a0001','a0001','a0001','b0001','b0001','c0001')
    amount <- c(1000,200,300,10000,400,10000)
    df <- data.frame(date_time, account, amount)
    
    time.interval <- 60
    agg.count <- 10
    agg.amount <- 20000
    

    代码:

    library(data.table)
    setDT(df)
    df[, date_time := as.POSIXct(date_time, format="%Y-%m-%d %H:%M:%S")]
    df[, oneMinLater := date_time + time.interval]
    df[, hit :=
        df[df, 
        .N >= agg.count & sum(amount, na.rm=TRUE) >= agg.amount,
        by=.EACHI, 
        on=.(account, date_time > date_time, date_time <= oneMinLater)]$V1
    ]
    

    输出:

                 date_time account amount         oneMinLater   hit
    1: 2018-01-22 18:18:00   a0001   1000 2018-01-22 18:19:00 FALSE
    2: 2018-01-22 18:18:05   a0001    200 2018-01-22 18:19:05 FALSE
    3: 2018-01-22 18:18:19   a0001    300 2018-01-22 18:19:19 FALSE
    4: 2018-01-22 18:18:00   b0001  10000 2018-01-22 18:19:00 FALSE
    5: 2018-01-22 18:30:12   b0001    400 2018-01-22 18:31:12 FALSE
    6: 2018-01-22 18:18:11   c0001  10000 2018-01-22 18:19:11 FALSE
    

    【讨论】:

    • 感谢您的及时回复@chinsoon12。如果我想将新列 V1 命名为命中怎么办?我怎样才能做到这一点?
    • 或将 v.result 作为列附加到 df 中,名称为“hit”
    • 谢谢! @chinsoon 第一次知道=.EACHI。在可读性方面,我总觉得 data.table 比 dplyr 相当复杂。
    • 还有一个问题,如果我想在 hit==FALSE 条件下计算总计数和总金额。我怎样才能做到这一点?
    • 你的意思是上述输出的另一个聚合吗?
    【解决方案2】:

    这可能是一个可能的解决方案:

       library(data.table)
        library(lubridate)
        library(zoo)
        setDT(df)
        df[, date.time := as.POSIXct(date_time, format="%Y-%m-%d %H:%M:%S")]
        df[, time.diff := difftime(date.time,min(date.time), units='mins')+0.0001, by=account]
        df[, interval := ceiling(time.diff / dminutes(time.interval)), by=account]
        df[, agg.cnt:=seq_len(.N), by=.(account, interval)]
        df[, agg.amt2:=cumsum(amount), by=.(account, interval)]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-05-12
      • 2014-10-05
      • 2021-07-18
      • 2021-11-11
      • 2019-11-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多