2012-03-25 16 views
343

Quando ho bisogno di filtrare un data.frame, vale a dire, le righe estratto che soddisfano determinate condizioni, preferisco usare la funzione subset:Perché `` `` meglio di `sottoinsieme`?

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

Piuttosto che la funzione [:

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

Ci sono due ragioni principali per la mia preferenza:

  1. Trovo che il codice si legge meglio, da sinistra a destra. Persino le persone che non sanno nulla di R potrebbero dire cosa sta facendo la dichiarazione subset precedente.

  2. Poiché le colonne possono essere denominate come variabili nell'espressione select, è possibile salvare alcune sequenze di tasti. Nel mio esempio sopra, ho solo dovuto digitare airquality una volta con subset, ma tre volte con [.

Così vivevo felice, utilizzando subset ovunque, perché è più breve e legge meglio, anche sostenendo la sua bellezza ai miei compagni di programmatori R. Ma ieri il mio mondo è andato in pezzi. Durante la lettura della documentazione subset, ho notato questa sezione:

Warning

This is a convenience function intended for use interactively. For programming it is better to use the standard subsetting functions like [, and in particular the non-standard evaluation of argument subset can have unanticipated consequences.

Qualcuno potrebbe aiutare a chiarire che cosa significano gli autori?

In primo luogo, cosa intendono per "per l'uso in modo interattivo"? So che cos'è una sessione interattiva, al contrario di uno script eseguito in modalità BATCH ma non vedo che differenza dovrebbe fare.

Quindi, potresti spiegare "la valutazione non standard del sottoinsieme argomento" e perché è pericoloso, magari fornire un esempio?

+12

E 'un po' meno (ma dado meno di sottoinsieme) da usare con, 'con (airquality, airquality [Mese == 8 & Temp> 90,])' –

+1

Questo thread discute l'avvertimento 'subset()': http://r.789695.n4.nabble.com/Variable-passed-to-function-not-used-in-function-in-select-in-subset- tt872217.html – jthetzel

+3

Potresti anche dare un'occhiata a Cirlces 8.2.31 e 8.2.32 di 'The R Inferno' http://www.burns-stat.com/pages/Tutor/R_inferno.pdf –

risposta

201

Questa domanda ha avuto una buona risposta nei commenti di @James, indicando un'eccellente spiegazione di Hadley Wickham sui pericoli di subset (e funzioni simili) [here]. Vai a leggerlo!

E 'una un po' lunga lettura, quindi potrebbe essere utile per registrare qui l'esempio che Hadley utilizza che si rivolge più direttamente la questione del "cosa può andare storto?":

Hadley suggerisce il seguente esempio: supponiamo di vuole sottoinsieme e poi riordinare un frame di dati utilizzando le seguenti funzioni:

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

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

subscramble(mtcars, cyl == 4) 

restituisce l'errore:

Error in eval(expr, envir, enclos) : object 'cyl' not found

perché R no l onger "sa" dove trovare l'oggetto chiamato "cyl". Egli sottolinea inoltre la roba veramente bizzarro che può accadere se per caso v'è un oggetto chiamato 'cil' nel contesto globale:

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

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

(eseguirli e vedere di persona, è abbastanza pazzo.)

+2

Posso avere alcune domande per principianti per chiarimenti? Quando scriviamo 'sottoinsieme (mtcars, cyl == 4)' (al livello più alto), dove R cerca cyl? Se guarda nell'oggetto 'mtcars' che viene passato a' subset() ', allora non dovrebbe essere in grado di trovare' cyl' anche se 'scramble' si trova all'interno di un'altra funzione, dato che' mtcars' è ancora passato a vero? Se la mia domanda non ha senso, potresti semplicemente approfondire il motivo per cui R non può più trovare 'cyl'. Grazie! – Heisenberg

+3

@Anh All'interno di 'subset.data.frame', la cosa che stiamo cercando di valutare a quel punto è semplicemente' condition'. Questo non esiste in 'mtcars'. Quindi 'subset.data.frame' usa' enclos = parent.frame() 'per assicurare che' condizione' sia correttamente valutata come 'cyl == 4'. Ma poi siamo tornati al frame che lo racchiude, e ora quando R cerca 'cyl' non sta più guardando all'interno di' mtcars'. Se non usassimo 'enclos', qualcosa come' sottoinsieme (mtcars, cyl == a) 'non funzionerebbe affatto. – joran

+0

qualcuno sa perché subset() non implementa il metodo più veloce e più sicuro dietro le quinte? –

20

anche [ è più veloce:

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 
+26

Sì e no. Penso che la differenza di tempo che stai vedendo sia dovuta a due cose. 1) un piccolo overhead (<100 microsecondi) e 2) 'sottoinsieme 'a differenza di' ['rimuove le righe dove il filtro valuta come' NA'. Fai questo e vedrai che sono entrambi veloci quanto in confronto "abbastanza": 'x <- do.call (rbind, rep (list (airquality), 100)); microbenchmark (sottoinsieme (x, Mese == 8 & Temp> 90), {i <- x $ Month == 8 & x $ Temp> 90; x [! is.na (i) & i,]}) ' – flodel