R中的data.table-使用多个键的多个筛选器-二进制搜索

R中的data.table-使用多个键的多个筛选器-二进制搜索,r,data.table,R,Data.table,我不明白如何根据data.table中的多个键进行筛选。以内置的mtcars数据集为例 DT <- data.table(mtcars) setkey(DT, am, gear, carb) 并给出了正确的结果。此外,如果我想要am==1&gear==4&(carb==4 | carb==2),这同样有效 > DT[.(1, 4, c(4, 2))] mpg cyl disp hp drat wt qsec vs am gear carb 1: 21.0 6

我不明白如何根据
data.table
中的多个键进行筛选。以内置的
mtcars
数据集为例

DT <- data.table(mtcars)
setkey(DT, am, gear, carb)
并给出了正确的结果。此外,如果我想要
am==1&gear==4&(carb==4 | carb==2)
,这同样有效

> DT[.(1, 4, c(4, 2))]
    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
1: 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
2: 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
3: 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
4: 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2
然而,当我想要有
am==1&(gear==3 | gear==4)和(carb==4 | carb==2)
时,似乎是合理的

> DT[.(1, c(3, 4), c(4, 2))]
    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
1:   NA  NA    NA  NA   NA    NA    NA NA  1    3    4
2: 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
3: 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2

失败了。您能解释一下什么是正确的方法吗?

您没有从查询中得到错误的原因是data.table将在值是其他值的倍数时重用这些值。换句话说,由于
am
1
可以使用2次,因此它可以在不告诉您的情况下执行此操作。如果您要执行一个查询,其中允许的值的数量不是彼此的倍数,那么它会给您一个警告。比如说

DT[.(c(1,0),c(5,4,3),c(8,6,4))]
将向您发出警告,抱怨1项的剩余部分,这与键入
data.table(c(1,0)、c(5,4,3)、c(8,6,4))
时看到的错误相同。无论何时合并
X[Y]
,都应将
X
Y
视为数据表

如果改用
CJ

DT[CJ(c(1,0),c(5,4,3),c(8,6,4))]
然后,它将为您和data生成所有值的每个组合。table将给出您期望的结果

从小插曲(粗体是我的):

这里发生了什么事?请再读一遍。为项目提供的值 第二个键列“MIA”必须在dest key中找到匹配的vlaues 第一个键列原点提供的匹配行上的列。 我们以前不能跳过键列的值。因此,我们提供 来自键列原点的所有唯一值“MIA”自动关闭 回收利用,以符合unique(origin)的长度3。

为了完整起见,矢量扫描语法将在不使用
CJ

DT[am == 1 & gear == 4 & carb == 4]


如何知道是否需要二进制搜索?如果子集设置的速度无法忍受,则需要进行二进制搜索。例如,我正在玩一个48M行的data.table,二元搜索和向量之间的差异是惊人的。具体来说,矢量扫描需要1.490秒的时间,而二进制搜索只需要0.001秒。当然,这假设我已经为data.table设置了键。如果我包括设置关键点所需的时间,那么设置关键点和执行子集的组合为1.628。因此,您必须选择毒药

您没有从查询中得到错误的原因是data.table将在值是其他值的倍数时重用这些值。换句话说,由于
am
1
可以使用2次,因此它可以在不告诉您的情况下执行此操作。如果您要执行一个查询,其中允许的值的数量不是彼此的倍数,那么它会给您一个警告。比如说

DT[.(c(1,0),c(5,4,3),c(8,6,4))]
将向您发出警告,抱怨1项的剩余部分,这与键入
data.table(c(1,0)、c(5,4,3)、c(8,6,4))
时看到的错误相同。无论何时合并
X[Y]
,都应将
X
Y
视为数据表

如果改用
CJ

DT[CJ(c(1,0),c(5,4,3),c(8,6,4))]
然后,它将为您和data生成所有值的每个组合。table将给出您期望的结果

从小插曲(粗体是我的):

这里发生了什么事?请再读一遍。为项目提供的值 第二个键列“MIA”必须在dest key中找到匹配的vlaues 第一个键列原点提供的匹配行上的列。 我们以前不能跳过键列的值。因此,我们提供 来自键列原点的所有唯一值“MIA”自动关闭 回收利用,以符合unique(origin)的长度3。

为了完整起见,矢量扫描语法将在不使用
CJ

DT[am == 1 & gear == 4 & carb == 4]


如何知道是否需要二进制搜索?如果子集设置的速度无法忍受,则需要进行二进制搜索。例如,我正在玩一个48M行的data.table,二元搜索和向量之间的差异是惊人的。具体来说,矢量扫描需要1.490秒的时间,而二进制搜索只需要0.001秒。当然,这假设我已经为data.table设置了键。如果我包括设置关键点所需的时间,那么设置关键点和执行子集的组合为1.628。所以你必须选择你的毒药

这个问题现在已经成为重复问题的目标,我觉得现有的答案可以改进,以帮助新手
data.table
用户

1.
DT[.()]
DT[CJ()]
之间有什么区别? 根据
?data.table
()
list()
的别名,作为参数
i
提供的
list
在内部转换为
data.table
。因此,
DT[(1,c(3,4),c(2,4))]
相当于
DT[数据表(1,c(3,4),c(2,4))]

data.table(1, c(3, 4), c(2, 4))
#   V1 V2 V3
#1:  1  3  2
#2:  1  4  4
data.table
由两行组成,这两行是最长向量的长度<代码>1被回收

这与创建所提供向量的所有组合的交叉连接不同

CJ(1, c(3, 4), c(2, 4))
   V1 V2 V3
#1:  1  3  2
#2:  1  3  4
#3:  1  4  2
#4:  1  4  4
请注意,
setDT(expand.grid())
将产生相同的结果

这解释了OP为什么会得到两种不同的结果:

DT[.(1, c(3, 4), c(2, 4))]
#   mpg cyl disp  hp drat    wt  qsec vs am gear carb
#1:  NA  NA   NA  NA   NA    NA    NA NA  1    3    2
#2:  21   6  160 110  3.9 2.620 16.46  0  1    4    4
#3:  21   6  160 110  3.9 2.875 17.02  0  1    4    4

DT[CJ(1, c(3, 4), c(2, 4))]
#    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
#1:   NA  NA    NA  NA   NA    NA    NA NA  1    3    2
#2:   NA  NA    NA  NA   NA    NA    NA NA  1    3    4
#3: 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
#4: 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2
#5: 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
#6: 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
请注意,参数
nomatch=0
将删除不匹配的行,即包含
NA
的行

2.在% 除了
CJ()
am==1&(gear==3 | gear==4)和(carb==2 | carb==4)
之外,还有使用值匹配的第三个等效选项:

DT[am == 1 & gear %in%  c(3, 4) & carb %in% c(2, 4)]
#    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
#1: 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
#2: 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2
#3: 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
#4: 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
请注意,
CJ()
要求在两个
result <- microbenchmark::microbenchmark(
  setkey = {
    DT_keyed <- copy(DT)
    setkeyv(DT_keyed, c("am", "gear", "carb"))},
  cj_keyed = {
    DT_keyed <- copy(DT)
    setkeyv(DT_keyed, c("am", "gear", "carb")) 
    DT_keyed[CJ(1, c(3, 4), c(2, 4)), nomatch = 0]},
  or_keyed = {
    DT_keyed <- copy(DT)
    setkeyv(DT_keyed, c("am", "gear", "carb")) 
    DT_keyed[am == 1 & (gear == 3 | gear == 4) & (carb == 2 | carb == 4)]},
  or_unkey = {
    copy = DT_unkey <- copy(DT)
    DT_unkey[am == 1 & (gear == 3 | gear == 4) & (carb == 2 | carb == 4)]},
  in_keyed =  {
    DT_keyed <- copy(DT)
    setkeyv(DT_keyed, c("am", "gear", "carb")) 
    DT_keyed[am %in% c(1) & gear %in%  c(3, 4) & carb %in% c(2, 4)]},
  in_unkey = {
    copy = DT_unkey <- copy(DT)
    DT_unkey[am %in% c(1) & gear %in%  c(3, 4) & carb %in% c(2, 4)]},
  times = 10L)
print(result)
#Unit: milliseconds
#     expr       min        lq     mean    median       uq      max neval
#   setkey 198.23972 198.80760 209.0392 203.47035 213.7455 245.8931    10
# cj_keyed 210.03574 212.46850 227.6808 216.00190 254.0678 259.5231    10
# or_keyed 244.47532 251.45227 296.7229 287.66158 291.3811 404.8678    10
# or_unkey  69.78046  75.61220 103.6113  89.32464 111.5240 231.6814    10
# in_keyed 269.82501 270.81692 302.3453 274.42716 321.2935 431.9619    10
# in_unkey  93.75537  95.86832 119.4371 100.19446 126.6605 251.4172    10

ggplot2::autoplot(result)