老话题重谈
从一个错误开始
grouped_mean <- function(data, group_var, summary_var) {
data %>%
group_by(group_var) %>%
summarise(mean = mean(summary_var))
}
# 这个函数的目的是求分组后的均值
grouped_mean(mtcars, cyl, mpg)
报错
#> Error: Must group by variables found in `.data`.
#> * Column `group_var` is not found.
正确的方式如下
grouped_mean <- function(data, group_var, summary_var) {
group_var <- enquo(group_var)# 引用
summary_var <- enquo(summary_var)# 引用
data %>%
group_by(!!group_var) %>% # !评估
summarise(mean = mean(!!summary_var))# !!评估
}
grouped_mean(mtcars, cyl, mpg)
世界上没有无缘无故的爱,也没有无缘无故的恨
为什么?这就涉及到参数的调用,局部变量和全局变量的问题
另一个例子
df <- data.frame(
y = 1,
var = 2
)
var <- "y"
df[[var]]
#> [1] 1
df$y
#> [1] 1
这两种表达式,最后都会将y值提取,严格上讲
[[是一个评估函数,因此可以使用var这种间接引用
$ 是一个引用函数
# 将cyl 引用
x_var <- quote(cyl)
y_var <- quote(mpg)
x_var
#> cyl
y_var
#> mpg
library("dplyr")
by_cyl <- mtcars %>%
group_by(!!x_var) %>% # Open x_var
summarise(mean = mean(!!y_var)) # Open y_var
#> `summarise()` ungrouping output (override with `.groups` argument)
这里使用的是quote,但实际函数中使用的是
enquo和!!
最终形成下面的函数
grouped_mean <- function(data, group_var, summary_var) {
group_var <- enquo(group_var)
summary_var <- enquo(summary_var)
data %>%
group_by(!!group_var) %>%
summarise(mean = mean(!!summary_var))
}
这里解决的问题是参数作为字符串输入
grouped_mean2 <- function(data, group_var, summary_var) {
group_var <- sym(group_var)# 代替了enquo
summary_var <- sym(summary_var)# 代替了enquo
data %>%
group_by(!!group_var) %>%
summarise(mean = mean(!!summary_var))
}
这样函数就可以纳入字符串参数了
grouped_mean2(starwars, "gender", "mass")
#> `summarise()` ungrouping output (override with `.groups` argument)
#> # A tibble: 3 x 2
#> gender mean
#> <chr> <dbl>
#> 1 feminine NA
#> 2 masculine NA
#> 3 <NA> NA
先看例子
这里在单一变量前面加了dot,这个没什么别的作用,只是为了养成编程好习惯
使用单个点作为多参数的使用
grouped_mean <- function(.data, .summary_var, ...) {
.summary_var <- enquo(.summary_var)
.data %>%
group_by(...) %>%
summarise(mean = mean(!!.summary_var))
}
grouped_mean(mtcars, disp, cyl, am)
#> `summarise()` regrouping output by 'cyl' (override with `.groups` argument)
#> # A tibble: 6 x 3
#> # Groups: cyl [3]
#> cyl am mean
#> <dbl> <dbl> <dbl>
#> 1 4 0 136.
#> 2 4 1 93.6
#> 3 6 0 205.
#> 4 6 1 155
#> 5 8 0 358.
#> # … with 1 more row
上面的例子summary的变量是disp,分组变量是cyl和am,使用三个点这里传递了任意个参数
另外一种需要引用的写法,这种写法的需求是对参数进行修改名称的时候
grouped_mean2 <- function(.data, .summary_var, ...) {
.summary_var <- enquo(.summary_var)
.group_vars <- enquos(...)
.data %>%
group_by(!!!.group_vars) %>%
summarise(mean = mean(!!.summary_var))
}
grouped_mean(mtcars, disp, cyl, am)
#> `summarise()` regrouping output by 'cyl' (override with `.groups` argument)
#> # A tibble: 6 x 3
#> # Groups: cyl [3]
#> cyl am mean
#> <dbl> <dbl> <dbl>
#> 1 4 0 136.
#> 2 4 1 93.6
#> 3 6 0 205.
#> 4 6 1 155
#> 5 8 0 358.
#> # … with 1 more row
单一变量(描述的变量)的情况
grouped_mean2 <- function(.data, .summary_var, ...) {
summary_var <- enquo(.summary_var)
group_vars <- enquos(...)
# 使用as_label将变量名转换为字符
summary_nm <- as_label(summary_var)
summary_nm <- paste0("avg_", summary_nm)# 添加前缀
.data %>%
group_by(!!!group_vars) %>%
summarise(!!summary_nm := mean(!!summary_var)) # 评估新变量名,这里等号需要加:
}
grouped_mean2(mtcars, disp, cyl, am)
#> `summarise()` regrouping output by 'cyl' (override with `.groups` argument)
#> # A tibble: 6 x 3
#> # Groups: cyl [3]
#> cyl am avg_disp
#> <dbl> <dbl> <dbl>
#> 1 4 0 136.
#> 2 4 1 93.6
#> 3 6 0 205.
#> 4 6 1 155
#> 5 8 0 358.
#> # … with 1 more row
多参数(分组变量)的情况
grouped_mean2 <- function(.data, .summary_var, ...) {
summary_var <- enquo(.summary_var)
# 使用enquos,这里需要添加参数named
group_vars <- enquos(..., .named = TRUE)
summary_nm <- as_label(summary_var)
summary_nm <- paste0("avg_", summary_nm)
# 添加前缀
names(group_vars) <- paste0("groups_", names(group_vars))
.data %>%
group_by(!!!group_vars) %>% # Unquote-splice as usual
summarise(!!summary_nm := mean(!!summary_var))
}
grouped_mean2(mtcars, disp, cyl, am)
#> `summarise()` regrouping output by 'groups_cyl' (override with `.groups` argument)
#> # A tibble: 6 x 3
#> # Groups: groups_cyl [3]
#> groups_cyl groups_am avg_disp
#> <dbl> <dbl> <dbl>
#> 1 4 0 136.
#> 2 4 1 93.6
#> 3 6 0 205.
#> 4 6 1 155
#> 5 8 0 358.
#> # … with 1 more row
之前的多变量,都是使用分组变量,那么对于多描述变量?
grouped_mean3 <- function(.data, .group_var, ...) {
group_var <- enquo(.group_var)
summary_vars <- enquos(..., .named = TRUE)
#使用purrr的map函数
# map函数的具体使用后续再考虑
# 简单说就是对第一个参数(数据集、或者list)分别使用第二参数(函数)
# 在r语言中...其实相当于list
# expr函数是将代码转换为表达式,但是并不执行
# 目的是和添加新变量名
summary_vars <- purrr::map(summary_vars, function(var) {
expr(mean(!!var, na.rm = TRUE))
})
#summary_vars为一个list
# 添加前缀avg_
names(summary_vars) <- paste0("avg_", names(summary_vars))
.data %>%
group_by(!!group_var) %>%
summarise(!!!summary_vars)
}
grouped_mean3(starwars, species, height, mass)
#> `summarise()` ungrouping output (override with `.groups` argument)
#> # A tibble: 38 x 3
#> species avg_height avg_mass
#> <chr> <dbl> <dbl>
#> 1 Aleena 79 15
#> 2 Besalisk 198 102
#> 3 Cerean 198 82
#> 4 Chagrian 196 NaN
#> 5 Clawdite 168 55
#> # … with 33 more rows
教程来自Tidy evaluation,by Hadley Wickham
总体上内容偏旧了,看完之后收获很小,应该有更容易的方法
peace