Почему `[` лучше чем `subset`?

362

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

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

Вместо функции [:

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

Есть две основные причины моего предпочтения:

  • Я считаю, что код читается лучше, слева направо. Даже люди, которые ничего не знают о R, могут сказать, что делает инструкция subset выше.

  • Поскольку столбцы можно назвать переменными в выражении select, я могу сэкономить несколько нажатий клавиш. В приведенном выше примере мне приходилось вводить airquality один раз с subset, но три раза с [.

Итак, я жил счастливым, используя subset всюду, потому что он короче и читает лучше, даже защищая его красоту среди моих соратников-кодеров. Но вчера мой мир развалился. Читая документацию subset, я замечаю этот раздел:

Предупреждение

Это функция удобства, предназначенная для интерактивного использования. Для программирования лучше использовать стандартные функции подмножества, такие как [, и, в частности, нестандартная оценка подмножества аргументов может иметь непредвиденные последствия.

Может кто-то помочь прояснить, что означают авторы?

Во-первых, что они подразумевают под "для интерактивного использования"? Я знаю, что такое интерактивный сеанс, в отличие от script в режиме BATCH, но я не вижу, какую именно разницу он должен сделать.

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

  • 13
    Это немного меньше (но меньше, чем подмножество) для использования с with(airquality, airquality[Month == 8 & Temp > 90, ])
  • 1
    В этой теме обсуждается предупреждение subset() : r.789695.n4.nabble.com/…
Показать ещё 8 комментариев
Теги:
filter
subset
r-faq

2 ответа

214
Лучший ответ

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

Это несколько длинное чтение, поэтому может быть полезно записать здесь пример, который использует Хэдли, который наиболее непосредственно затрагивает вопрос "что может пойти не так?":

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

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

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

subscramble(mtcars, cyl == 4)

Это возвращает ошибку:

Ошибка в eval (expr, envir, enc): объект 'cyl' не найден

потому что R больше не "знает", где найти объект, называемый "цил". Он также указывает на действительно причудливые вещи, которые могут произойти, если случайно в глобальной среде есть объект, называемый "цил":

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) не сработало бы вообще.
Показать ещё 9 комментариев
22

Также [ выполняется быстрее:

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
  • 31
    И да и нет. Я думаю, что разница во времени, которую вы видите, обусловлена двумя причинами. 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 ,] })

Ещё вопросы

Сообщество Overcoder
Наверх
Меню