Чому `[` краще, ніж `підмножина`?


400

Коли мені потрібно фільтрувати data.frame, тобто витягувати рядки, які відповідають певним умовам, я вважаю за краще використовувати subsetфункцію:

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

Замість [функції:

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

Є дві основні причини моїх переваг:

  1. Я вважаю, що код читається краще зліва направо. Навіть люди, які нічого не знають про R, могли сказати, що робить subsetтвердження вище.

  2. Оскільки стовпці можна називати змінними у selectвиразі, я можу зберегти кілька натискань клавіш. У моєму прикладі вище я повинен був набрати лише airqualityодин раз subset, але три рази [.

Тож я жила щасливою, використовуючи subsetвсюди, тому що вона коротша і краще читається, навіть відстоюючи її красу моїм колегам R-кодерам. Але вчора мій світ розпався. Читаючи subsetдокументацію, я помічаю цей розділ:

Увага

Це функція зручності, призначена для інтерактивного використання. Для програмування краще використовувати стандартні функції підмножини типу [, і, зокрема, нестандартна оцінка набору аргументів може мати непередбачувані наслідки.

Може хтось допоможе з’ясувати, що означають автори?

По-перше, що вони означають під " інтерактивним використанням "? Я знаю, що таке інтерактивна сесія, на відміну від сценарію, запущеного в режимі BATCH, але я не бачу, яку різницю він повинен мати.

Тоді, чи можете ви пояснити " нестандартну оцінку підмножини аргументів ", і чому це небезпечно, можливо, наведіть приклад?


14
Трохи менше (але гайка менше, ніж підмножина) для використання,with(airquality, airquality[Month == 8 & Temp > 90, ])
Тайлер Рінкер,

7
Ви також можете подивитися на Cirlces 8.2.31 та 8.2.32 'The R Inferno' burns-stat.com/pages/Tutor/R_inferno.pdf
Патрік Бернс

9
Спробуйте замість data.table, синтаксис за замовчуванням схожий на якість повітря [Місяць == 8 та Темп> 90,] - дуже читабельний та набагато швидший.
Stian Håklev

3
ДОБРЕ. тож якщо підмножина погана у використанні - а як щодо [vs. dplyr :: filter ()?
користувачJT

4
Для тих, хто цікавиться, dplyr::filterє та ж проблема. Тобто, якщо в середовищі є змінна з цим ім'ям, вона використовуватиме її замість змінної у кадрі даних. Робить заплутану налагодження!
Делете

Відповіді:


241

На це питання добре відповів у коментарях @James, вказуючи на чудове пояснення Хедлі Вікхем небезпеки subset(та функцій, як це) [тут] . Іди читай!

Це дещо довго читається, тому може бути корисним записати тут той приклад, який використовує Хадлі, який найбільш безпосередньо стосується питання "що може піти не так?":

Хедлі пропонує наступний приклад: припустимо, ми хочемо підмножити та переупорядкувати кадр даних за допомогою наступних функцій:

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

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

subscramble(mtcars, cyl == 4)

Це повертає помилку:

Помилка eval (expr, envir, enclos): об'єкт 'cyl' не знайдено

тому що R більше не "знає", де знайти об'єкт під назвою "cyl". Він також вказує на справді химерні речі, які можуть статися, якщо випадково в глобальному середовищі з’явиться предмет, який називається "cyl":

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

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

(Запустіть їх і подивіться самі, це досить божевільно.)


2
Чи можу я мати якісь запитання для новачків для уточнення? Коли ми пишемо subset(mtcars, cyl == 4)(на верхньому рівні), де R шукає циліндр? Якщо він заглядає в mtcarsоб'єкт, якому передається subset(), то чи не може він бути в змозі знайти cylнавіть якщо він scrambleзнаходиться в межах іншої функції, оскільки mtcarsвін все ще передається йому? Якщо моє запитання не має сенсу, ви можете просто детальніше пояснити, чому R більше не можна знайти cyl. Дякую!
Гейзенберг

4
@Anh Всередині subset.data.frameріч, яку ми намагаємось оцінити в цей момент, якраз і є condition. Цього не існує в Росії mtcars. Тому subset.data.frameвикористовує enclos = parent.frame()для того, щоб conditionправильно оцінити як cyl == 4. Але тоді ми вискочили назад до рамки, що додається, і тепер, коли R шукає, cylїї вже не шукаємо всередині mtcars. Якби ми не використовували enclos, щось подібне subset(mtcars,cyl == a)взагалі не працювало б.
joran

хтось знає, чому підмножина () не просто реалізує швидший і безпечніший метод [,] поза кадром?
Вболівальник номер один Бьоркс

1
@MikePalmice Це так. Останній рядок subset.data.frameє x[r, vars, drop = drop]. Проблема полягає в тому, як перейти від котирування subsetта selectаргументів до чогось, до чого ти можеш справедливо перейти [.data.frame.
joran

@joran отримав, дякую. як ви думаєте про те, чи потрібно використовувати фільтр dplyr замість []?
Вболівальник номер один Бьоркс

30

Також [швидше:

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
Так і ні. Я думаю, що різниця у часі ви бачите через дві речі. 1) невеликий (<100 мікросекунд) накладний та 2) subsetна відміну від [видалення рядків, за якими фільтр оцінює NA. Зробіть це, і ви побачите, що вони обидва настільки ж швидкі, якщо порівняти їх "справедливо":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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.