Mengapa `[` lebih baik dari `subset`?

400

Ketika saya perlu memfilter data.frame, yaitu, mengekstrak baris yang memenuhi kondisi tertentu, saya lebih suka menggunakan subsetfungsi:

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

Alih-alih [fungsi:

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

Ada dua alasan utama untuk preferensi saya:

  1. Saya menemukan kode membaca lebih baik, dari kiri ke kanan. Bahkan orang yang tidak tahu apa-apa tentang R bisa mengatakan apa yang dilakukan subsetpernyataan di atas.

  2. Karena kolom dapat disebut sebagai variabel dalam selectekspresi, saya dapat menyimpan beberapa penekanan tombol. Dalam contoh saya di atas, saya hanya perlu mengetik airqualitysatu kali subset, tetapi tiga kali dengan [.

Jadi saya hidup bahagia, menggunakan subset mana mana karena lebih pendek dan membaca lebih baik, bahkan menganjurkan keindahannya kepada sesama R coders saya. Tapi kemarin duniaku pecah. Saat membaca subsetdokumentasi, saya perhatikan bagian ini:

Peringatan

Ini adalah fungsi kenyamanan yang dimaksudkan untuk digunakan secara interaktif. Untuk pemrograman, lebih baik menggunakan fungsi standar subsetting seperti [, dan khususnya evaluasi subset argumen non-standar dapat memiliki konsekuensi yang tidak terduga.

Bisakah seseorang membantu menjelaskan apa yang dimaksud penulis?

Pertama, apa yang mereka maksud dengan " untuk digunakan secara interaktif "? Saya tahu apa sesi interaktif itu, sebagai lawan dari skrip dijalankan dalam mode BATCH tapi saya tidak melihat perbedaan apa yang harus dibuat.

Lalu, bisakah Anda menjelaskan " evaluasi non-standar dari subset argumen " dan mengapa itu berbahaya, mungkin memberikan contoh?

flodel
sumber
14
Ini sedikit kurang (tapi kurang dari himpunan bagian) untuk digunakan dengan,with(airquality, airquality[Month == 8 & Temp > 90, ])
Tyler Rinker
7
Anda mungkin juga melihat Cirlces 8.2.31 dan 8.2.32 dari 'The R Inferno' burns-stat.com/pages/Tutor/R_inferno.pdf
Patrick Burns
9
Coba data.table, sintaks standarnya seperti airquality [Month == 8 & Temp> 90,] - sangat mudah dibaca, dan jauh lebih cepat.
Stian Håklev
3
BAIK. jadi jika subset buruk untuk digunakan - bagaimana dengan [vs. dplyr :: filter ()?
userJT
4
Bagi yang penasaran, dplyr::filtermemiliki masalah yang sama. Yaitu jika lingkungan kebetulan memiliki variabel dengan nama itu, ia akan menggunakannya sebagai ganti variabel dalam bingkai data. Membuat debugging membingungkan!
Menghapus

Jawaban:

241

Pertanyaan ini dijawab dengan baik di komentar oleh @James, menunjuk pada penjelasan yang sangat baik dari Hadley Wickham tentang bahaya subset(dan fungsi-fungsi seperti itu) [di sini] . Baca itu!

Ini agak lama dibaca, jadi mungkin berguna untuk mencatat di sini contoh yang digunakan Hadley yang paling langsung menjawab pertanyaan "apa yang bisa salah?":

Hadley menyarankan contoh berikut: misalkan kita ingin mengatur ulang dan kemudian menyusun ulang bingkai data menggunakan fungsi berikut:

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

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

subscramble(mtcars, cyl == 4)

Ini mengembalikan kesalahan:

Kesalahan dalam eval (expr, envir, enclos): objek 'cyl' tidak ditemukan

karena R tidak lagi "tahu" di mana menemukan objek yang disebut 'cyl'. Dia juga menunjukkan hal-hal aneh yang dapat terjadi jika secara kebetulan ada objek yang disebut 'cyl' di lingkungan global:

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

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

(Jalankan mereka dan lihat sendiri, ini cukup gila.)

joran
sumber
2
Bolehkah saya memiliki beberapa pertanyaan pemula untuk klarifikasi? Ketika kita menulis subset(mtcars, cyl == 4)(di tingkat atas), di mana R mencari cyl? Jika melihat ke mtcarsobjek yang diteruskan ke subset(), maka bukankah seharusnya ia dapat menemukan cylbahkan jika scrambleberada dalam fungsi lain, karena mtcarsmasih diteruskan ke sana? Jika pertanyaan saya tidak masuk akal, Anda bisa menguraikan lebih lanjut mengapa R tidak bisa lagi ditemukan cyl. Terima kasih!
Heisenberg
4
@ Anh Di dalam subset.data.frame, hal yang kami coba evaluasi pada saat itu adalah adil condition. Itu tidak ada di mtcars. Jadi subset.data.framedigunakan enclos = parent.frame()untuk memastikan bahwa conditiondievaluasi dengan benar sebagai cyl == 4. Tapi kemudian kita telah muncul kembali ke bingkai terlampir, dan sekarang ketika R mencari cylitu tidak lagi melihat ke dalam mtcars. Jika kami tidak menggunakan enclos, sesuatu seperti subset(mtcars,cyl == a)tidak akan berfungsi sama sekali.
joran
apakah ada yang tahu mengapa subset () tidak hanya mengimplementasikan metode [,] yang lebih cepat dan lebih aman di belakang layar?
Penggemar nomor satu Bjorks
1
@MikePalmice Tidak. Baris terakhir subset.data.frameadalah x[r, vars, drop = drop]. Masalahnya adalah bagaimana mendapatkan dari tanda kutip subsetdan selectargumen untuk sesuatu yang dapat Anda sampaikan secara sah [.data.frame.
joran
@ joran mengerti, terima kasih. bagaimana menurut Anda apakah akan menggunakan filter dplyr bukan []?
Penggemar nomor satu Bjorks
30

Juga [lebih cepat:

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
sumber
36
Iya dan tidak. Saya pikir perbedaan waktu yang Anda lihat disebabkan oleh dua hal. 1) overhead kecil (<100 mikrodetik) dan 2) subsettidak seperti [menghilangkan baris tempat filter mengevaluasi NA. Lakukan ini dan Anda akan melihat bahwa keduanya sama-sama cepat bila dibandingkan "cukup":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 ,] })
goda