1

我想在一个非常大的 data.frame 上使用 diff 函数:1.4 亿行和两列。

目标是为每个 user_id 计算两个连续日期活动之间的差距。对于每个用户,第一个活动没有前一个活动,所以我需要一个 NA 值。

我使用了这个函数,它适用于小数据集,但对于大数据集,它真的很慢。我从昨天开始就在等,它还在运行。

df2 <- as.vector(unlist(tapply(df$DATE,df$user_id, FUN=function(x){ return (c(NA,diff(x)))})))

我有很多内存(24GO)和一个 4 核 CPU,但只有一个在工作。

我们该如何管理这个问题?如果我将数据帧转换为矩阵会更好吗?

4

2 回答 2

4

这是一个使用示例数据的示例,该数据集最初有 1000 万行,有 100 个用户,diff每个有 100,000 个时间点,然后是 1.4 亿行,有 1,400 个用户,所以时间点数相同。这会将时间点转置到列。我应该想象,如果您将用户转移到列中,它会更快。我在这里使用@Arun 的答案作为模板。基本上它表明,在一张非常大的桌子上,您可以在 < 90 秒内在单核 (i7 2.6 GhZ) 上完成此操作(并且使用可能未完全优化的代码):

require(data.table)

## Smaller sample dataset - 10 million row, 100 users, 100,000 time points each
DT <- data.table( Date = sample(100,1e7,repl=TRUE) , User = rep(1:100,each=1e5) )

## Size of table in memory
tables()
#    NAME       NROW MB COLS      KEY
#[1,] DT   10,000,000 77 Date,User    
#Total: 77MB


## Diff by user
dt.test <- quote({
    DT2 <- DT[ , list(Diff=diff(c(0,Date))) , by=list(User) ]
    DT2 <- DT2[, as.list(setattr(Diff, 'names', 1:length(Diff))) , by = list(User)]
    })


## Benchmark it
require(microbenchmark)
microbenchmark( eval(dt.test) , times = 5L )
#Unit: seconds
#         expr      min       lq median       uq      max neval
# eval(dt.test) 5.788364 5.825788 5.9295 5.942959 6.109157     5

## And with 140 million rows...
DT <- data.table( Date = sample(100,1.4e8,repl=TRUE) , User = rep(1:1400,each=1e5) )
#tables()
#    NAME        NROW   MB
#[1,] DT   140,000,000 1069

microbenchmark( eval(dt.test) , times = 1L )
#Unit: seconds
#         expr     min      lq  median      uq     max neval
# eval(dt.test) 84.3689 84.3689 84.3689 84.3689 84.3689     1
于 2013-05-03T14:13:42.480 回答
2

如果您tapply一起避免,这会快得多,这相当容易,因为您的tapply调用假定数据已经按user_idand排序DATE

set.seed(21)
N <- 1e6
Data <- data.frame(DATE=Sys.Date()-sample(365,N,TRUE),
                   USER=sample(1e3,N,TRUE))
Data <- Data[order(Data$USER,Data$DATE),]
system.time({
  Data$DIFF <- unlist(tapply(Data$DATE,Data$USER, function(x) c(NA,diff(x))))
})
#   user  system elapsed 
#   1.58    0.00    1.59
Data2 <- Data
system.time({
  Data2$DIFF <- c(NA,diff(Data2$DATE))
  is.na(Data2$DIFF) <- which(c(NA,diff(Data2$USER))==1)
})
#   user  system elapsed 
#   0.12    0.00    0.12
identical(Data,Data2)
# [1] TRUE
于 2013-05-03T14:19:07.457 回答