从时间序列数据创建间隔

Creating intervals from time series data

我有一个用户和访问时间的数据框。访问可以重复。 我正在尝试创建一个 用户列表 并根据给定的 时间间隔 命名为 ,例如年.

timestamp user
1  2013-03-06 01:00:00    1
2  2014-07-06 21:00:00    1
3  2014-07-31 23:00:00    2
4  2014-08-09 17:00:00    2
5  2014-08-14 20:00:00    2
6  2014-08-14 22:00:00    3
7  2014-08-16 15:00:00    3
8  2014-08-19 02:00:00    1
9  2014-12-28 18:00:00    1
10 2015-01-17 17:00:00    1
11 2015-01-22 22:00:00    2
12 2015-01-22 22:00:00    3
13 2015-03-23 15:00:00    4
14 2015-04-05 18:00:00    1
15 2015-04-06 01:00:00    2 

我的代码示例 已经创建了一个 按年份 分组的用户列表。 我的问题是我需要在这种方法中 修改 table,这成为我的 table 的问题 百万条目.

test <- structure(list(timestamp = c("2013-03-06 01:00:00", "2014-07-06 21:00:00", 
                                 "2014-07-31 23:00:00", "2014-08-09 17:00:00", "2014-08-14 20:00:00", 
                                 "2014-08-14 22:00:00", "2014-08-16 15:00:00", "2014-08-19 02:00:00", 
                                 "2014-12-28 18:00:00", "2015-01-17 17:00:00", "2015-01-22 22:00:00", 
                                 "2015-01-22 22:00:00", "2015-03-23 15:00:00", "2015-04-05 18:00:00", 
                                 "2015-04-06 01:00:00"), user = c(1L, 1L, 2L, 2L, 2L, 3L, 3L, 
                                                                  1L, 1L, 1L, 2L, 3L, 4L, 1L, 2L)), .Names = c("timestamp", "user"
                                                                  ), class = "data.frame", row.names = c(NA, -15L))

require(lubridate)
#Creating "POSIXct" object from string timestamp
timestamp <- lapply(test$timestamp,
                function(x)parse_date_time(x, "y-m-d H:M:S"))
test$timestamp <- do.call(c,timestamp)
print(class(test$timestamp))

#Adding column for year
test <- cbind(test,sapply(timestamp, function(x)year(x)))
colnames(test)[3]<- "year"

#Creating list of year time intervals for users
intervals <- names(table(test$year))
users <- lapply(intervals, function(x)test[test$year %in% x,"user"])
names(users) <- intervals

没有时间戳

timestamp 视为一个字符。仅当每个时间戳的前 4 位数字代表年份时才有效。

library(dplyr)
test %>%
  group_by( user, substr(timestamp,1,4 ) ) %>%
  summarise( )

#    user `substr(timestamp, 1, 4)`
#   <int> <chr>                    
# 1     1 2013                     
# 2     1 2014                     
# 3     1 2015                     
# 4     2 2014                     
# 5     2 2015                     
# 6     3 2014                     
# 7     3 2015                     
# 8     4 2015

dplyr + lubridate

将从时间戳中提取年份

library( dplyr )
library( lubridate )
test %>%
  mutate( timestamp = as.POSIXct( timestamp, format = "%Y-%m-%d %H:%M:%S" ) ) %>%
  group_by( user, lubridate::year( timestamp ) ) %>%
  summarise( )

# # Groups:   user [?]
#    user `year(timestamp)`
#   <int>             <dbl>
# 1     1              2013
# 2     1              2014
# 3     1              2015
# 4     2              2014
# 5     2              2015
# 6     3              2014
# 7     3              2015
# 8     4              2015

table

频率table也很快

table( test$user, substr( test$timestamp, 1, 4 ) )

#   2013 2014 2015
# 1    1    3    2
# 2    0    3    2
# 3    0    2    1
# 4    0    0    1

还有更多选择...选择一个

编辑

如果速度有问题,ty data.table

dcast( 
  setDT( test )[, timestamp :=  as.POSIXct( timestamp, format = "%Y-%m-%d %H:%M:%S" )][, .N, by = list( user, data.table::year(timestamp) )],
  user ~ data.table,
  value.var = "N")

#    user 2013 2014 2015
# 1:    1    1    3    2
# 2:    2   NA    3    2
# 3:    3   NA    2    1
# 4:    4   NA   NA    1

您还可以使用基本(统计)函数 aggregate(),如下所示:

aggregate( x = test$user, 
           by = list(year=substr(test$timestamp,1,4)), 
           FUN = unique ) 

结果:

  year          x
1 2013          1
2 2014    1, 2, 3
3 2015 1, 2, 3, 4

以上假设您的时间戳列最初只是一个字符向量,与您的结构化示例数据中包含的完全相同。在这种情况下,您可以直接用 substr(test$timestamp,1,4) 代替年份,而无需先转换为日期。

但是,如果您已经将时间戳列作为日期,只需替换您在尝试的解决方案中演示的 lubridate::year() 函数即可。

另一个使用闪电般快速 data.table 包的选项:

library(data.table)
setDT(test) # make `test` a data.frame 'by reference' (no copy is made at all)

test[, j=.(users=list(unique(user))),
       by=.(year=substr(test$timestamp,1,4))] 

   year   users
1: 2013       1
2: 2014   1,2,3
3: 2015 1,2,3,4

再次假设您的 test$timestamp 列是字符向量 - 否则根据需要替换 lubridate::year()。

更新:

简单更改以显示分组而不是按月(正如评论中提到的那样):

 test[, j=.(users=list(unique(user))),
        by=.(ym=substr(test$timestamp,1,7))] 

        ym users
1: 2013-03     1
2: 2014-07   1,2
3: 2014-08 2,3,1
4: 2014-12     1
5: 2015-01 1,2,3
6: 2015-03     4
7: 2015-04   1,2

或按天分组,以帮助演示如何使用链接进行子集化:

test[, j=.(users=list(unique(user))),
       by=.(ymd=substr(test$timestamp,1,11))][ymd>='2014-08-01' & ymd<= '2014-08-21']

           ymd users
1: 2014-08-09      2
2: 2014-08-14    2,3
3: 2014-08-16      3
4: 2014-08-19      1

filtering/subsetting 的注意事项,如果您只对 "one off" 计算的日期子集感兴趣(而不是为了其他目的保存整个聚合集),它可能会在 DT[i, j, by]i 中进行子集计算 "one off" 会更有效率。