Pourquoi est «[« mieux que «sous-ensemble»?

400

Lorsque je dois filtrer un data.frame, c'est-à-dire extraire des lignes qui remplissent certaines conditions, je préfère utiliser la subsetfonction:

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

Plutôt que la [fonction:

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

Il y a deux raisons principales à ma préférence:

  1. Je trouve que le code se lit mieux, de gauche à droite. Même les personnes qui ne connaissent rien à R pourraient dire ce que fait la subsetdéclaration ci-dessus.

  2. Étant donné que les colonnes peuvent être appelées variables dans l' selectexpression, je peux enregistrer quelques frappes. Dans mon exemple ci-dessus, je n'ai dû taper airqualityqu'une seule fois avec subset, mais trois fois avec [.

Je vivais donc heureux, en utilisant subsetpartout parce qu'il est plus court et se lit mieux, même en préconisant sa beauté à mes collègues codeurs R. Mais hier, mon monde s'est brisé. En lisant la subsetdocumentation, je remarque cette section:

Attention

Il s'agit d'une fonction pratique destinée à être utilisée de manière interactive. Pour la programmation, il est préférable d'utiliser les fonctions de sous-ensemble standard comme [, et en particulier l'évaluation non standard du sous-ensemble d'arguments peut avoir des conséquences imprévues.

Quelqu'un pourrait-il aider à clarifier ce que les auteurs veulent dire?

Premièrement, que veulent-ils dire par « pour une utilisation interactive »? Je sais ce qu'est une session interactive, par opposition à un script exécuté en mode BATCH mais je ne vois pas quelle différence cela devrait faire.

Ensuite, pourriez-vous expliquer " l'évaluation non standard du sous-ensemble d'arguments " et pourquoi elle est dangereuse, peut-être fournir un exemple?

flodel
la source
14
Il est légèrement moins (mais moins que le sous-ensemble) à utiliser avec,with(airquality, airquality[Month == 8 & Temp > 90, ])
Tyler Rinker
7
Vous pouvez également consulter Cirlces 8.2.31 et 8.2.32 de 'The R Inferno' burns-stat.com/pages/Tutor/R_inferno.pdf
Patrick Burns
9
Essayez plutôt data.table, la syntaxe par défaut est comme la qualité de l'air [Month == 8 & Temp> 90,] - très lisible et beaucoup plus rapide.
Stian Håklev
3
D'ACCORD. donc si le sous-ensemble est mauvais à utiliser - qu'en est-il de [vs dplyr :: filter ()?
userJT
4
Pour ceux qui se demandent, dplyr::filtera le même problème. Autrement dit, si l'environnement possède une variable portant ce nom, il l'utilisera à la place de la variable dans le bloc de données. Rend le débogage déroutant!
Deleet

Réponses:

241

Cette question a été bien répondu dans les commentaires de @James, pointant une excellente explication par Hadley Wickham des dangers de subset(et des fonctions similaires) [ici] . Allez le lire!

C'est une lecture assez longue, donc il peut être utile d'enregistrer ici l'exemple que Hadley utilise qui répond le plus directement à la question de "qu'est-ce qui peut mal tourner?":

Hadley suggère l'exemple suivant: supposons que nous voulons sous-ensemble puis réorganiser une trame de données en utilisant les fonctions suivantes:

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

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

subscramble(mtcars, cyl == 4)

Cela renvoie l'erreur:

Erreur dans eval (expr, envir, enclos): objet 'cyl' introuvable

car R ne "sait" plus où trouver l'objet appelé 'cyl'. Il souligne également les choses vraiment bizarres qui peuvent se produire si par hasard il y a un objet appelé «cyl» dans l'environnement mondial:

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

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

(Exécutez-les et voyez par vous-même, c'est assez fou.)

joran
la source
2
Puis-je avoir des questions pour les débutants pour clarifier? Lorsque nous écrivons subset(mtcars, cyl == 4)(au niveau supérieur), où R recherche-t-il cyl? S'il regarde l' mtcarsobjet qui est passé à subset(), alors ne devrait-il pas être capable de trouver cylmême s'il se scrambletrouve dans une autre fonction, car il lui mtcarsest toujours transmis? Si ma question n'a pas de sens, vous pouvez simplement expliquer pourquoi R ne peut plus trouver cyl. Merci!
Heisenberg
4
@Anh Inside subset.data.frame, la chose que nous essayons d'évaluer à ce stade est juste condition. Cela n'existe pas mtcars. subset.data.frameUtilise donc enclos = parent.frame()pour s'assurer qu'il conditionest correctement évalué comme cyl == 4. Mais ensuite, nous avons remonté jusqu'au cadre englobant, et maintenant, lorsque R cherche, cylil ne regarde plus à l'intérieur mtcars. Si nous ne l'utilisions pas enclos, quelque chose comme ça subset(mtcars,cyl == a)ne fonctionnerait pas du tout.
joran
Quelqu'un sait-il pourquoi subset () n'implémenterait pas simplement la méthode [,] plus rapide et plus sûre en arrière-plan?
Fan numéro un de Bjorks
1
@MikePalmice Oui. La dernière ligne de subset.data.frameest x[r, vars, drop = drop]. Le problème est de savoir comment passer des arguments non cités subsetet des selectarguments à quelque chose que vous pouvez valablement transmettre [.data.frame.
joran
@joran l'a compris, merci. comment pensez-vous d'utiliser ou non le filtre de dplyr []?
Fan numéro un de Bjorks
30

Est également [plus rapide:

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
la source
36
Oui et non. Je pense que le décalage horaire que vous voyez est dû à deux choses. 1) une petite surcharge (<100 microsecondes) et 2) subsetcontrairement à [supprime les lignes où le filtre est évalué NA. Faites-le et vous verrez qu'ils sont tous les deux aussi rapides que comparés "assez":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