“Evaluation”(求值)是指执行代码并计算表达式的值的过程。

  1. 延迟求值:某些编程语言支持延迟求值(Lazy Evaluation),这意味着表达式的求值被推迟到其结果实际需要时才进行。

  2. 即时求值:与延迟求值相对的是即时求值(Eager Evaluation),在这种情况下,表达式尽可能早地被求值。

  3. 元编程:在元编程中,代码可以生成或操作其他代码,求值过程可以应用于这些动态生成的代码。

27.1 非标准求值

27.2 整洁求值

Tidy Evaluation (Tidy Eval) 是一个非标准评估的框架,也叫延迟评估。

  1. quasiquotation

  2. quosures

  3. data masking

27.3 Quosure

Show the code
x <- mtcars$mpg
enquo(x)
#> <quosure>
#> expr: ^<dbl: 21, 21, 22.8, 21.4, 18.7, ...>
#> env:  empty
enquo(mtcars)
#> <quosure>
#> expr: ^<df[,11]>
#> env:  empty

mutate(mtcars,!!"cyl") %>% head()
mpg cyl disp hp drat wt qsec vs am gear carb “cyl”
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 cyl
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 cyl
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 cyl
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 cyl
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 cyl
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 cyl
Show the code
grouped_mean <- function(data, summary_var, ...) {
  summary_var <- enquo(summary_var)
    group_var <- enquos(...)
 
  data %>%
    group_by(!!!group_var) %>%   ### 准引用  unquote-splice !!! ,发音为 bang-bang-bang,取消引用多个参数
    summarise(mean = mean(!!summary_var))
}

grouped_mean(mtcars, cyl, mpg)
mpg mean
10.4 8
13.3 8
14.3 8
14.7 8
15.0 8
15.2 8
15.5 8
15.8 8
16.4 8
17.3 8
17.8 6
18.1 6
18.7 8
19.2 7
19.7 6
21.0 6
21.4 5
21.5 4
22.8 4
24.4 4
26.0 4
27.3 4
30.4 4
32.4 4
33.9 4
Show the code
grouped_mean <- function(data, group_var, summary_var) {
  data %>%
    group_by({{group_var}}) %>%
    summarise(mean = mean({{summary_var}}))
}

grouped_mean(mtcars, cyl, mpg)
cyl mean
4 26.66364
6 19.74286
8 15.10000

27.4 Data masking

data masking

  • 环境变量(env-variables) ,一般你在Rstuido右上角的Environment中发现它。比如n <- 10这里的n

  • 数据变量(data-variables),一般指数据框的某个变量。比如data <- data.frame(x = 1, n = 2)中的data$n

grouped_mean(mtcars, cyl, mpg)

cylmpg是打算传递的参数,是环境变量,但我们期望他们在函数中当作mtcars中的数据变量,即当做mtcars的一个列的名字来使用, 那么要完成这个角色转换,就需要引用(quote)和解引用(unquote)两个工序:

  • 第一步,用 enquo()把用户传递过来的参数引用起来

  • 第二步,用 !! 取消引用,然后使用参数的内容

这个quote-unquote的过程让环境变量名变成了数据变量,也可以理解为在函数评估过程中,数据变量(data-variable)遮盖了环境变量(env-variable),即数据遮盖(data masking),看到cyl,正常情况下,本来应该是到环境变量里去找这个cyl对应的值,然而,数据遮盖机制,插队了,让代码去数据变量中去找cyl以及对应的值。

我们通过rlang::qq_show()看看这个quote-unquote机制是怎么工作的

先看看qq_show()

Show the code
  group_var <-  quote(cyl)
summary_var <-  quote(mpg)
    
rlang::qq_show( 
    data %>%
    group_by(!!group_var) %>%
    summarise(mean = mean(!!summary_var))
)
#> data %>% group_by(cyl) %>% summarise(mean = mean(mpg))

27.5 名称注入

27.5.1 glue 语法

Show the code
name <- "susan" 
tibble("{name}" := 2)
susan
2

27.5.2 embracing 语法

Show the code
my_summarise4 <- function(data,by, expr) {   
    data %>% 
        summarise(     "mean_{{expr}}" := mean({{ expr }}),   #embracing拥抱 {{}}     
                       "sum_{{expr}}" := sum({{ expr }}), #拥抱变量会告诉 dplyr使用存储在参数中的值,而不是将参数作为文本变量名称     
                       "n_{{expr}}" := n(),   # 海象运算符(walrus) :=     
                       .by ={{by}}   ) }   

my_summarise4(mpg,by = cyl, expr = displ)
cyl mean_displ sum_displ n_displ
4 2.145679 173.8 81
6 3.408861 269.3 79
8 5.132857 359.3 70
5 2.500000 10.0 4

何时使用{{}}

  • Data-masking: arrange()filter()summarize() compute with variables

  • Tidy-selection:select()relocate()rename() select variables

Show the code
rr <- function(dt, oldName, newName) { 
 rename(dt, !!rlang::sym(newName) := !! rlang::sym(oldName))
      }

rr(dt = as_tibble(iris), oldName = 'Petal.Width', newName = 'petal') %>%
      head(., 2)
Sepal.Length Sepal.Width Petal.Length petal Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa