大数据集上的R diff函数

时间:2013-05-03 13:20:03

标签: r dataframe multicore subset

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

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

我使用了这个函数,它适用于小数据集,但是对于大数据集,它确实很慢。我从昨天开始等待,现在还在运行。

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

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

我们如何才能解决这个问题?如果我将数据帧转换为矩阵会更好吗?

2 个答案:

答案 0 :(得分:4)

这是一个使用数据集的示例数据的示例,该数据集最初为1000万行,有100个用户,每个diff 100,000个时间点,然后是1.4亿行,有1,400个用户,所以时间点数相同。这会将时间点转换为列。我应该想象如果你将用户转换为列,它会更快。我使用@Arun 's answer here作为模板。基本上它表明,在一个非常大的桌子上,你可以在单个核心(i7 2.6 GhZ)上进行&lt; 90秒(这是使用可能没有完全失效的代码):

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

答案 1 :(得分:2)

如果您完全避免使用tapply,这会快得多,这相当容易,因为您的tapply调用假设数据已经按user_idDATE排序。

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