Dlaczego „[„ lepszy niż „podzbiór”?

400

Kiedy muszę filtrować ramkę data.frame, tzn. Wyodrębnić wiersze spełniające określone warunki, wolę użyć subsetfunkcji:

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

Zamiast [funkcji:

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

Są dwa główne powody mojej preferencji:

  1. Uważam, że kod czyta się lepiej, od lewej do prawej. Nawet ludzie, którzy nie wiedzą nic o R, mogą powiedzieć, co subsetrobi powyższe stwierdzenie.

  2. Ponieważ kolumny w selectwyrażeniu mogą być nazywane zmiennymi , mogę zapisać kilka naciśnięć klawiszy. W powyższym przykładzie musiałem pisać airqualitytylko raz subset, ale trzy razy [.

Tak więc żyłem szczęśliwy, używając subsetwszędzie, ponieważ jest on krótszy i czyta się lepiej, a nawet polecam jego piękno innym znajomym koderom R. Ale wczoraj mój świat się rozpadł. Podczas czytania subsetdokumentacji zauważam ten rozdział:

Ostrzeżenie

Jest to wygodna funkcja przeznaczona do użytku interaktywnego. Do programowania lepiej jest używać standardowych funkcji podzestawu, takich jak [, aw szczególności niestandardowa ocena podzbioru argumentów może mieć nieoczekiwane konsekwencje.

Czy ktoś mógłby pomóc wyjaśnić, co mają na myśli autorzy?

Po pierwsze, co rozumieją przez „ do użytku interaktywnego ”? Wiem, czym jest sesja interaktywna, w przeciwieństwie do skryptu uruchamianego w trybie BATCH, ale nie widzę, jaką różnicę powinna ona robić.

Czy mógłbyś zatem wyjaśnić „ niestandardową ocenę podzbioru argumentów ” i dlaczego jest to niebezpieczne, może podać przykład?

flodel
źródło
14
Jest to nieco mniej (ale mniej orzechów niż podzbiór) do użycia,with(airquality, airquality[Month == 8 & Temp > 90, ])
Tyler Rinker
7
Możesz także rzucić okiem na Cirlces 8.2.31 i 8.2.32 z „The R Inferno” burns-stat.com/pages/Tutor/R_inferno.pdf
Patrick Burns
9
Zamiast tego wypróbuj data.table, domyślna składnia jest podobna do airquality [Month == 8 & Temp> 90,] - bardzo czytelna i znacznie szybsza.
Stian Håklev
3
OK. więc jeśli podzbiór jest zły w użyciu - co z [vs. dplyr :: filter ()?
userJT
4
Dla tych, którzy zastanawiają się, dplyr::filterma ten sam problem. To znaczy, jeśli środowisko ma zmienną o tej nazwie, użyje jej zamiast zmiennej w ramce danych. Powoduje mylące debugowanie!
Deleet

Odpowiedzi:

241

Odpowiedzi na to pytanie udzielono w komentarzach @James, wskazując na doskonałe wyjaśnienie Hadleya Wickhama na temat niebezpieczeństw subset(i podobnych funkcji) [tutaj] . Przeczytaj to!

Jest to nieco długa lektura, więc pomocne może być zanotowanie tutaj przykładu, który wykorzystuje Hadley, który najbardziej bezpośrednio odnosi się do pytania „co może pójść nie tak?

Hadley sugeruje następujący przykład: załóżmy, że chcemy rozdzielić, a następnie zmienić kolejność ramki danych za pomocą następujących funkcji:

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

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

subscramble(mtcars, cyl == 4)

To zwraca błąd:

Błąd w eval (expr, envir, encos): nie znaleziono obiektu „cyl”

ponieważ R już nie „wie”, gdzie znaleźć obiekt o nazwie „cyl”. Wskazuje również na naprawdę dziwaczne rzeczy, które mogą się zdarzyć, jeśli przypadkiem w globalnym środowisku pojawi się obiekt o nazwie „cyl”:

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

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

(Uruchom je i przekonaj się sam, to całkiem szalone.)

Joran
źródło
2
Czy mogę prosić o wyjaśnienia dla początkujących? Kiedy piszemy subset(mtcars, cyl == 4)(na najwyższym poziomie), gdzie R szuka cyl? Jeśli patrzy na mtcarsobiekt, który jest przekazywany subset(), to czy nie powinien być w stanie znaleźć, cylnawet jeśli scramblejest w innej funkcji, ponieważ mtcarsnadal jest do niego przekazywany? Jeśli moje pytanie nie ma sensu, możesz po prostu wyjaśnić, dlaczego R nie może już znaleźć cyl. Dzięki!
Heisenberg
4
@ Anh Inside subset.data.frame, rzeczą, którą próbujemy ocenić w tym momencie, jest właśnie condition. To nie istnieje mtcars. subset.data.frameUżywa więc, enclos = parent.frame()aby upewnić się, że conditionjest poprawnie ocenione jako cyl == 4. Ale potem wróciliśmy do otaczającej ramy i teraz, gdy R szuka, cylnie jest już w środku mtcars. Gdybyśmy nie korzystali enclos, coś takiego w subset(mtcars,cyl == a)ogóle by nie działało.
joran
czy ktoś wie, dlaczego subset () nie wdrożyłby po prostu szybszej i bezpieczniejszej metody [,] za sceną?
Fan Bjorks numer jeden
1
@MikePalmice Tak. Ostatni wiersz subset.data.frameto x[r, vars, drop = drop]. Problem polega na tym, jak przejść od cytatów subseti selectargumentów do czegoś, co można poprawnie przekazać [.data.frame.
joran
@ joran rozumiem, dzięki. jak myślisz o tym, czy zamiast tego użyć filtra dplyr []?
Fan Bjorks numer jeden
30

[Jest także szybszy:

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
źródło
36
Tak i nie. Myślę, że różnica czasu, którą widzisz, wynika z dwóch rzeczy. 1) niewielki (<100 mikrosekund) narzut i 2) w subsetprzeciwieństwie do [usuwa wiersze, w których ocenia filtr NA. Zrób to, a zobaczysz, że oba są równie szybkie w porównaniu z „dość”: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