Perché `[` meglio di `sottoinsieme`?


400

Quando devo filtrare un data.frame, ovvero estrarre le righe che soddisfano determinate condizioni, preferisco usare la subsetfunzione:

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

Piuttosto che la [funzione:

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

Ci sono due motivi principali per la mia preferenza:

  1. Trovo che il codice sia migliore, da sinistra a destra. Perfino le persone che non sanno nulla di R potrebbero dire cosa subsetsta facendo la frase sopra.

  2. Poiché le colonne possono essere definite variabili selectnell'espressione, posso salvare alcune sequenze di tasti. Nel mio esempio sopra, ho dovuto scrivere solo airqualityuna volta con subset, ma tre volte con [.

Quindi vivevo felice, usando subsetovunque perché è più corto e legge meglio, sostenendo persino la sua bellezza per i miei colleghi programmatori. Ma ieri il mio mondo si è rotto. Durante la lettura della subsetdocumentazione, noto questa sezione:

avvertimento

Questa è una funzione di praticità destinata all'uso interattivo. Per la programmazione è meglio usare le funzioni di sottoinsieme standard come [, e in particolare la valutazione non standard del sottoinsieme di argomenti può avere conseguenze impreviste.

Qualcuno potrebbe aiutare a chiarire cosa significano gli autori?

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

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


14
È leggermente meno (ma meno di un sottoinsieme) da usare con,with(airquality, airquality[Month == 8 & Temp > 90, ])
Tyler Rinker

7
Puoi anche dare un'occhiata a Cirlces 8.2.31 e 8.2.32 di 'The R Inferno' burns-stat.com/pages/Tutor/R_inferno.pdf
Patrick Burns

9
Prova invece data.table, la sintassi predefinita è come airquality [Month == 8 & Temp> 90,] - molto leggibile e molto più veloce.
Stian Håklev,

3
OK. quindi se il sottoinsieme è male da usare - che dire di [vs. dplyr :: filter ()?
userJT

4
Per quelli che si chiedono, dplyr::filterha lo stesso problema. Vale a dire se l'ambiente ha una variabile con quel nome, la utilizzerà invece della variabile nel frame di dati. Crea confusione debug!
Deleet il

Risposte:


241

A questa domanda è stata data una risposta esaustiva nei commenti di @James, indicando un'eccellente spiegazione di Hadley Wickham sui pericoli di subset(e funziona come tale) [qui] . Vai a leggerlo!

È una lettura un po 'lunga, quindi può essere utile registrare qui l'esempio che Hadley usa che affronta direttamente la domanda "cosa può andare storto?":

Hadley suggerisce il seguente esempio: supponiamo di voler sottoinsieme e quindi riordinare un frame di dati usando le seguenti funzioni:

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

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

subscramble(mtcars, cyl == 4)

Questo restituisce l'errore:

Errore in eval (expr, envir, enclos): oggetto 'cil' non trovato

perché R non "sa" più dove trovare l'oggetto chiamato "cil". Sottolinea inoltre le cose veramente bizzarre che possono accadere se per caso esiste un oggetto chiamato 'cil' nell'ambiente globale:

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

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

(Eseguili e vedi di persona, è piuttosto folle.)


2
Posso avere alcune domande da principiante per chiarimenti? Quando scriviamo subset(mtcars, cyl == 4)(al livello più alto), dove R cerca il cil? Se esamina l' mtcarsoggetto a cui viene passato subset(), allora non dovrebbe essere in grado di trovare cylanche se si scrambletrova all'interno di un'altra funzione, dal momento che gli mtcarsviene ancora passato? Se la mia domanda non ha senso, potresti semplicemente approfondire il motivo per cui R non riesce più a trovare cyl. Grazie!
Heisenberg,

4
@Anh Inside subset.data.frame, la cosa che stiamo cercando di valutare a quel punto è giusta condition. Non esiste mtcars. Quindi subset.data.frameutilizza enclos = parent.frame()per garantire che conditionsia valutato correttamente come cyl == 4. Ma poi siamo tornati alla cornice chiusa, e ora quando R lo cerca cylnon sta più guardando dentro mtcars. Se non lo usassimo enclos, qualcosa del genere subset(mtcars,cyl == a)non funzionerebbe affatto.
joran,

qualcuno sa perché subset () non dovrebbe semplicemente implementare il metodo [,] più veloce e sicuro dietro le quinte?
Fan numero uno di Bjorks,

1
@MikePalmice Lo fa. L'ultima riga di subset.data.frameè x[r, vars, drop = drop]. Il problema è come passare dal non quotato subsete dagli selectargomenti a qualcosa a cui puoi validamente passare [.data.frame.
joran,

@joran capito, grazie. come pensi se usare il filtro dplyr invece di []?
Fan numero uno di Bjorks il

30

Inoltre [è 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

36
Sì e no. Penso che la differenza di tempo che vedi sia dovuta a due cose. 1) un piccolo overhead (<100 microsecondi) e 2) a subsetdifferenza [rimuove le righe in cui il filtro valuta NA. Fallo e vedrai che sono entrambi veloci se confrontati "abbastanza":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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.