J'ai l'habitude de regrouper des tâches similaires en une seule ligne. Par exemple, si j'ai besoin de filtrer sur a
, b
et c
dans 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?
la source
base
observation similaire avec des vecteurs en procédant comme suit:chain_vec <- function() { x <- which(a < .001); x[which(b[x] > .999)] }
etand_vec <- function() { which(a < .001 & b > .999) }
. (oùa
etb
sont des vecteurs de la même longueurrunif
- j'ai utilisén = 1e7
pour ces coupures).Réponses:
Généralement, la réponse a été donnée dans les commentaires: la "méthode de chaînage"
data.table
est plus rapide dans ce cas que la "méthode de anding" car le chaînage exécute les conditions les unes après les autres. Comme chaque étape réduit la taille de la,data.table
il y a moins à évaluer pour la suivante. "Anding" évalue à chaque fois les conditions pour les données en taille réelle.Nous pouvons le démontrer avec un exemple: lorsque les étapes individuelles ne diminuent PAS la taille de la
data.table
(c'est-à-dire que les conditions à vérifier sont les mêmes pour les deux approches):En utilisant les mêmes données mais le
bench
package, qui vérifie automatiquement si les résultats sont identiques:Comme vous pouvez le voir ici, l' approche anding est 2,43 fois plus rapide dans ce cas . Cela signifie que l' enchaînement ajoute en fait des frais généraux , ce qui suggère que l'anding devrait être plus rapide. SAUF si les conditions réduisent la taille de l'
data.table
étape par étape. Théoriquement, l'approche de chaînage pourrait même être plus lente (même en laissant de côté les frais généraux), à savoir si une condition augmenterait la taille des données. Mais pratiquement je pense que ce n'est pas possible car le recyclage des vecteurs logiques n'est pas autorisédata.table
. Je pense que cela répond à votre question bonus.A titre de comparaison, des fonctions originales sur ma machine avec
bench
:la source