来自 dplyr 的子集和过滤器之间的区别
Posted
技术标签:
【中文标题】来自 dplyr 的子集和过滤器之间的区别【英文标题】:Difference between subset and filter from dplyr 【发布时间】:2017-02-14 09:36:09 【问题描述】:在我看来,子集和过滤器(来自 dplyr)具有相同的结果。 但我的问题是:在某些时候是否存在潜在差异,例如。速度,它可以处理的数据大小等?在某些情况下使用其中一种更好吗?
例子:
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
【问题讨论】:
这篇文章比较了subset
、filter
、with
和[
、how-to-use-or-and-in-dplyr-to-subset-a-data-frame
主要区别在于subset
在?subset
中带有警告:“这是一个用于交互使用的便利函数。对于编程,最好使用标准子集函数,如@987654330 @,特别是参数子集的非标准评估可能会产生意想不到的后果。” filter
旨在以交互方式和编程方式与 dplyr 和 tidyverse 的其余部分一起稳健地工作,并在必要时具有单独的标准评估版本 filter_
。此外,它将逗号视为&
。
@alistaire 只是一个更新,filter_()
和 _
版本的 dplyr 函数现在已被弃用,以支持整洁的评估语义。有关当前最佳实践的详细信息,请参阅programming with dplyr。
我知道;该评论来自四年前。
【参考方案1】:
在主要用例中,它们的行为相同:
library(dplyr)
identical(
filter(starwars, species == "Wookiee"),
subset(starwars, species == "Wookiee"))
# [1] TRUE
但它们有很多不同之处,包括(我尽可能详尽,但可能遗漏了一些):
subset
可用于矩阵
filter
可用于数据库
filter
删除行名
subset
删除类、名称和行名以外的属性。
subset
有一个 select
参数
subset
循环使用它的条件参数
filter
支持条件作为单独的参数
filter
保留列的类
filter
支持.data
代词
filter
支持一些 rlang
功能
filter
支持分组
filter
支持 n()
和 row_number()
filter
更严格
filter
算起来会快一点
subset
在其他包中有方法
subset
可用于矩阵
subset(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
虽然列不能直接用作subset
参数中的变量
subset(state.x77, Population < 400)
subset.matrix(state.x77, Population
两者都不适用于filter
filter(state.x77, state.x77[,"Population"] < 400)
UseMethod("filter_") 中的错误:没有适用于 'filter_' 的方法 应用于“c('matrix', 'double', 'numeric')”类的对象
filter(state.x77, Population < 400)
UseMethod("filter_") 中的错误:没有适用于 'filter_' 的方法 应用于“c('matrix', 'double', 'numeric')”类的对象
filter
可用于数据库
library(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
不能
tbl(con,"mtcars") %>%
subset(hp < 65)
subset.default(., hp
filter
删除行名
filter(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
没有
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
删除类、名称和行名以外的属性。
cars_head <- head(cars)
attr(cars_head, "info") <- "head of cars dataset"
attributes(subset(cars_head, speed > 0))
#> $names
#> [1] "speed" "dist"
#>
#> $row.names
#> [1] 1 2 3 4 5 6
#>
#> $class
#> [1] "data.frame"
attributes(filter(cars_head, speed > 0))
#> $names
#> [1] "speed" "dist"
#>
#> $row.names
#> [1] 1 2 3 4 5 6
#>
#> $class
#> [1] "data.frame"
#>
#> $info
#> [1] "head of cars dataset"
subset
有一个 select
参数
虽然dplyr
遵循tidyverse
原则,旨在让每个函数做一件事,所以select
是一个单独的函数。
identical(
subset(starwars, species == "Wookiee", select = c("name", "height")),
filter(starwars, species == "Wookiee") %>% select(name, height)
)
# [1] TRUE
它还有一个 drop
参数,这在使用 select 参数的上下文中很有意义。
subset
回收它的条件参数
half_iris <- subset(iris,c(TRUE,FALSE))
dim(iris) # [1] 150 5
dim(half_iris) # [1] 75 5
filter
没有
half_iris <- filter(iris,c(TRUE,FALSE))
filter_impl(.data, quo) 中的错误:结果的长度必须为 150,而不是 2
filter
支持条件作为单独的参数
条件被提供给...
,因此我们可以将多个条件作为不同的参数,这与使用&
相同,但有时由于逻辑运算符优先级和自动标识可能更具可读性。
identical(
subset(starwars,
(species == "Wookiee" | eye_color == "blue") &
mass > 120),
filter(starwars,
species == "Wookiee" | eye_color == "blue",
mass > 120)
)
filter
保留列的类
df <- data.frame(a=1:2, b = 3:4, c= 5:6)
class(df$a) <- "foo"
class(df$b) <- "Date"
# subset preserves the Date, but strips the "foo" class
str(subset(df,TRUE))
#> 'data.frame': 2 obs. of 3 variables:
#> $ a: int 1 2
#> $ b: Date, format: "1970-01-04" "1970-01-05"
#> $ c: int 5 6
# filter keeps both
str(dplyr::filter(df,TRUE))
#> 'data.frame': 2 obs. of 3 variables:
#> $ a: 'foo' int 1 2
#> $ b: Date, format: "1970-01-04" "1970-01-05"
#> $ c: int 5 6
filter
支持使用.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
支持一些 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
支持分组
iris %>%
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
支持 n()
和 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
更严格
如果输入可疑,则会触发错误。
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
算起来会快一点
借用本杰明在他的答案中构建的数据集(153 k 行),速度提高了一倍,尽管它应该很少成为瓶颈。
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
在其他包中有方法
subset
是 S3 泛型,就像 dplyr::filter
一样,但 subset
作为基本函数更有可能在其他包中开发方法,一个突出的例子是 zoo:::subset.zoo
。
【讨论】:
TODO:过滤器失败并出现更多有用的错误【参考方案2】:filter
的另一个优点是它可以很好地处理分组数据。 subset
忽略分组。
所以当数据被分组时,subset
仍然会引用整个数据,但filter
只会引用组。
# 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
【讨论】:
【参考方案3】:不同之处还在于,当您在 dplyr 中有两个不同的功能时,子集比过滤器做的事情更多,您也可以选择和删除
subset(df, select=c("varA", "varD"))
dplyr::select(df,varA, varD)
【讨论】:
【参考方案4】:有趣。我试图查看结果数据集的差异,但我无法解释为什么“[”运算符的行为不同(即为什么它也返回 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
【讨论】:
【参考方案5】:另一个尚未提及的区别是过滤器会丢弃行名,而子集不会:
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
【讨论】:
这在某些用例中可能很关键,在这些用例中,行名是必不可少的,并且有优势将它们排除在主要数据之外,例如在计算聚类距离矩阵时【参考方案6】:确实,它们产生了相同的结果,并且在概念上非常相似。
subset
的优点是它是基础 R 的一部分,不需要任何额外的包。对于较小的样本量,它似乎比 filter
快一点(在您的示例中快 6 倍,但以微秒为单位)。
随着数据集的增长,filter
似乎在效率上占了上风。在 15,000 条记录中,filter
比 subset
快了大约 300 微秒。在 153,000 条记录中,filter
的速度提高了三倍(以毫秒为单位)。
所以就人类时间而言,我认为两者之间没有太大区别。
另一个优势(这是一个小众优势)是filter
可以在 SQL 数据库上操作而无需将数据拉入内存。 subset
根本不会那样做。
就我个人而言,我倾向于使用filter
,但这仅仅是因为我已经在使用dplyr
框架。如果您不使用内存不足的数据,它不会有太大的不同。
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
【讨论】:
先生,对我来说结果正好相反!对于这两种情况,subset 在我的机器上的性能都比过滤器好。 可能有六个原因。执行上的差异是否足以在意? 子集 1.164632 1.220479 1.717666 1.266967 1.421527 ,过滤器 5.314198 5.440985 5.669854 5.595846 5.793876以上是关于来自 dplyr 的子集和过滤器之间的区别的主要内容,如果未能解决你的问题,请参考以下文章