【问题标题】:data.table. Fast way to count number of changes within every column数据表。快速计算每列中的更改次数
【发布时间】:2016-11-02 15:34:40
【问题描述】:

我想知道每个变量在每个组中更改了多少次,然后将结果添加到所有组。

我是这样找到的:

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

它有效,它产生了正确的结果,但在我的大型数据表中它真的很慢。 我想在同一个 lapply 中执行这两个操作(或者更快更紧凑的操作),但是第一个是按组完成的,第二个不是。

可以用更简单的方式编写(可能并非总是如此)

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

但它仍然很慢并且需要大量内存。

还有什么想法吗?

我也尝试过使用 diffs 而不是 shift,但它变得更加困难。

这里有一个虚拟示例:

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 配置为能够使用页面文件使用更多内存,这会使许多操作变慢。 我知道我可以拆分文件并再次加入它,但有时这会使事情变得更加困难或某些操作不可拆分。

【问题讨论】:

  • 除非我遗漏了什么,按“ID”排序后,您可以计算值变化与 id 不变的次数; id = mi$ID[-1] == mi$ID[-nrow(mi)]; sapply(mi, function(x) sum((x[-1] != x[-length(x)]) &amp; id))
  • 很抱歉,您的方法比我的第一种方法慢两倍,比第二种方法慢四倍。
  • 需要基准和适当大的示例(编写为n的函数)...
  • 有了新的 data.table 我的 benhcmkark 是:method1 1.35sec,method2 0.092sec,alexis 0.30sec
  • @skan:从你的两个答案中,我不得不删除[, -1],因为否则我无法让它工作——它似乎与"[.data.table" 的工作方式有关,除非我有一个过时的版本。除此之外,您的“method2”确实更快,但是(至少对于您的大型数据集而言)它似乎没有返回与“method1”和我的相同的结果(返回相同的结果)。

标签: r data.table


【解决方案1】:

您的第二种方法产生的结果不正确,因此不是一个公平的比较点。这是 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

【讨论】:

  • 您能解释一下地图吗?
  • 它基本上和mi != shift(mi)一样,除了它保持列表结构而不是转换为矩阵,这样可以节省很多时间
  • 我试过了,我的数据库快了 60 倍。
  • 您能否解释一下为什么您使用“&amp;, !ID”而我的方法不需要它?
  • @skan 因为你是通过ID 执行的,所以会自动知道它们之间的边界,而这是针对整个列完成的,并通过跟踪ID 的变化来计算边界(与@alexis_laz 相同的想法)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-31
  • 2016-02-21
  • 1970-01-01
相关资源
最近更新 更多