data.table:如何在一个键上二进制搜索两个(数字)值:包括示例

时间:2014-10-21 19:16:48

标签: r data.table

示例数据:

library(data.table)
DT <- data.table(a = c(1, 3, 5, 9, 15), 
                 b = c("a", "c", "d", "e", "f"))

我想获得两行a == 3 | a == 9,即

# a b
# 3 c
# 9 e

我知道如果我:DT[, a:=as.character(a)]然后setkey(DT, a)DT[c("3", "9")],我可以得到想要的结果。但是我想知道,如果有其他方法可以进行这种二进制搜索而不事先将a更改为字符?

2 个答案:

答案 0 :(得分:4)

首先,每次执行基于连接/二进制搜索的子集之前,不必转换为字符列。你可以使用J()并传递一个整数/数字/字符/逻辑/ bit64 :: integer64向量,如下所示:

DT[J(vec1, vec2, ...)]

其中,vec1将与第一个键列匹配,vec2与第二个键列匹配,依此类推。

为方便起见,您不必为字符类型添加J()这一事实是附加功能。因为整数/数字/逻辑向量已经具有这样的含义 - DT[1]将返回第一行,所以我们无法为这些类型提供相同的快捷方式。希望这能回答你原来的问题。

回到您的问题,使用值a对列(3,9)进行子集化,您可以使用data.table基于二进制搜索的子集来执行此操作:

require(data.table)
setkey(DT, a)
DT[J(c(3,9))] ## alternatively DT[.(c(3,9))] in 1.9.4+
#    a b
# 1: 3 c
# 2: 9 e

要使用data.table的二分搜索功能 fast 子集,有两点需要注意:

  • 它要求您对整个数据进行排序(可能并不总是如此)。
  • 在非常大的数据集上,重新安排内存中数据的时间可能非常耗时(查找订单本身通常比移动数据便宜得多)。

为了解决这些问题并提供更好的功能,data.table通过引入一个新的实验性功能 - 借助二级密钥<自动索引来解决1.9.4中的这个问题/ em>的。马特在1.9.4中实现了这一点。

自动索引的作用是,如果辅助密钥尚不存在,则在第一次运行data.table可理解(此时)的表达式时,将创建辅助密钥。它只使用data.table的快速基数排序来存储此列的顺序,并将其存储为属性。与setkey不同,根本没有重新排序数据。您还可以使用set2key()设置辅助密钥。

第一次运行时,所花费的时间等于a)辅助密钥的时间(通常非常小),以及b)查询的时间。从第二次开始,这只是查询的时间,而且使用二进制搜索很快。

如果使用data.table现在可以理解的表达式查询另一列,那么它还会在第一次运行时另外为该列设置一个辅助键。等等...

两种方法之间的速度应该没有(明显的)差异(一旦setkeyset2key完成)。见下面的例子。

辅助密钥的概念可以扩展到自动索引之外,也可以扩展到连接。这将加速data.table进一步加入。


这是一个例子。我将使用1.9.5,因为Matt已经修复了自动索引中的一些错误。

require(data.table) ## 1.9.5+
set.seed(45L)
DT = data.table(x=sample(1e3, 5e7, TRUE))[, y := x]
setkey(DT, x)
set2key(DT, y)

请注意,setkey(.) DT将通过引用重新排序。但set2key只会设置一个属性,因此您的数据不会根据y的顺序重新排序。

列x和y是相同的(有意)。让我们测试两者:

system.time(DT[J(100L)])   ## on column x, 0.003 seconds
system.time(DT[y == 100L]) ## on column y, 0.003 seconds, uses secondary keys

identical(DT[J(100L)], DT[y==100L]) # [1] TRUE

矢量扫描需要多长时间?

options(datatable.auto.index = FALSE)
system.time(DT[y == 100L]) ## 0.428 seconds

答案 1 :(得分:1)

您不需要将其转换为字符向量(尽管整数会更有意义)

 DT <- data.table(a = c(1, 3, 5, 9, 15), b = c("a", "c", "d", "e", "f"))
 setkey(DT, a)
 DT[J(c(3, 9))]

此外,如果您在CRAN中拥有最新版本,则第二次使用in i将自动使用二进制搜索