data.table。快速计算每列内的更改次数

时间:2016-11-02 15:34:40

标签: r data.table

我想知道每个变量在每个组中的变化次数,然后为所有组添加结果。

我发现了这种方式:

mi[,lapply(.SD, function(x) sum(x != shift(x), 
  na.rm=T) ), by = ID][,-1][,lapply(.SD,sum, na.rm=T)]

它有效,它会产生正确的结果,但在我的大型数据表中它确实很慢。 我想在同一个lapply中做两个操作(或者更快更紧凑的东西),但第一个是由group完成的,第二个不是。

它可以用更简单的方式编写(也许并不总是)

mi[,lapply(.SD, function(x) sum(x != shift(x), 
    na.rm=T) )] [,-1]-mi[,length(unique(ID))]+1

但它仍然很慢,需要大量记忆。

还有其他想法吗?

我也尝试过差异而不是移位,但它变得更加困难。

这里有一个虚拟的例子:

mi <- data.table(ID=rep(1:3,each=4) , year=rep(1:4, times=3),
   VREP=rep(1:3,each=4) , VDI=rep(1:4, times=3), RAN=sample(12))
mi <- rbind(mi, data.table(4,1,1,1,0), use.names=F)

基准测试的典范

mi <- as.data.table(matrix(sample(0:100,10000000,
 replace=T), nrow=100000, ncol=100))
mi[,ID := rep(1:1000,each=100)]

我的问题是真正的数据集要大得多,它在内存大小的限制内,然后我配置了R以便能够使用页面文件使用更多内存,并且它使许多操作变慢。 我知道我可以分割文件并再次加入它,但有时会使事情变得更加困难,或者某些操作无法拆分。

1 个答案:

答案 0 :(得分:2)

您的第二种方法产生的结果不正确,因此不是一个公平的比较点。这是alexis_laz建议的优化版本:

setorder(mi, ID)
setDT(Map(`!=`, mi, shift(mi)))[,
  lapply(lapply(.SD, `&`, !ID), sum, na.rm = T), .SDcols = -"ID"]
#   year VREP VDI RAN
#1:    9    0   9   9

在更大的样本上:

setorder(mi, ID)
microbenchmark(method1(), alexis_laz(), eddi(), times = 5)
#Unit: milliseconds
#         expr       min        lq      mean    median        uq      max neval
#    method1() 7336.1830 7510.9543 7932.0476 8150.3197 8207.2181 8455.563     5
# alexis_laz() 1350.0338 1492.3793 1509.0790 1492.5426 1577.3318 1633.107     5
#       eddi()  400.3999  475.6908  494.5805  504.6163  524.2077  567.988     5