可以有条件地计算 dplyr::summarize() 的不同部分吗?
Can different parts of dplyr::summarize() be computed conditionally?
是否可以让条件语句对 dplyr::summarize() 的不同部分进行操作?
假设我正在处理 iris
数据并输出摘要,我只想在请求时包含 Sepal.Length 的平均值。所以我可以做类似的事情:
data(iris)
include_length = T
if (include_length) {
iris %>%
group_by(Species) %>%
summarize(mean_sepal_width = mean(Sepal.Width), mean_sepal_length = mean(Sepal.Length))
} else {
iris %>%
group_by(Species) %>%
summarize(mean_sepal_width = mean(Sepal.Width))
}
但是有没有办法在管道内实现条件,这样就不需要重复了?
大约 。
可能的解决方案:
library(magrittr)
library(dplyr)
data(iris)
include_length = T
iris %>%
group_by(Species) %>%
{ if (include_length) {summarize(., mean_sepal_width = mean(Sepal.Width), mean_sepal_length = mean(Sepal.Length))}
else {summarize(., mean_sepal_width = mean(Sepal.Width))}
}
在 base R 中,您可以执行 c(x, if (d) y)
并根据 d
的值,您将在结果中包含或排除第二个元素。 x
和 y
可以是向量或列表。
这个技巧适用于 data.table,因为 return 表达式是一个列表:
library(data.table)
f = function(d) data.table(iris)[, c(
.(mw = mean(Sepal.Width)),
if(d) .(ml = mean(Sepal.Length))
), by=Species]
用法
> f(TRUE)
Species mw ml
1: setosa 3.428 5.006
2: versicolor 2.770 5.936
3: virginica 2.974 6.588
> f(FALSE)
Species mw
1: setosa 3.428
2: versicolor 2.770
3: virginica 2.974
在 DT[...]
中,.()
是 shorthand 用于 list()
。您可能有想要打管道的原因,但我认为这个选项值得考虑。
有点hackish的方式:
iris %>%
group_by(Species) %>%
summarise(mean_sepal_length=if(include_length) mean(Sepal.Length) else NA,
mean_sepal_width=mean(Sepal.Width))
如果 include_length == TRUE
,这将创建一个列,否则为 NA。如果这是一个问题,您可以删除 post-processing 中的 NA 列。
您可以使用 dplyr SET 函数的 .dots
参数以编程方式进行评估,例如
library(dplyr)
take_means <- function(include_length){
iris %>%
group_by(Species) %>%
summarize_(mean_sepal_width = ~mean(Sepal.Width),
.dots = if(include_length){
list(mean_sepal_length = ~mean(Sepal.Length))
})
}
take_means(TRUE)
#> # A tibble: 3 × 3
#> Species mean_sepal_width mean_sepal_length
#> <fctr> <dbl> <dbl>
#> 1 setosa 3.428 5.006
#> 2 versicolor 2.770 5.936
#> 3 virginica 2.974 6.588
take_means(FALSE)
#> # A tibble: 3 × 2
#> Species mean_sepal_width
#> <fctr> <dbl>
#> 1 setosa 3.428
#> 2 versicolor 2.770
#> 3 virginica 2.974
是否可以让条件语句对 dplyr::summarize() 的不同部分进行操作?
假设我正在处理 iris
数据并输出摘要,我只想在请求时包含 Sepal.Length 的平均值。所以我可以做类似的事情:
data(iris)
include_length = T
if (include_length) {
iris %>%
group_by(Species) %>%
summarize(mean_sepal_width = mean(Sepal.Width), mean_sepal_length = mean(Sepal.Length))
} else {
iris %>%
group_by(Species) %>%
summarize(mean_sepal_width = mean(Sepal.Width))
}
但是有没有办法在管道内实现条件,这样就不需要重复了?
大约
可能的解决方案:
library(magrittr)
library(dplyr)
data(iris)
include_length = T
iris %>%
group_by(Species) %>%
{ if (include_length) {summarize(., mean_sepal_width = mean(Sepal.Width), mean_sepal_length = mean(Sepal.Length))}
else {summarize(., mean_sepal_width = mean(Sepal.Width))}
}
在 base R 中,您可以执行 c(x, if (d) y)
并根据 d
的值,您将在结果中包含或排除第二个元素。 x
和 y
可以是向量或列表。
这个技巧适用于 data.table,因为 return 表达式是一个列表:
library(data.table)
f = function(d) data.table(iris)[, c(
.(mw = mean(Sepal.Width)),
if(d) .(ml = mean(Sepal.Length))
), by=Species]
用法
> f(TRUE)
Species mw ml
1: setosa 3.428 5.006
2: versicolor 2.770 5.936
3: virginica 2.974 6.588
> f(FALSE)
Species mw
1: setosa 3.428
2: versicolor 2.770
3: virginica 2.974
在 DT[...]
中,.()
是 shorthand 用于 list()
。您可能有想要打管道的原因,但我认为这个选项值得考虑。
有点hackish的方式:
iris %>%
group_by(Species) %>%
summarise(mean_sepal_length=if(include_length) mean(Sepal.Length) else NA,
mean_sepal_width=mean(Sepal.Width))
如果 include_length == TRUE
,这将创建一个列,否则为 NA。如果这是一个问题,您可以删除 post-processing 中的 NA 列。
您可以使用 dplyr SET 函数的 .dots
参数以编程方式进行评估,例如
library(dplyr)
take_means <- function(include_length){
iris %>%
group_by(Species) %>%
summarize_(mean_sepal_width = ~mean(Sepal.Width),
.dots = if(include_length){
list(mean_sepal_length = ~mean(Sepal.Length))
})
}
take_means(TRUE)
#> # A tibble: 3 × 3
#> Species mean_sepal_width mean_sepal_length
#> <fctr> <dbl> <dbl>
#> 1 setosa 3.428 5.006
#> 2 versicolor 2.770 5.936
#> 3 virginica 2.974 6.588
take_means(FALSE)
#> # A tibble: 3 × 2
#> Species mean_sepal_width
#> <fctr> <dbl>
#> 1 setosa 3.428
#> 2 versicolor 2.770
#> 3 virginica 2.974