在 data.table 中使用 adply

Using adply in data.table

我有一个大 data.table 看起来像:

dt<-data.table(start=c("2012-07-13 23:45:00", "2012-07-14 15:30:00", 
                       "2012-07-14 23:57:00"), 
               end=c("2012-07-14 00:02:00", "2012-07-14 15:35:00", 
                     "2012-07-15 00:05:00"), id=c(1,2,1),cat=c("a","b","a"))
dt
                 start                 end id cat
1: 2012-07-13 23:45:00 2012-07-14 00:02:00  1   a
2: 2012-07-14 15:30:00 2012-07-14 15:35:00  2   b
3: 2012-07-14 23:57:00 2012-07-15 00:05:00  1   a

我需要获得一个输出,该输出按 ID 和类别显示每个日历日的事件总分钟数。使用上面的示例,输出应该是:

          day id cat V1
1: 13.07.2012  1   a 15
2: 14.07.2012  1   a  5
3: 14.07.2012  2   b  5
4: 15.07.2012  1   a  5

我使用 plyr 包中的 adply 函数按分钟间隔划分持续时间:

fn<-function(x){ 
  s<-seq(from = as.POSIXct(x$start), 
         to = as.POSIXct(x$end)-1,by = "mins")
# here s is a sequence of all minutes in the given interval
  df<-data.table(x$id,x$cat,s)

# return new data.table that contains each calendar minute for each id 
# and categoryy of the original data
  df
}
# run the function above for each row in the data.table
dd<-adply(dt,1,fn)

# extract the date from calendar minutes
dd[,day:=format(as.POSIXct(s,"%d.%m.%Y %H:%M%:%S"), "%d.%m.%Y")]

#calculate sum of all minutes of event for each day, id and category
dd[,.N,by=c("day","id","cat")][order(day,id,cat)]

上面的解决方案非常适合我的需求,除了计算时间。当adply是运行在一个非常大的数据和fn函数中定义的几个类别时,感觉永远是CPU运行s。

对于如何在此问题中使用纯 data.table 功能的任何提示,我将不胜感激。

试试看这是否更快。 它仍然是 data.table 在后台,但我正在为该过程使用 dplyr 语法。

library(data.table)

dt<-data.table(start=c("2012-07-13 23:45:00", "2012-07-14 15:30:00", 
                       "2012-07-14 23:57:00"), 
               end=c("2012-07-14 00:02:00", "2012-07-14 15:35:00", 
                     "2012-07-15 00:05:00"), id=c(1,2,1),cat=c("a","b","a"))

fn<-function(x){ 
  s<-seq(from = as.POSIXct(x$start), 
         to = as.POSIXct(x$end)-1,by = "mins")
  # here s is a sequence of all minutes in the given interval
  df<-data.table(x$id,x$cat,s)

  # return new data.table that contains each calendar minute for each id 
  # and categoryy of the original data
  df
}


library(dplyr)

dt %>%
  rowwise() %>%                        # for each row
  do(fn(.)) %>%                        # apply your function
  select(day=s, id=V1, cat=V2) %>%     # rename columns
  mutate(day = substr(day,1,10)) %>%   # keep only the day
  ungroup %>%
  group_by(day,id,cat) %>%         
  summarise(N=n()) %>%
  ungroup


# Source: local data frame [4 x 4]
# 
#           day    id   cat     N
#         (chr) (dbl) (chr) (int)
# 1 2012-07-13     1     a    15
# 2 2012-07-14     1     a     5
# 3 2012-07-14     2     b     5
# 4 2012-07-15     1     a     5

我会提出一些建议

  1. 仅转换为 as.POSIXct 一次,而不是每一行。
  2. 而不是在每次迭代中创建一个整体 data.tableadply,只需在 data.table 范围内使用 by
  3. 为此,使用 .I
  4. 简单地创建一个行索引

这是一个快速尝试(我使用了 substr,因为它可能比 as.Dateas.POSIXct 更快。如果您希望它是 Date class 再次对结果使用 res[, Date := as.IDate(Date)] 而不是按组进行)。

dt[, `:=`(start = as.POSIXct(start), end = as.POSIXct(end), indx = .I)]
dt[, seq(start, end - 1L, by = "mins"), by = .(indx, id, cat)
   ][, .N, by = .(Date = substr(V1, 1L, 10L), id, cat)]
#          Date id cat  N
# 1: 2012-07-13  1   a 15
# 2: 2012-07-14  1   a  5
# 3: 2012-07-14  2   b  5
# 4: 2012-07-15  1   a  5