Data.table 在筛选数据表时,链接优于ANDing的性能优势

Data.table 在筛选数据表时,链接优于ANDing的性能优势,data.table,r,Data.table,R,我习惯于将类似的任务组合成一行。例如,如果我需要在数据表中筛选a、b和c,我会将它们与and一起放在一个[]中。昨天,我注意到,在我的特殊情况下,这是难以置信的缓慢和测试链接过滤器代替。我在下面举了一个例子 首先,我给随机数生成器添加种子,加载并创建一个虚拟数据集 #设置RNG种子 种子集(-1) #加载库 库(数据表) #创建数据表 dt expr min lq平均中值uq max #>链式过滤器()25.17734 31.24489 39.44092 37.53919 43.51588 78

我习惯于将类似的任务组合成一行。例如,如果我需要在数据表中筛选
a
b
c
,我会将它们与and一起放在一个
[]
中。昨天,我注意到,在我的特殊情况下,这是难以置信的缓慢和测试链接过滤器代替。我在下面举了一个例子

首先,我给随机数生成器添加种子,加载并创建一个虚拟数据集

#设置RNG种子
种子集(-1)
#加载库
库(数据表)
#创建数据表
dt expr min lq平均中值uq max
#>链式过滤器()25.17734 31.24489 39.44092 37.53919 43.51588 78.12492
#>和_filter()92.6641112.06136 130.92834 127.64009 149.17320 206.61777
#>内瓦尔cld
#>100 a
#>100 b
由(v0.3.0)于2019-10-25创建

在这种情况下,链接将运行时间减少约70%。为什么会这样?我的意思是,在数据表中,引擎盖下面发生了什么?我没有看到任何反对使用
&
的警告,所以我很惊讶差异如此之大。在这两种情况下,他们评估相同的条件,所以这不应该是一个区别。在AND情况下,
&
是一个快速运算符,然后它只需过滤数据表一次(即,使用ANDs产生的逻辑向量),而不是在链式情况下过滤三次

奖金问题
这个原则一般适用于数据表操作吗?模块化任务总是一种更好的策略吗?

大多数情况下,答案在阿莱迪的评论中给出:数据的“链接方法”。在这种情况下,表比“anding方法”更快,因为链接一个接一个地运行条件。由于每一步都减少了
数据表的大小,因此下一步需要评估的数据就更少了。“Anding”每次评估全尺寸数据的条件

我们可以用一个例子来说明这一点:当单个步骤没有减小
数据表的大小时(即,两种方法的检查条件相同):

正如您在这里看到的,在这种情况下,anding方法的速度是的2.43倍。这意味着链接实际上增加了一些开销,这意味着通常的anding应该更快除非条件是逐步减小
数据表的大小。从理论上讲,链接方法甚至可能更慢(即使将开销放在一边),也就是说,如果某个条件会增加数据的大小。但实际上我认为这是不可能的,因为在
data.table
中不允许循环使用逻辑向量。我想这回答了你的奖金问题

为了进行比较,我的机器上带有
工作台的原始功能:

res <- bench::mark(
  chain = chain_filter_original(),
  and = and_filter_original()
)
summary(res)
#> # A tibble: 2 x 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 chain        29.6ms   30.2ms     28.5     79.5MB     7.60
#> 2 and         125.5ms  136.7ms      7.32   228.9MB     7.32
summary(res, relative = TRUE)
#> # A tibble: 2 x 6
#>   expression   min median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <dbl>  <dbl>     <dbl>     <dbl>    <dbl>
#> 1 chain       1      1         3.89      1        1.04
#> 2 and         4.25   4.52      1         2.88     1
res#A tibble:2 x 6
#>表达式最小中位数`itr/sec`mem_alloc`gc/sec`
#>               
#>1条链条29.6ms 30.2ms 28.5 79.5MB 7.60
#>2和125.5毫秒136.7毫秒7.32 228.9 MB 7.32
摘要(res,relative=TRUE)
#>#A tibble:2 x 6
#>表达式最小中位数`itr/sec`mem_alloc`gc/sec`
#>                    
#>1链条1 3.89 1 1.04
#>2及4.25 4.52 1 2.88 1

我也这么认为,我也有同样的想法。根据我的经验,在一般操作中可以观察到链式加速。虽然data.tavle确实对这种情况进行了一些优化(这本身就是一项壮举,与基本R相比是一个巨大的改进!),但一般来说,a&B&C&D将在组合结果和过滤之前评估所有N个逻辑条件。然而,通过链接,第二个、第三个和第四个逻辑调用仅计算n次(在哪里n@MichaelChirico哇!这太奇怪了!我不知道为什么,但是我只是假设它会像C++的短路径一样在MichaelChirico的评论后面继续,你可以通过以下方式来制作一个类似的<代码>基础<代码>矢量观察:<代码> ChanyVEC @ MichaelChirico Ah,我明白了。链中的数据表要小得多,因此可以更快地评估条件并进行筛选?这很有意义。感谢您的见解!
res <- bench::mark(
  chain = chain_filter(),
  and = and_filter()
)
summary(res)
#> # A tibble: 2 x 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 chain         299ms    307ms      3.26     691MB     9.78
#> 2 and           123ms    142ms      7.18     231MB     5.39
summary(res, relative = TRUE)
#> # A tibble: 2 x 6
#>   expression   min median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <dbl>  <dbl>     <dbl>     <dbl>    <dbl>
#> 1 chain       2.43   2.16      1         2.99     1.82
#> 2 and         1      1         2.20      1        1
res <- bench::mark(
  chain = chain_filter_original(),
  and = and_filter_original()
)
summary(res)
#> # A tibble: 2 x 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 chain        29.6ms   30.2ms     28.5     79.5MB     7.60
#> 2 and         125.5ms  136.7ms      7.32   228.9MB     7.32
summary(res, relative = TRUE)
#> # A tibble: 2 x 6
#>   expression   min median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <dbl>  <dbl>     <dbl>     <dbl>    <dbl>
#> 1 chain       1      1         3.89      1        1.04
#> 2 and         4.25   4.52      1         2.88     1