数据表索引(setkey)中列的顺序是否重要?

3

我有一个关于索引 data.table 对象的问题。

setkey(data, A, B)
data[, C:=length(unique(B, na.rm=T)), by=A]

我在想是否应该更改索引的顺序为

setkey(data, B, A)

提高速度。或者它们是相同的吗?如何为其建立索引?
data[c>=3, D:=sum(A), by=B]

4
你可以使用library(microbenchmark)来测试它。在devel版本中,你可以使用uniqueN代替length(unique - akrun
1
您可以使用microbenchmark包自行测试。但据我所知,对于这种类型的操作,关键字并不重要;它用于使用i参数进行快速选择。 - Nick Kennedy
1
我使用了 micorbenchmark 进行测试,但在一个简单的函数和小数据上得到了混合的结果。因此,我想问问作者或其他可能注意到差异的人。 - JeanVuda
3
对于小数据,基准测试没什么用。你需要创建一个大数据集(大约 1e6 行或更多)并进行测试。 - akrun
2
@MichaelChirico unique 函数也没有 na.rm=TRUE 参数。unique(c('x', 'x', NA), na.rm=TRUE)# [1] "x" NA - akrun
显示剩余2条评论
1个回答

2

首先,你的length(unique(B, na.rm = T))代码并不像你想的那样工作 - na.rm = TRUE不是unique函数的一个参数,而是被传递给...并被忽略(感谢@akrun指出这一点)。也许最好的方法是运行uniqueN(na.omit(B))来得到你想要的结果。

考虑到这一点,我进行了9个(=3x3)基准测试,比较了你建议的代码的速度,变换键的顺序:(B,A)(A,B)或没有键(X)。例如,下面所提到的BAX函数是:

BAX <- function(){
  data <- data.table(A = sample(50, size = 1e6, T),
                     B = sample(c(1:150000, NA), size = 1e6, T))
  setkey(data, B, A)
  data[ , C := uniqueN(na.omit(B)), by = A]
  data[C >= 18500, D := sum(A), by = B]
}

这是每种排列方式重复200次的结果:
> microbenchmark(times = 200L,
                 XX(), XAB(), XBA(), ABX(), ABAB(),
                 ABBA(), BAX(), BAAB(), BABA())
Unit: milliseconds
   expr       min        lq     mean    median       uq      max neval    cld
   XX()  70.05867  73.66665 105.2628  96.55443 116.5883 213.2926   200 a     
  XAB() 112.52981 121.91760 161.2687 157.66455 172.6626 370.4791   200     ef
  XBA() 112.56648 122.65417 165.9513 158.96873 174.6038 406.3392   200      f
  ABX()  79.59582  82.33355 110.8462 101.04939 125.0158 198.1082   200 a     
 ABAB()  83.81686  90.40803 123.1391 126.94853 132.0878 182.0694   200  b    
 ABBA() 112.50687 117.68602 151.8467 155.72603 161.2123 228.5776   200    de 
  BAX()  85.82144  93.87965 134.5259 130.40824 146.1559 263.9083   200  bc   
 BAAB() 100.48214 105.35192 150.9692 146.76173 156.0230 392.4626   200    de 
 BABA()  93.29706 104.70251 142.8426 138.12848 149.1106 279.4645   200   cd  

从这个简单的例子中,你最好的选择是:不对表进行键排序(似乎从预排序中获得了最小的收益),或者首先按(A,B)进行键排序并保留它。
反过来,首先按(B,A)进行键排序并保留它也表现得相当不错。鉴于此,我实际上对XBA的表现感到非常惊讶。
如果你想知道为什么没有进行键排序就可以如此快速,那么基本上所有的键排序都是为了你要做的事情而进行的预排序;这只会在给定操作中稍微提高一些速度,但在操作之间重新进行键排序是一种成本。按照手册的说法,这是一个“键控的”与一个“临时”的比较:

by包含x的键的前n列时,我们称之为“键控的”。在“键控的”中,组在RAM中连续出现,并且内部批量复制内存,以获得额外的速度。否则,我们称之为“临时的”。临时的仍然比tapply等操作快很多倍,但是当数据集非常大时,特别是每个组的大小很大时,它不如键控的快。不要与下面定义的keyby混淆。

键排序的真正速度优势在于子集和合并,对于像你这样的操作,我发现“临时”的已经完全可以满足需求了。

感谢您详细的回答。我有点惊讶,这些操作不需要使用任何索引。除了 length(unique(B))-any(is.na(B)),如果我们使用 length(unique(na.omit(B))) 呢?在我看来,这可能需要一些严肃的计算。我非常感谢您详细的回答。谢谢! - JeanVuda
1
我在这一点上添加了手册中的引用,以增加清晰度。 - MichaelChirico
1
na.omit确实是更好的选择。 - MichaelChirico

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接