示例数据:
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
更改为字符?
答案 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现在可以理解的表达式查询另一列,那么它还会在第一次运行时另外为该列设置一个辅助键。等等...
两种方法之间的速度应该没有(明显的)差异(一旦setkey
和set2key
完成)。见下面的例子。
辅助密钥的概念可以扩展到自动索引之外,也可以扩展到连接。这将加速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将自动使用二进制搜索