Ich habe die Angewohnheit, ähnliche Aufgaben in einer einzigen Zeile zusammenzufassen. Wenn ich zum Beispiel nach und in einer Datentabelle filtern a
muss b
, füge c
ich sie in einer []
mit UNDs zusammen. Gestern habe ich festgestellt, dass dies in meinem speziellen Fall unglaublich langsam war und stattdessen Verkettungsfilter getestet. Ich habe unten ein Beispiel aufgenommen.
Zuerst setze ich den Zufallszahlengenerator, lade data.table und erstelle einen Dummy-Datensatz.
# 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))
Als nächstes definiere ich meine Methoden. Der erste Ansatz kettet Filter zusammen. Das zweite UND setzt die Filter zusammen.
# 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)]
}
Hier überprüfe ich, ob sie die gleichen Ergebnisse liefern.
# Check both give same result
identical(chain_filter(), and_filter())
#> [1] TRUE
Schließlich vergleiche ich sie.
# 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
Erstellt am 25.10.2019 vom reprex-Paket (v0.3.0)
In diesem Fall reduziert die Verkettung die Laufzeit um ca. 70%. Warum ist das so? Ich meine, was ist unter der Haube in der Datentabelle los? Ich habe keine Warnungen vor der Verwendung gesehen &
, daher war ich überrascht, dass der Unterschied so groß ist. In beiden Fällen bewerten sie die gleichen Bedingungen, so dass dies kein Unterschied sein sollte. Im AND-Fall &
handelt es sich um einen Schnelloperator, der die Datentabelle nur einmal filtern muss (dh unter Verwendung des aus den ANDs resultierenden logischen Vektors), im Gegensatz zum dreimaligen Filtern im Verkettungsfall.
Bonus-Frage
Gilt dieses Prinzip für Datentabellenoperationen im Allgemeinen? Ist die Modularisierung von Aufgaben immer eine bessere Strategie?
base
Beobachtung mit Vektoren machen, indem Sie Folgendes tun: chain_vec <- function() { x <- which(a < .001); x[which(b[x] > .999)] }
und and_vec <- function() { which(a < .001 & b > .999) }
. (wo a
und b
sind Vektoren gleicher Länge von runif
- ich habe n = 1e7
für diese Cutoffs verwendet).