0

我正在寻求使用 R 在会话中创建和跟踪用户会话和活动的帮助。在较高级别上,我有一列用户 ID 和一列时间戳。

对于每个用户 ID,我想计算时间戳之间的时间差,并使用它根据时间限制值分配会话号。(即用户的第一个时间戳获得的会话编号为 1。如果下一个时间戳小于 30 分钟后,会话编号将保持为 1,否则将递增为 2,依此类推。)

一旦为每个用户操作观察分配了会话编号,我就想对每个会话中的活动进行排序。每个会话的第一个观察的活动编号为 1,会话中的第二个观察的活动编号为 2,依此类推。当会话编号更改(或用户 ID 更改)时,活动编号将重置回 1。

我有以下重现,它可以满足我的需求,但是,此代码在我的完整数据集(140 万行)上非常慢。特别是,调用difftime和 for 循环非常慢。我想知道是否有更好的方法来做到这一点并且对多种想法持开放态度。也许有一种方法可以在不使用 purrr 的 for 循环的情况下做到这一点,或者这对于并行库来说是一个很好的用例。很高兴听到有关如何简化此过程的任何建议。

library(tidyverse)

session_time_limit <- 30

x <- tibble(ID = c("a", "a", "a", "a", "b", "b", "c", "c", "c"),
       Date = c(as.POSIXct("2021-01-25 19:17:12 UTC"), #a1
                as.POSIXct("2021-01-25 19:17:30 UTC"), #a2
                as.POSIXct("2021-01-25 19:57:12 UTC"), #a3
                as.POSIXct("2021-01-25 19:59:12 UTC"), #a4
                as.POSIXct("2021-01-25 20:11:12 UTC"), #b1
                as.POSIXct("2021-01-25 20:42:12 UTC"), #b2
                as.POSIXct("2021-01-25 21:15:42 UTC"), #c1
                as.POSIXct("2021-01-25 21:17:12 UTC"), #c2
                as.POSIXct("2021-01-25 21:20:13 UTC"))) #c3

x <- x %>% 
  arrange(ID, Date) %>% 
  group_by(ID) %>% 
  mutate(tdiff = difftime(Date, lag(Date), units = "mins"),
         session_number = 1,
         activity_within_session = 1)

for(i in seq(2, nrow(x))) {
  if(!is.na(x$tdiff[i]) &
     #     x$ID[i] == x$ID[i-1] &
     x$tdiff[i] > session_time_limit)
  {
    x$session_number[i] = x$session_number[i-1] + 1
  } else if(!is.na(x$tdiff[i]) &
            x$ID[i] == x$ID[i-1] &
            x$tdiff[i] <= session_time_limit)
  {
    x$session_number[i] = x$session_number[i-1]
  }
}

# Activity within Session
for(i in seq(2, nrow(x))) {
  if(!is.na(x$tdiff[i]) &
     #  x$ID[i] == x$ID[i-1] &
     x$session_number[i] == x$session_number[i-1])
  {
    x$activity_within_session[i] = 
      x$activity_within_session[i-1] + 1
  }
}
4

1 回答 1

2

一个选项可能是:

library(tidyverse)
library(lubridate)

session_time_limit <- 30
df <- tibble(ID = c("a", "a", "a", "a", "b", "b", "c", "c", "c"),
             Date = c(as.POSIXct("2021-01-25 19:17:12 UTC"), #a1
                      as.POSIXct("2021-01-25 19:17:30 UTC"), #a2
                      as.POSIXct("2021-01-25 19:57:12 UTC"), #a3
                      as.POSIXct("2021-01-25 19:59:12 UTC"), #a4
                      as.POSIXct("2021-01-25 20:11:12 UTC"), #b1
                      as.POSIXct("2021-01-25 20:42:12 UTC"), #b2
                      as.POSIXct("2021-01-25 21:15:42 UTC"), #c1
                      as.POSIXct("2021-01-25 21:17:12 UTC"), #c2
                      as.POSIXct("2021-01-25 21:20:13 UTC"))) #c3

df %>% 
  group_by(ID) %>%
  mutate(tdiff = replace_na(interval(lag(Date), Date)/dminutes(1), 0),
         session_number = cumsum(tdiff > session_time_limit) + 1) %>% 
  group_by(session_number, .add = T) %>% 
  mutate(activity_within_session = row_number()) %>% 
  ungroup()
#> # A tibble: 9 x 5
#>   ID    Date                tdiff session_number activity_within_session
#>   <chr> <dttm>              <dbl>          <dbl>                   <int>
#> 1 a     2021-01-25 19:17:12  0                 1                       1
#> 2 a     2021-01-25 19:17:30  0.3               1                       2
#> 3 a     2021-01-25 19:57:12 39.7               2                       1
#> 4 a     2021-01-25 19:59:12  2                 2                       2
#> 5 b     2021-01-25 20:11:12  0                 1                       1
#> 6 b     2021-01-25 20:42:12 31                 2                       1
#> 7 c     2021-01-25 21:15:42  0                 1                       1
#> 8 c     2021-01-25 21:17:12  1.5               1                       2
#> 9 c     2021-01-25 21:20:13  3.02              1                       3

reprex 包于 2021-05-26 创建 (v2.0.0 )

数据表

library(data.table)
setDT(df)
df[, tdiff := c(0, diff(as.numeric(Date)) / 60), by = ID
   ][, session_number := cumsum(tdiff > session_time_limit) + 1, by = ID
     ][, activity_within_session := rowid(ID, session_number)][]

或者

library(data.table)
library(magrittr)
df[, tdiff := c(0, diff(as.numeric(Date)) / 60), by = ID] %>% 
  .[, session_number := cumsum(tdiff > session_time_limit) + 1, by = ID] %>% 
  .[, activity_within_session := rowid(ID, session_number)] %>% 
  .[]
    
   ID                Date     tdiff session_number activity_within_session
1:  a 2021-01-25 19:17:12  0.000000              1                       1
2:  a 2021-01-25 19:17:30  0.300000              1                       2
3:  a 2021-01-25 19:57:12 39.700000              2                       1
4:  a 2021-01-25 19:59:12  2.000000              2                       2
5:  b 2021-01-25 20:11:12  0.000000              1                       1
6:  b 2021-01-25 20:42:12 31.000000              2                       1
7:  c 2021-01-25 21:15:42  0.000000              1                       1
8:  c 2021-01-25 21:17:12  1.500000              1                       2
9:  c 2021-01-25 21:20:13  3.016667              1                       3
于 2021-05-26T18:54:14.317 回答