在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
上述代码有很多.
,那么每一行的.
分别代表那些含义呢?回答这个问题之前,我们需要先了解.
符号可以代表那些成分。
占位符
一个占位符
.
符号的第一大作用就是占位符,通常用在%>%
符号中传递参数:
y %>% f()
表示f(y)
y %>% f(x, .)
表示f(x, y)
z %>% f(x, y, arg = .)
表示f(x, y, arg = z)
发现什么规律了?当所传递的参数不是函数的第一个参数时,就需要用.
(或.x
)来占位。
当传递的参数是第一个时也需要有占位符,不过被省略罢了。
cyl disp hp
Mazda RX4 6 160 110
Mazda RX4 Wag 6 160 110
cyl disp hp
Mazda RX4 6 160 110
Mazda RX4 Wag 6 160 110
lambda匿名函数
.
如果出现在函数.f
的位置上,就是purrr
风格的匿名函数~fun(.)
。这可以说是整个tidyverse
体系中最难理解的.
符号的用途,尤其是与map_**()
系列函数连用时,需要重点理解。
单独作为purrr风格匿名函数
GEAR CARB
Mazda RX4 4 4
Mazda RX4 Wag 4 4
Datsun 710 4 1
与map_**()
和mutate()
系列函数连用
这种情况相对比较复杂,我们还是从例子入手
例子1
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)
传入的数据框。其中第一个.
是可以省略的。
小结
-
tidyvere中:
- 占位符(时常经常和 %>% 一起)
- Lambda函数
- 一元函数(LHS)
-
其他情形
-
注意
回答文章最初的问题
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
是特有的一个符号