前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >R海拾遗_再谈非标准评估

R海拾遗_再谈非标准评估

作者头像
火星娃统计
发布2021-06-29 14:55:10
6390
发布2021-06-29 14:55:10
举报
文章被收录于专栏:火星娃统计火星娃统计

概述

老话题重谈

gogogo

从一个错误开始

代码语言:javascript
复制
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.

正确的方式如下

代码语言:javascript
复制
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)

为什么?

世界上没有无缘无故的爱,也没有无缘无故的恨

为什么?这就涉及到参数的调用,局部变量和全局变量的问题

另一个例子

代码语言:javascript
复制
df <- data.frame(
  y = 1,
  var = 2
)

var <- "y"
df[[var]]
#> [1] 1

df$y
#> [1] 1

这两种表达式,最后都会将y值提取,严格上讲

[[是一个评估函数,因此可以使用var这种间接引用

$ 是一个引用函数

Quote 和 quote

代码语言:javascript
复制
# 将cyl 引用
x_var <- quote(cyl)
y_var <- quote(mpg)

x_var
#> cyl

y_var
#> mpg
  • dplyr包中是通过!!来进行评估,因为之前将cyl引用,这使用!!打开
代码语言:javascript
复制
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和!!

最终形成下面的函数

代码语言:javascript
复制
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))
}

sym和!!

这里解决的问题是参数作为字符串输入

代码语言:javascript
复制
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))
}

这样函数就可以纳入字符串参数了

代码语言:javascript
复制
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,这个没什么别的作用,只是为了养成编程好习惯

使用单个点作为多参数的使用

代码语言:javascript
复制
grouped_mean <- function(.data, .summary_var, ...) {
  .summary_var <- enquo(.summary_var)

  .data %>%
    group_by(...) %>%
    summarise(mean = mean(!!.summary_var))
}
代码语言:javascript
复制
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,使用三个点这里传递了任意个参数

另外一种需要引用的写法,这种写法的需求是对参数进行修改名称的时候

代码语言:javascript
复制
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

修改参数名字

单一变量(描述的变量)的情况

  1. 使用enquo进行引用
  2. 使用as_label转换分组变量为字符串
  3. 使用paste0粘贴前缀
  4. 使用!!评估,并注意等号的写法
代码语言:javascript
复制
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

多参数(分组变量)的情况

  1. 使用enquos引用,需要添加参数named
  2. 使用names获得group的名字
  3. 使用paste0添加前缀
代码语言:javascript
复制
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

之前的多变量,都是使用分组变量,那么对于多描述变量?

代码语言:javascript
复制
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)
}
代码语言:javascript
复制
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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 火星娃统计 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • gogogo
  • 为什么?
  • Quote 和 quote
  • sym和!!
  • 多参数评估
  • 修改参数名字
  • 结束语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档