J'ai l'habitude de regrouper des tâches similaires en une seule ligne. Par exemple, si j'ai besoin de filtrer sur a, bet cdans un tableau de données, je vais les regrouper en un []avec des AND. Hier, j'ai remarqué que dans mon cas particulier, c'était des filtres de chaînage incroyablement lents et testés à la place. J'ai inclus un exemple ci-dessous.
Tout d'abord, j'amorce le générateur de nombres aléatoires, charge data.table et crée un ensemble de données factices.
# 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))
Ensuite, je définis mes méthodes. Les premières chaînes d'approche filtrent ensemble. Le second ET combine les filtres.
# 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)]
}
Ici, je vérifie qu'ils donnent les mêmes résultats.
# Check both give same result
identical(chain_filter(), and_filter())
#> [1] TRUE
Enfin, je les compare.
# 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
Créé le 2019-10-25 par le package reprex (v0.3.0)
Dans ce cas, le chaînage réduit le temps d'exécution d'environ 70%. pourquoi est-ce le cas? Je veux dire, que se passe-t-il sous le capot dans le tableau de données? Je n'ai vu aucun avertissement contre l'utilisation &, j'ai donc été surpris que la différence soit si grande. Dans les deux cas, ils évaluent les mêmes conditions, donc cela ne devrait pas faire de différence. Dans le cas ET, &est un opérateur rapide et il n'a alors qu'à filtrer la table de données une fois (c'est-à-dire en utilisant le vecteur logique résultant des ET), par opposition au filtrage trois fois dans le cas de chaînage.
Question bonus
Ce principe est-il valable pour les opérations sur les tableaux de données en général? La modularisation des tâches est-elle toujours une meilleure stratégie?
baseobservation similaire avec des vecteurs en procédant comme suit: chain_vec <- function() { x <- which(a < .001); x[which(b[x] > .999)] }et and_vec <- function() { which(a < .001 & b > .999) }. (où aet bsont des vecteurs de la même longueur runif- j'ai utilisé n = 1e7pour ces coupures).