【问题标题】:Aggregate rows close in time聚合行及时关闭
【发布时间】:2020-05-03 10:34:52
【问题描述】:

我在 R 中有一个数据集,格式如下:

data = data.table(
id = c(1, 2, 2, 2, 3, 3, 3, 3 ,3, 3),
Start = c("2019-03-01 09:15:36", 
"2019-01-01 08:00:00", "2019-01-01 08:00:10","2019-01-01 08:00:30",
"2019-01-01 08:00:30", "2019-01-01 08:00:40","2019-01-01 08:00:50",
"2019-01-01 08:01:10", "2019-01-01 08:01:20","2019-01-01 08:01:31"
))
data$Start = as.POSIXct(data$Start ,format = "%Y-%m-%d %H:%M:%S")

每一行代表与一个人的交互,每个人由 id 标识(在示例数据集中,我有 3 个人)。根据定义,应合并开始日期之间少于 15 秒的交互(仅计为一次)。这个 15 秒的窗口应该随着每次新的交互而延长(参见第 3 个人的示例结果)。

鉴于此规则,对于data,我想创建一个标识唯一交互的新ID 列。结果应该是:

data$newID 
[1] 1 2 2 3 4 4 4 5 5 5

这在data.table 中应该是可能的,没有低效的for 循环,但我不能让它工作......

【问题讨论】:

    标签: r performance datetime data.table aggregate


    【解决方案1】:

    执行此操作的一种方法是每次值大于 15 时计算差异并更改组

    data[, new := c(NA,diff.difftime(Start))][, new_id := cumsum(c(1, abs(na.omit(new)) >= 15))]
    

    给出,

        id               Start      new new_id
     1:  1 2019-03-01 09:15:36       NA      1
     2:  2 2019-01-01 08:00:00 -5102136      2
     3:  2 2019-01-01 08:00:10       10      2
     4:  2 2019-01-01 08:00:30       20      3
     5:  3 2019-01-01 08:00:00      -30      4
     6:  3 2019-01-01 08:00:10       10      4
     7:  3 2019-01-01 08:00:20       10      4
     8:  3 2019-01-01 08:00:40       20      5
     9:  3 2019-01-01 08:00:50       10      5
    10:  3 2019-01-01 08:01:01       11      5
    

    您可以照常删除任何不需要的列

    【讨论】:

    • 它适用于示例,但如果下一个人的开始时间接近前一个人的最后一个,它将失败。我更改了示例以反映这种情况
    【解决方案2】:

    受@Sotos 回答的启发,我认为正确的解决方案是:

    data$test = c(T,data$ID[-1] != data$ID[-nrow(data)] |
                               diff.difftime(data$Start) > 15)
    

    这会生成一个布尔值,应删除所有 F(尽管这不会创建新 ID,但根据问题,它解决了隐式删除问题)。

    【讨论】:

    • 类似data[, newID := cumsum(id != shift(id, fill=id[1L]) | c(TRUE, diff(Start) > 15L))]
    • @chinsoon12 是的,这将回答最初的问题。
    【解决方案3】:

    我很好奇以下是否适用于您的数据:

    library(data.table)
    
    dt[
      ,
      session := frollapply(Start, 2, function(x) diff(x) >= 15, fill = 1),
      by = 'id'
    ][
      ,
      session := cumsum(session)
    ]
    
    #     id               Start session
    #  1:  1 2019-03-01 09:15:36       1
    #  2:  2 2019-01-01 08:00:00       2
    #  3:  2 2019-01-01 08:00:10       2
    #  4:  2 2019-01-01 08:00:30       3
    #  5:  3 2019-01-01 08:00:00       4
    #  6:  3 2019-01-01 08:00:10       4
    #  7:  3 2019-01-01 08:00:20       4
    #  8:  3 2019-01-01 08:00:40       5
    #  9:  3 2019-01-01 08:00:50       5
    # 10:  3 2019-01-01 08:01:01       5
    

    数据:

    dt = data.table(
      id = c(1, 2, 2, 2, 3, 3, 3, 3 , 3, 3),
      Start = as.POSIXct(
        c(
          "2019-03-01 09:15:36",
          "2019-01-01 08:00:00",
          "2019-01-01 08:00:10",
          "2019-01-01 08:00:30",
          "2019-01-01 08:00:00",
          "2019-01-01 08:00:10",
          "2019-01-01 08:00:20",
          "2019-01-01 08:00:40",
          "2019-01-01 08:00:50",
          "2019-01-01 08:01:01"
        ),
        ,
        format = "%Y-%m-%d %H:%M:%S"
      )
    )
    

    【讨论】:

    • 我现在不能测试它,但是你能用更新的数据试试吗(虽然我认为通过 ``by = id`` 你的答案不会落入相同的像 Sotos 的问题一样。在一个有 10.000.000 行的数据集中再次对我的答案进行基准测试会很有趣
    • @DiogoSantos 似乎可以很好地处理更新的数据。但是我不知道它将如何在 10,000,000 行上执行 - 可能会很慢,这是很多数据。不管怎样,让我知道结果如何。
    猜你喜欢
    • 2021-07-26
    • 2018-01-27
    • 2010-10-18
    • 1970-01-01
    • 2014-03-05
    • 2021-04-16
    • 2013-01-17
    • 1970-01-01
    • 2021-12-02
    相关资源
    最近更新 更多