【问题标题】:Grouping based on multiple variables, including time-series data基于多个变量的分组,包括时间序列数据
【发布时间】:2019-05-14 15:36:52
【问题描述】:

我有一个数据框,列出了在应用程序上执行的每个“事件”(用于操作的术语)。每个事件都有一个用户 ID、时间戳(格式:'hms' num)和日期戳(格式:日期)等变量,但这些是与我的问题相关的唯一变量。我正在尝试对每个用户在 30 分钟内发生的事件进行分组。例如,用户 123 的 8 行事件将被分组为 2 行,代表应用程序上的不同活动会话。

我尝试使用 group_by 函数,但不知道如何考虑特定时间段。

我目前没有任何代码可以作为我尝试过的示例。

以下是我的数据框的示例:

user ID         Event Name         Date         Time

23              Press              01/01/2019   10:20:52
23              Read               01/01/2019   10:21:43
23              Click              01/01/2019   10:27:21
23              Press              01/01/2019   10:28:05
87              Read               01/01/2019   11:42:51
87              Press              01/01/2019   12:16:02
87              Read               01/01/2019   12:17:49
23              Click              01/01/2019   15:42:51
23              Click              01/01/2019   15:43:45
23              Press              01/01/2019   15:45:12
64              Read               01/01/2019   18:01:33
64              Click              01/01/2019   18:02:26
64              Click              01/01/2019   18:02:58
64              Read               01/01/2019   18:04:19
64              Press              01/01/2019   18:10:47

在这个例子中,我只展示了一天的几个事件。但是数据框有数千行,所有这些信息涵盖了大约 5 个月。

理想情况下,我试图让最终结果如下所示:

user ID         Event Name         Date         Time

23              Session            01/01/2019   10:20:52
87              Session            01/01/2019   11:42:51
87              Session            01/01/2019   12:16:02
23              Session            01/01/2019   15:42:51
64              Session            01/01/2019   18:01:33

因此,基本上在 30 分钟内发生的多行事件已被压缩为 1 行,其中事件名称已重命名为会话。任何帮助都将不胜感激,因为我仍然是 R 的新手,而且这个问题似乎比我迄今为止所习惯的要先进一些。

【问题讨论】:

  • “在 30 分钟内”是指最后一个事件距离第一个事件

标签: r time-series grouping


【解决方案1】:

我会采用迭代方法。下面的方法将处理会话(简单的英语定义)以某种方式持续超过 30 分钟的情况。您需要在前 30 个之后创建一个切点,然后让下一个立即开始一个新的“会话”(您的定义)。如果不以这种方式迭代,我想不出该怎么做。


从您的示例数据开始:

library(tidyverse)
library(lubridate)

events <- tribble(
~`user ID`, ~`Event Name`,        ~Date,      ~Time,
        23,       "Press", "01/01/2019", "10:20:52",
        23,       "Read" , "01/01/2019", "10:21:43",
        23,       "Click", "01/01/2019", "10:27:21",
        23,       "Press", "01/01/2019", "10:28:05",
        87,       "Read" , "01/01/2019", "11:42:51",
        87,       "Press", "01/01/2019", "12:16:02",
        87,       "Read" , "01/01/2019", "12:17:49",
        23,       "Click", "01/01/2019", "15:42:51",
        23,       "Click", "01/01/2019", "15:43:45",
        23,       "Press", "01/01/2019", "15:45:12",
        64,       "Read" , "01/01/2019", "18:01:33",
        64,       "Click", "01/01/2019", "18:02:26",
        64,       "Click", "01/01/2019", "18:02:58",
        64,       "Read" , "01/01/2019", "18:04:19",
        64,       "Press", "01/01/2019", "18:10:47"
)

然后添加参考行 ID 和有用的日期时间字段:

events <- events %>% 
  mutate(
    event_id = row_number(),
    date_time = mdy_hms(paste(Date, Time))
  )

现在我们制作第一个会话表,为每个用户获取第一个会话:

sessions <- events %>% 
  group_by(`user ID`) %>% 
  summarise(session_start = min(date_time)) %>% 
  mutate(session_end = session_start + minutes(30))

然后我们迭代!

  1. left_join() 查找哪些事件已在已知会话中。
  2. anti_join() 告诉我们哪些记录不在该已知匹配表中。
  3. 如果没有任何这样的未知数,你就完了!
  4. 如果有,获取这些会话,并将它们添加到 sessions 表中。
while(TRUE) {
  in_a_known_session <- events %>% 
    left_join(sessions, by = "user ID") %>% 
    filter(date_time >= session_start & date_time < session_end)
  unassigned <- events %>% 
    anti_join(in_a_known_session, by = "event_id")
  if (nrow(unassigned) == 0) {
    break
  }
  sessions <- sessions %>% 
    bind_rows(
      unassigned %>% 
        group_by(`user ID`) %>% 
        summarise(session_start = min(date_time)) %>% 
        mutate(session_end = session_start + minutes(30))
    )
}

最后,以您在示例中要查找的形式获得它:

sessions <- sessions %>% 
  arrange(session_start) %>% 
  mutate(
    `Event Name` = "Session",
    Date = format(session_start, "%m/%d/%Y"),
    Time = format(session_start, "%H:%M:%S")
  ) %>% 
  select(-starts_with("session_"))

如果这对您有用,并且您接受它,这将是我第一次接受 StackOverflow 答案! :D

【讨论】:

  • 您提供了非常详尽的答案。我建议您使用您在评论中提供的其他信息来编辑答案,而不是评论答案。您的评论包含有关您的答案上下文的关键信息,因此它应该包含在答案中。
  • @Benjamin 非常感谢您在这里的帮助,您的解决方案对我来说非常有效。真的很感激
猜你喜欢
  • 2017-08-02
  • 2021-11-20
  • 2017-09-14
  • 1970-01-01
  • 1970-01-01
  • 2020-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多