Mam w zwyczaju zlepianie podobnych zadań w jedną linię. Na przykład, jeśli trzeba filtrować a
, b
i c
w tabeli danych, będę je razem w jednym []
z AND. Wczoraj zauważyłem, że w moim konkretnym przypadku było to niezwykle wolne i zamiast tego przetestowałem filtry łańcuchowe. Podałem przykład poniżej.
Najpierw zaszczepiam generator liczb losowych, ładuję tabelę danych i tworzę fikcyjny zestaw danych.
# Set RNG seed
set.seed(-1)
# Load libraries
library(data.table)
# Create data table
dt <- data.table(a = sample(1:1000, 1e7, replace = TRUE),
b = sample(1:1000, 1e7, replace = TRUE),
c = sample(1:1000, 1e7, replace = TRUE),
d = runif(1e7))
Następnie określam swoje metody. Łańcuchy pierwszego podejścia filtrują razem. Drugi AND razem filtruje.
# Chaining method
chain_filter <- function(){
dt[a %between% c(1, 10)
][b %between% c(100, 110)
][c %between% c(750, 760)]
}
# Anding method
and_filter <- function(){
dt[a %between% c(1, 10) & b %between% c(100, 110) & c %between% c(750, 760)]
}
Tutaj sprawdzam, że dają te same wyniki.
# Check both give same result
identical(chain_filter(), and_filter())
#> [1] TRUE
Wreszcie dokonuję ich analizy porównawczej.
# Benchmark
microbenchmark::microbenchmark(chain_filter(), and_filter())
#> Unit: milliseconds
#> expr min lq mean median uq max
#> chain_filter() 25.17734 31.24489 39.44092 37.53919 43.51588 78.12492
#> and_filter() 92.66411 112.06136 130.92834 127.64009 149.17320 206.61777
#> neval cld
#> 100 a
#> 100 b
Utworzono 25.10.2019 przez pakiet reprezentx (v0.3.0)
W takim przypadku tworzenie łańcuchów skraca czas działania o około 70%. Dlaczego tak jest? Mam na myśli, co się dzieje pod maską w tabeli danych? Nie widziałem żadnych ostrzeżeń przed używaniem &
, więc byłem zaskoczony, że różnica jest tak duża. W obu przypadkach oceniają te same warunki, więc nie powinno być różnicy. W przypadku AND &
jest szybkim operatorem, a następnie musi tylko raz przefiltrować tabelę danych (tj. Przy użyciu wektora logicznego wynikającego z AND), a nie trzykrotnie w przypadku łańcucha.
Pytanie bonusowe
Czy zasada ta dotyczy ogólnie operacji na tabelach danych? Czy zadania modularne są zawsze lepszą strategią?
base
obserwacji za pomocą wektorów, wykonując następujące czynności: chain_vec <- function() { x <- which(a < .001); x[which(b[x] > .999)] }
i and_vec <- function() { which(a < .001 & b > .999) }
. (gdzie a
i b
są wektory o tej samej długości od runif
- użyłem n = 1e7
dla tych wartości granicznych).