Warum ist "[" besser als "Teilmenge"?

400

Wenn ich einen data.frame filtern muss, dh Zeilen extrahieren muss, die bestimmte Bedingungen erfüllen, bevorzuge ich die subsetFunktion:

subset(airquality, Month == 8 & Temp > 90)

Anstelle der [Funktion:

airquality[airquality$Month == 8 & airquality$Temp > 90, ]

Es gibt zwei Hauptgründe für meine Präferenz:

  1. Ich finde, der Code liest sich besser, von links nach rechts. Sogar Leute, die nichts über R wissen, könnten sagen, was die subsetobige Aussage tut.

  2. Da Spalten im selectAusdruck als Variablen bezeichnet werden können, kann ich einige Tastenanschläge speichern. In meinem obigen Beispiel musste ich nur airqualityeinmal mit subset, aber dreimal mit tippen [.

Also lebte ich glücklich, benutzte es subsetüberall, weil es kürzer ist und besser liest, und befürwortete sogar seine Schönheit meinen R-Programmierkollegen. Aber gestern ist meine Welt auseinandergebrochen. Beim Lesen der subsetDokumentation bemerke ich diesen Abschnitt:

Warnung

Dies ist eine praktische Funktion zur interaktiven Verwendung. Für die Programmierung ist es besser, die Standard-Teilmengenfunktionen wie [zu verwenden, und insbesondere die nicht standardmäßige Bewertung der Argument-Teilmenge kann unerwartete Konsequenzen haben.

Könnte jemand helfen zu klären, was die Autoren meinen?

Was verstehen sie unter " interaktiv nutzen "? Ich weiß, was eine interaktive Sitzung ist, im Gegensatz zu einem Skript, das im BATCH-Modus ausgeführt wird, aber ich sehe nicht, welchen Unterschied es machen sollte.

Könnten Sie dann bitte " die nicht standardmäßige Bewertung der Argumentuntermenge " erklären und warum dies gefährlich ist, vielleicht ein Beispiel geben?

flodel
quelle
14
Es ist etwas weniger (aber Nuss weniger als Teilmenge) zu verwenden,with(airquality, airquality[Month == 8 & Temp > 90, ])
Tyler Rinker
7
Vielleicht sehen Sie sich auch Cirlces 8.2.31 und 8.2.32 von 'The R Inferno' burns-stat.com/pages/Tutor/R_inferno.pdf an
Patrick Burns
9
Versuchen Sie stattdessen data.table. Die Standardsyntax lautet wie Luftqualität [Monat == 8 & Temp> 90,] - sehr gut lesbar und viel schneller.
Stian Håklev
3
OK. Also, wenn die Verwendung einer Teilmenge schlecht ist - was ist mit [vs. dplyr :: filter ()?
BenutzerJT
4
Für diejenigen, die sich fragen, dplyr::filterhat das gleiche Problem. Das heißt, wenn die Umgebung zufällig eine Variable mit diesem Namen hat, wird sie anstelle der Variablen im Datenrahmen verwendet. Verwechselt das Debuggen!
Deleet

Antworten:

241

Diese Frage wurde in den Kommentaren von @James gut beantwortet und wies auf eine ausgezeichnete Erklärung von Hadley Wickham für die Gefahren subset(und Funktionen wie diese) [hier] hin . Geh und lies es!

Es ist eine etwas lange Lektüre, daher kann es hilfreich sein, hier das Beispiel aufzuzeichnen, das Hadley verwendet und das die Frage "Was kann schief gehen?" Am direktesten anspricht:

Hadley schlägt das folgende Beispiel vor: Angenommen, wir möchten einen Datenrahmen mit den folgenden Funktionen unterteilen und dann neu anordnen:

scramble <- function(x) x[sample(nrow(x)), ]

subscramble <- function(x, condition) {
  scramble(subset(x, condition))
}

subscramble(mtcars, cyl == 4)

Dies gibt den Fehler zurück:

Fehler bei der Bewertung (Ausdruck, Umgebung, Beilage): Objekt 'Zyl' nicht gefunden

weil R nicht mehr "weiß", wo sich das Objekt namens "cyl" befindet. Er weist auch auf die wirklich bizarren Dinge hin, die passieren können, wenn es zufällig ein Objekt namens "Zyl" in der globalen Umgebung gibt:

cyl <- 4
subscramble(mtcars, cyl == 4)

cyl <- sample(10, 100, rep = T)
subscramble(mtcars, cyl == 4)

(Führen Sie sie aus und überzeugen Sie sich selbst, es ist ziemlich verrückt.)

joran
quelle
2
Darf ich einige Neulinge zur Klärung haben? Wenn wir schreiben subset(mtcars, cyl == 4)(auf oberster Ebene), wo sucht R nach Zyl? Wenn es in das mtcarsObjekt schaut , an das übergeben wird subset(), sollte es dann nicht finden können, cylselbst wenn scramblees sich in einer anderen Funktion befindet, da mtcarses immer noch an das Objekt übergeben wird ? Wenn meine Frage keinen Sinn ergibt, können Sie einfach näher erläutern, warum R nicht mehr finden kann cyl. Vielen Dank!
Heisenberg
4
@Anh Inside subset.data.frame, das, was wir an diesem Punkt zu bewerten versuchen, ist einfach condition. Das gibt es in nicht mtcars. So subset.data.frameverwendet , enclos = parent.frame()um sicherzustellen , dass conditionkorrekt als ausgewertet wird cyl == 4. Aber dann sind wir wieder zum umschließenden Rahmen zurückgekehrt, und jetzt, wo R danach sucht, schaut cyles nicht mehr hinein mtcars. Wenn wir nicht verwenden würden enclos, würde so etwas subset(mtcars,cyl == a)überhaupt nicht funktionieren.
Joran
Weiß jemand, warum subset () nicht einfach die schnellere und sicherere [,] Methode hinter den Kulissen implementiert?
Björks Nummer eins Fan
1
@ MikePalmice Das tut es. Die letzte Zeile von subset.data.frameist x[r, vars, drop = drop]. Das Problem ist, wie man von den nicht zitierten subsetund selectArgumenten zu etwas gelangt, an das man gültig übergeben kann [.data.frame.
Joran
@joran hat es verstanden, danke. Wie denkst du darüber, ob du den Filter von dplyr anstelle von verwenden sollst []?
Björks Nummer eins Fan
30

Ist auch [schneller:

require(microbenchmark)        
microbenchmark(subset(airquality, Month == 8 & Temp > 90),airquality[airquality$Month == 8 & airquality$Temp > 90,])
    Unit: microseconds
                                                           expr     min       lq   median       uq     max neval
                     subset(airquality, Month == 8 & Temp > 90) 301.994 312.1565 317.3600 349.4170 500.903   100
     airquality[airquality$Month == 8 & airquality$Temp > 90, ] 234.807 239.3125 244.2715 271.7885 340.058   100
Bartektartanus
quelle
36
Ja und nein. Ich denke, der Zeitunterschied, den Sie sehen, ist auf zwei Dinge zurückzuführen. 1) ein kleiner Overhead (<100 Mikrosekunden) und 2) im subsetGegensatz zu [Zeilen, in denen der Filter ausgewertet wird NA. Wenn Sie dies tun, werden Sie sehen, dass beide im Vergleich "fair" genauso schnell sind:x <- do.call(rbind, rep(list(airquality), 100)); microbenchmark(subset(x, Month == 8 & Temp > 90),{ i <- x$Month == 8 & x$Temp > 90; x[!is.na(i) & i ,] })
Flodel