Tidyverse包中的.符号

dplyr
dot
Author

Lee

Published

October 18, 2022

tidyverse中,我们经常会遇到.符号,不同场景下的.所代表的的含义不同。如果不搞清楚它们各自的含义,会给读写代码带来很大的困扰。观察以下代码:

library(tidyverse)
read_csv("d:/Myblog/datas/wages.csv", show_col_types = FALSE) %>% 
  mutate(letter = str_extract(race, "(?<=h)(.)")) %>% 
  select(., -letter) %>% 
  mutate_at(vars(race), ~ as.factor(.)) %>% 
  mutate_at(vars(sex), ~ if_else(. == "male", 1, 0)) %>% 
  filter_if(~is.numeric(.), all_vars(. != 0)) %>% 
  split(.$sex) %>% 
  map(~ lm(earn ~ ., data = .)) %>% 
  map_dfr(~ broom::tidy(.), .id = "sex")
# A tibble: 8 × 6
  sex   term         estimate std.error statistic   p.value
  <chr> <chr>           <dbl>     <dbl>     <dbl>     <dbl>
1 1     (Intercept)  -121846.   37449.    -3.25    1.21e- 3
2 1     height           977.     515.     1.90    5.84e- 2
3 1     sex               NA       NA     NA      NA       
4 1     racehispanic     578.    7934.     0.0728  9.42e- 1
5 1     raceother      -2035.   11514.    -0.177   8.60e- 1
6 1     racewhite      12823.    5284.     2.43    1.56e- 2
7 1     ed              5234.     601.     8.71    4.30e-17
8 1     age              406.      95.5    4.25    2.52e- 5

上述代码有很多.,那么每一行的.分别代表那些含义呢?回答这个问题之前,我们需要先了解.符号可以代表那些成分。

1 占位符

1.1 一个占位符

.符号的第一大作用就是占位符,通常用在%>%符号中传递参数:

  • y %>% f()表示f(y)

  • y %>% f(x, .)表示f(x, y)

  • z %>% f(x, y, arg = .)表示f(x, y, arg = z)

发现什么规律了?当所传递的参数不是函数的第一个参数时,就需要用.(或.x)来占位。

当传递的参数是第一个时也需要有占位符,不过被省略罢了。
# 通常写法
mtcars %>% 
  select(cyl, disp, hp) %>% 
  slice_head(n = 2)
              cyl disp  hp
Mazda RX4       6  160 110
Mazda RX4 Wag   6  160 110
# 实际是省略了占位符
mtcars %>% 
  select(., cyl, disp, hp) %>% 
  slice_head(., n = 2)
              cyl disp  hp
Mazda RX4       6  160 110
Mazda RX4 Wag   6  160 110

1.2 多个占位符

1:10 %>% {
  c(min(.), max(.))
}
[1]  1 10

2 lambda匿名函数

.如果出现在函数.f的位置上,就是purrr风格的匿名函数~fun(.)。这可以说是整个tidyverse体系中最难理解的.符号的用途,尤其是与map_**()系列函数连用时,需要重点理解。

2.1 单独作为purrr风格匿名函数

mtcars %>% 
  select_at(vars(contains("ar")), ~toupper(.)) %>%
  head(3)
              GEAR CARB
Mazda RX4        4    4
Mazda RX4 Wag    4    4
Datsun 710       4    1

2.2map_**()mutate()系列函数连用

这种情况相对比较复杂,我们还是从例子入手

2.2.1 例子1

iris %>% 
  head(3) %>% 
  mutate(., r_sum = pmap_dbl(select_if(., is.numeric), sum))
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species r_sum
1          5.1         3.5          1.4         0.2  setosa  10.2
2          4.9         3.0          1.4         0.2  setosa   9.5
3          4.7         3.2          1.3         0.2  setosa   9.4

上述代码中有两个.,所代表的的意思是一样的,都是iris %>% head(3)传入的数据框。其中第一个.是可以省略的。

3 小结

  • tidyvere中:

    • 占位符(时常经常和 %>% 一起)
    • Lambda函数
    • 一元函数(LHS)
  • 其他情形

    • 回归公式
    • 正则表达式
  • 注意

    • 有些函数参数以 . 前缀(不要混淆喔! )

4 回答文章最初的问题

read_csv("d:/Myblog/datas/wages.csv", show_col_types = FALSE) %>% 
  mutate(letter = str_extract(race, "(?<=h)(.)")) %>% 
  select(., -letter) %>% 
  mutate_at(vars(race), ~ as.factor(.)) %>% 
  mutate_at(vars(sex), ~ if_else(. == "male", 1, 0)) %>% 
  filter_if(~is.numeric(.), all_vars(. != 0)) %>% 
  split(.$sex) %>% 
  map(~ lm(earn ~ ., data = .)) %>% 
  map_dfr(~ broom::tidy(.), .id = "sex")
# A tibble: 8 × 6
  sex   term         estimate std.error statistic   p.value
  <chr> <chr>           <dbl>     <dbl>     <dbl>     <dbl>
1 1     (Intercept)  -121846.   37449.    -3.25    1.21e- 3
2 1     height           977.     515.     1.90    5.84e- 2
3 1     sex               NA       NA     NA      NA       
4 1     racehispanic     578.    7934.     0.0728  9.42e- 1
5 1     raceother      -2035.   11514.    -0.177   8.60e- 1
6 1     racewhite      12823.    5284.     2.43    1.56e- 2
7 1     ed              5234.     601.     8.71    4.30e-17
8 1     age              406.      95.5    4.25    2.52e- 5
  • 第二行:正则表达式。

  • 第三行:占位符,表示前两步所传入的数据框,可以省略。

  • 第四行:匿名函数,表示race列,由于as.factor()只需一个传入参数,可以不用匿名函数形式。

  • 第五行,与第四行类似。不同点在于,if_else()不止一个参数,所以必须使用匿名函数。

  • 第六行,共有两个.

    • 第一个为普通的匿名函数,传入前几步生成的数据框,同样可以简写为is.numeric
    • 第二个也是匿名函数,但这里它是all_vars(expr)中expr的一种特有写法,代表所有数值型变量,行方向构成的向量, all_vars(. != 0)函数返回TRUE或FALSE再上一步的基础上,帮助filter是否筛选本行
  • 第七行,占位符,表示前几步处理后生成的数据框。

  • 第八行,在回归模型中:

    • 第一个表示除earn变量外的所有变量
    • 第二个为占位符,表示前几步生成的数据框
  • 第九行,有三个.

    • 第一个为占位符,表示前几步生成的数据框
    • 第二个为匿名函数,表示以此前步骤生成的数据框的元素进行迭代梳理
    • 第三个为参数名,.id是特有的一个符号