Por que o `[` é melhor que o `subconjunto`?

400

Quando preciso filtrar um data.frame, ou seja, extrair linhas que atendam a determinadas condições, prefiro usar a subsetfunção:

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

Em vez da [função:

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

Há duas razões principais para minha preferência:

  1. Acho que o código lê melhor, da esquerda para a direita. Mesmo as pessoas que não sabem nada sobre R podem dizer o que a subsetafirmação acima está fazendo.

  2. Como as colunas podem ser referidas como variáveis ​​na selectexpressão, posso salvar algumas pressionamentos de tecla. No meu exemplo acima, eu só precisei digitar airqualityuma vez com subset, mas três vezes com [.

Então, eu estava vivendo feliz, usando em subsettodos os lugares porque é mais curto e lê melhor, até mesmo defendendo sua beleza aos meus colegas programadores R. Mas ontem meu mundo se desfez. Ao ler a subsetdocumentação, observe esta seção:

Atenção

Esta é uma função de conveniência destinada ao uso interativamente. Para a programação, é melhor usar as funções de subconjunto padrão como [e, em particular, a avaliação não padrão do subconjunto de argumentos pode ter consequências imprevistas.

Alguém poderia ajudar a esclarecer o que os autores querem dizer?

Primeiro, o que eles querem dizer com " para uso interativo "? Eu sei o que é uma sessão interativa, em oposição a um script executado no modo BATCH, mas não vejo a diferença que deve fazer.

Então, você poderia explicar " a avaliação não padrão do subconjunto de argumentos " e por que é perigosa, talvez fornecer um exemplo?

modelo
fonte
14
Ele é ligeiramente menos (mas menos do que a porca subconjunto) para utilizar com,with(airquality, airquality[Month == 8 & Temp > 90, ])
Tyler Rinker
7
Você também pode dar uma olhada nas Cirlces 8.2.31 e 8.2.32 de 'The R Inferno' burns-stat.com/pages/Tutor/R_inferno.pdf
Patrick Burns
9
Tente data.table, a sintaxe padrão é como airquality [Month == 8 & Temp> 90,] - muito legível e muito mais rápido.
Stian Håklev
3
ESTÁ BEM. então, se o subconjunto é ruim de usar - e quanto a [vs. dplyr :: filter ()?
userJT
4
Para quem se pergunta, dplyr::filtertem o mesmo problema. Ou seja, se o ambiente tiver uma variável com esse nome, ele será usado em vez da variável no quadro de dados. Torna a depuração confusa!
Deleet

Respostas:

241

Essa pergunta foi bem respondida nos comentários de @James, apontando para uma excelente explicação de Hadley Wickham sobre os perigos subset(e funciona como ele) [aqui] . Vá ler!

É uma leitura um tanto longa, portanto, pode ser útil registrar aqui o exemplo que Hadley usa que aborda mais diretamente a questão "o que pode dar errado?":

Hadley sugere o seguinte exemplo: suponha que desejemos subconjunto e, em seguida, reordenar um quadro de dados usando as seguintes funções:

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

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

subscramble(mtcars, cyl == 4)

Isso retorna o erro:

Erro no eval (expr, envir, anexo): objeto 'cyl' não encontrado

porque R não "sabe" mais onde encontrar o objeto chamado 'cyl'. Ele também aponta as coisas verdadeiramente bizarras que podem acontecer se, por acaso, houver um objeto chamado 'cyl' no ambiente global:

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

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

(Execute-os e veja por si mesmo, é muito louco.)

joran
fonte
2
Posso ter algumas perguntas para iniciantes para esclarecimentos? Quando escrevemos subset(mtcars, cyl == 4)(no nível superior), onde R procura o cyl? Se ele olhar para o mtcarsobjeto ao qual é passado subset(), não deve ser capaz de encontrar cylmesmo se scrambleestiver dentro de outra função, pois mtcarsainda está sendo passado para ele? Se minha pergunta não fizer sentido, você pode apenas elaborar mais detalhes sobre por que o R não consegue mais encontrar cyl. Obrigado!
28513 Heisenberg
4
@ Anh Inside subset.data.frame, o que estamos tentando avaliar nesse momento é justo condition. Isso não existe mtcars. Então, subset.data.frameusa enclos = parent.frame()para garantir que conditionseja avaliado corretamente como cyl == 4. Mas então voltamos ao quadro anexo e agora, quando R o procura, cylele não está mais olhando para dentro mtcars. Se não usássemos enclos, algo como subset(mtcars,cyl == a)não funcionaria.
joran
alguém sabe por que o subconjunto () não implementaria apenas o método mais rápido e seguro [,] nos bastidores?
Bjorks fã número um 2/17
11
@MikePalmice Sim. A última linha de subset.data.frameé x[r, vars, drop = drop]. O problema é como passar dos argumentos não citados subsete selectpara algo que você possa transmitir validamente [.data.frame.
joran
@joran entendi, obrigado. como você pensa se deve usar o filtro dplyr em vez de []?
Bjorks número um fã
30

Também [é mais rápido:

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
fonte
36
Sim e não. Acho que a diferença de tempo que você está vendo se deve a duas coisas. 1) uma sobrecarga pequena (<100 microssegundos) e 2) subsetao contrário [remove linhas nas quais o filtro é avaliado NA. Faça isso e você vai ver que eles são ambos tão rápido quando comparado "bastante":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