Il me semble que le sous-ensemble et le filtre (de dplyr) ont le même résultat… .. Mais ma question est la suivante: existe-t-il à un moment une différence potentielle, par exemple la vitesse, la taille des données qu'il peut gérer, etc.? Y a-t-il des occasions où il vaut mieux utiliser l'un ou l'autre?
Exemple:
library(dplyr)
df1<-subset(airquality, Temp>80 & Month > 5)
df2<-filter(airquality, Temp>80 & Month > 5)
summary(df1$Ozone)
# Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
# 9.00 39.00 64.00 64.51 84.00 168.00 14
summary(df2$Ozone)
# Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
# 9.00 39.00 64.00 64.51 84.00 168.00 14
Ils produisent effectivement le même résultat et leur concept est très similaire.
L’avantage de subset
est qu’il fait partie de la base R et ne nécessite aucun paquet supplémentaire. Avec des échantillons de petite taille, il semble être un peu plus rapide que filter
(6 fois plus rapide dans votre exemple, mais cela se mesure en microsecondes).
À mesure que les ensembles de données augmentent, filter
semble gagner en efficacité. À 15 000 enregistrements, filter
dépasse subset
d’environ 300 microsecondes. Et avec 153 000 enregistrements, filter
est trois fois plus rapide (mesurée en millisecondes).
Donc, en termes de temps humain, je ne pense pas qu'il y ait beaucoup de différence entre les deux.
L’autre avantage (et c’est un avantage de niche) est que filter
peut fonctionner sur des bases de données SQL sans extraire les données en mémoire. subset
ne fait tout simplement pas cela.
Personnellement, j’ai tendance à utiliser filter
, mais uniquement parce que j’utilise déjà le framework dplyr
. Si vous ne travaillez pas avec des données insuffisantes, cela ne fera pas une grande différence.
library(dplyr)
library(microbenchmark)
# Original example
microbenchmark(
df1<-subset(airquality, Temp>80 & Month > 5),
df2<-filter(airquality, Temp>80 & Month > 5)
)
Unit: microseconds
expr min lq mean median uq max neval cld
subset 95.598 107.7670 118.5236 119.9370 125.949 167.443 100 a
filter 551.886 564.7885 599.4972 571.5335 594.993 2074.997 100 b
# 15,300 rows
air <- lapply(1:100, function(x) airquality) %>% bind_rows
microbenchmark(
df1<-subset(air, Temp>80 & Month > 5),
df2<-filter(air, Temp>80 & Month > 5)
)
Unit: microseconds
expr min lq mean median uq max neval cld
subset 1187.054 1207.5800 1293.718 1216.671 1257.725 2574.392 100 b
filter 968.586 985.4475 1056.686 1023.862 1036.765 2489.644 100 a
# 153,000 rows
air <- lapply(1:1000, function(x) airquality) %>% bind_rows
microbenchmark(
df1<-subset(air, Temp>80 & Month > 5),
df2<-filter(air, Temp>80 & Month > 5)
)
Unit: milliseconds
expr min lq mean median uq max neval cld
subset 11.841792 13.292618 16.21771 13.521935 13.867083 68.59659 100 b
filter 5.046148 5.169164 10.27829 5.387484 6.738167 65.38937 100 a
Une différence supplémentaire non encore mentionnée est que le filtre élimine les noms de domaine, contrairement au sous-ensemble:
filter(mtcars, gear == 5)
mpg cyl disp hp drat wt qsec vs am gear carb
1 26.0 4 120.3 91 4.43 2.140 16.7 0 1 5 2
2 30.4 4 95.1 113 3.77 1.513 16.9 1 1 5 2
3 15.8 4 351.0 264 4.22 3.170 14.5 0 1 5 4
4 19.7 4 145.0 175 3.62 2.770 15.5 0 1 5 6
5 15.0 4 301.0 335 3.54 3.570 14.6 0 1 5 8
subset(mtcars, gear == 5)
mpg cyl disp hp drat wt qsec vs am gear carb
Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.7 0 1 5 2
Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.9 1 1 5 2
Ford Pantera L 15.8 4 351.0 264 4.22 3.170 14.5 0 1 5 4
Ferrari Dino 19.7 4 145.0 175 3.62 2.770 15.5 0 1 5 6
Maserati Bora 15.0 4 301.0 335 3.54 3.570 14.6 0 1 5 8
Intéressant. J'essayais de voir la différence en termes de jeu de données résultant et je ne pourrais pas comprendre pourquoi l'opérateur "[" s'est comporté différemment (c'est-à-dire pourquoi il a également renvoyé des NA):
# Subset for year=2013
sub<-brfss2013 %>% filter(iyear == "2013")
dim(sub)
#[1] 486088 330
length(which(is.na(sub$iyear))==T)
#[1] 0
sub2<-filter(brfss2013, iyear == "2013")
dim(sub2)
#[1] 486088 330
length(which(is.na(sub2$iyear))==T)
#[1] 0
sub3<-brfss2013[brfss2013$iyear=="2013", ]
dim(sub3)
#[1] 486093 330
length(which(is.na(sub3$iyear))==T)
#[1] 5
sub4<-subset(brfss2013, iyear=="2013")
dim(sub4)
#[1] 486088 330
length(which(is.na(sub4$iyear))==T)
#[1] 0
Dans les principaux cas d'utilisation, ils se comportent de la même manière:
library(dplyr)
identical(
filter(starwars, species == "Wookiee"),
subset(starwars, species == "Wookiee"))
# [1] TRUE
Mais ils ont quelques différences, notamment (j’ai été aussi exhaustif que possible mais j’en ai peut-être oublié certaines):
subset
peut être utilisé sur des matricesfilter
peut être utilisé sur des bases de donnéesfilter
supprime les noms de lignesubset
a un argument select
subset
recycle son argument de conditionfilter
supporte les conditions en tant qu'arguments séparésfilter
supporte l'utilisation du pronom .data
filter
supporte certaines fonctionnalités rlang
filter
prend en charge le regroupementfilter
prend en charge n()
et row_number()
filter
est plus strictfilter
est un peu plus rapide quand ça comptesubset
a des méthodes dans d'autres paquetssubset
peut être utilisé sur des matricessubset(state.x77, state.x77[,"Population"] < 400)
# Population Income Illiteracy Life Exp Murder HS Grad Frost Area
# Alaska 365 6315 1.5 69.31 11.3 66.7 152 566432
# Wyoming 376 4566 0.6 70.29 6.9 62.9 173 97203
Bien que les colonnes ne puissent pas être utilisées directement en tant que variables dans l'argument subset
subset(state.x77, Population < 400)
Erreur dans subset.matrix (state.x77, Population <400): object 'Population' introuvable
Ni ne fonctionne avec filter
filter(state.x77, state.x77[,"Population"] < 400)
Error in UseMethod ("filter_"): aucune méthode applicable pour 'filter _' appliqué à un objet de classe "c ('matrice', 'double', 'numérique')"
filter(state.x77, Population < 400)
Error in UseMethod ("filter_"): aucune méthode applicable pour 'filter _' appliqué à un objet de classe "c ('matrice', 'double', 'numérique')"
filter
peut être utilisé sur des bases de donnéeslibrary(DBI)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbWriteTable(con, "mtcars", mtcars)
tbl(con,"mtcars") %>%
filter(hp < 65)
# # Source: lazy query [?? x 11]
# # Database: sqlite 3.19.3 [:memory:]
# mpg cyl disp hp drat wt qsec vs am gear carb
# <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
# 2 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
subset
ne peut pas
tbl(con,"mtcars") %>%
subset(hp < 65)
Erreur dans subset.default (., Hp <65): objet 'hp' introuvable.
filter
supprime les noms de lignefilter(mtcars, hp < 65)
# mpg cyl disp hp drat wt qsec vs am gear carb
# 1 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
# 2 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
subset
n'a pas
subset(mtcars, hp < 65)
# mpg cyl disp hp drat wt qsec vs am gear carb
# Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
# Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
subset
a un argument select
Alors que dplyr
suit les principes tidyverse
qui visent à ce que chaque fonction fasse une chose, donc select
est une fonction séparée.
identical(
subset(starwars, species == "Wookiee", select = c("name", "height")),
filter(starwars, species == "Wookiee") %>% select(name, height)
)
# [1] TRUE
Il a également un argument drop
, qui a principalement du sens dans le contexte de l’utilisation de l’argument select.
subset
recycle son argument de conditionhalf_iris <- subset(iris,c(TRUE,FALSE))
dim(iris) # [1] 150 5
dim(half_iris) # [1] 75 5
filter
n'a pas
half_iris <- filter(iris,c(TRUE,FALSE))
Erreur dans filter_impl (.data, quo): le résultat doit avoir une longueur de 150, et non 2
filter
supporte les conditions en tant qu'arguments séparésLes conditions sont transmises à ...
afin que nous puissions en avoir plusieurs comme arguments différents, ce qui revient au même que d'utiliser &
mais peut être plus lisible parfois en raison de la priorité des opérateurs logiques et de l'identification automatique.
identical(
subset(starwars,
(species == "Wookiee" | eye_color == "blue") &
mass > 120),
filter(starwars,
species == "Wookiee" | eye_color == "blue",
mass > 120)
)
filter
supporte l'utilisation du pronom .data
mtcars %>% filter(.data[["hp"]] < 65)
# mpg cyl disp hp drat wt qsec vs am gear carb
# 1 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
# 2 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
filter
supporte certaines fonctionnalités rlang
x <- "hp"
library(rlang)
mtcars %>% filter(!!sym(x) < 65)
# m pg cyl disp hp drat wt qsec vs am gear carb
# 1 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
# 2 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
filter65 <- function(data,var){
data %>% filter(!!enquo(var) < 65)
}
mtcars %>% filter65(hp)
# mpg cyl disp hp drat wt qsec vs am gear carb
# 1 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
# 2 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
filter
prend en charge le regroupementiris %>%
group_by(Species) %>%
filter(Petal.Length < quantile(Petal.Length,0.01))
# # A tibble: 3 x 5
# # Groups: Species [3]
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# <dbl> <dbl> <dbl> <dbl> <fctr>
# 1 4.6 3.6 1.0 0.2 setosa
# 2 5.1 2.5 3.0 1.1 versicolor
# 3 4.9 2.5 4.5 1.7 virginica
iris %>%
group_by(Species) %>%
subset(Petal.Length < quantile(Petal.Length,0.01))
# # A tibble: 2 x 5
# # Groups: Species [1]
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# <dbl> <dbl> <dbl> <dbl> <fctr>
# 1 4.3 3.0 1.1 0.1 setosa
# 2 4.6 3.6 1.0 0.2 setosa
filter
prend en charge n()
et row_number()
filter(iris, row_number() < n()/30)
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1 5.1 3.5 1.4 0.2 setosa
# 2 4.9 3.0 1.4 0.2 setosa
# 3 4.7 3.2 1.3 0.2 setosa
# 4 4.6 3.1 1.5 0.2 setosa
filter
est plus strictCela déclenche des erreurs si l'entrée est suspecte.
filter(iris, Species = "setosa")
# Error: `Species` (`Species = "setosa"`) must not be named, do you need `==`?
identical(subset(iris, Species = "setosa"), iris)
# [1] TRUE
df1 <- setNames(data.frame(a = 1:3, b=5:7),c("a","a"))
# df1
# a a
# 1 1 5
# 2 2 6
# 3 3 7
filter(df1, a > 2)
#Error: Column `a` must have a unique name
subset(df1, a > 2)
# a a.1
# 3 3 7
filter
est un peu plus rapide quand ça compteEmpruntant le jeu de données que Benjamin a construit dans sa réponse (153 000 lignes), il est deux fois plus rapide, même si cela devrait rarement être un goulot d'étranglement.
air <- lapply(1:1000, function(x) airquality) %>% bind_rows
microbenchmark::microbenchmark(
subset = subset(air, Temp>80 & Month > 5),
filter = filter(air, Temp>80 & Month > 5)
)
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# subset 8.771962 11.551255 19.942501 12.576245 13.933290 108.0552 100 b
# filter 4.144336 4.686189 8.024461 6.424492 7.499894 101.7827 100 a
subset
a des méthodes dans d'autres paquetssubset
est un générique S3, tout comme dplyr::filter
est, mais subset
en tant que fonction de base est plus susceptible d'avoir des méthodes développées dans d'autres packages, un exemple important est Zoo:::subset.Zoo
.
Un avantage supplémentaire de filter
est qu’il joue Nice avec des données groupées. subset
ignore les groupements.
Ainsi, lorsque les données sont groupées, subset
fera toujours référence à l’ensemble des données, mais filter
fera uniquement référence au groupe.
# setup
library(tidyverse)
data.frame(a = 1:2) %>% group_by(a) %>% subset(length(a) == 1)
# returns empty table
data.frame(a = 1:2) %>% group_by(a) %>% filter(length(a) == 1)
# returns all rows
Une différence est également que le sous-ensemble fait plus de choses que le filtre que vous pouvez également sélectionner et supprimer tout en ayant deux fonctions différentes
subset(df, select=c("varA", "varD"))
dplyr::select(df,varA, varD)