第 22 章 tidyverse中的dot

library(tidyverse)

22.1 每一行的 . 各自代表什么意思呢?

read_csv("./data/wages.csv") %>%
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") 回答之前，我们先介绍一些相关知识点 22.2 占位符 管道符号%>% 主要功能是传递参数。 • y %>% f() is equivalent to f(y) • y %>% f(x, .) is equivalent to f(x, y) • z %>% f(x, y, arg = .) is equivalent to f(x, y, arg = z) 我们经常这样写 mtcars %>% select(cyl, disp, hp) %>% head(2) ## # A tibble: 2 x 3 ## cyl disp hp ## <dbl> <dbl> <dbl> ## 1 6 160 110 ## 2 6 160 110 实际上，这里是有占位符的 mtcars %>% select(., cyl, disp, hp) %>% head(., 2) ## # A tibble: 2 x 3 ## cyl disp hp ## <dbl> <dbl> <dbl> ## 1 6 160 110 ## 2 6 160 110 22.3 Lambda函数 .出现在函数.f的位置上， 就是 purrr 风格的Lambda函数~ fun(.) mtcars %>% select_at(vars(contains("ar")), ~ toupper(.)) %>% head(3) ## # A tibble: 3 x 2 ## GEAR CARB ## <dbl> <dbl> ## 1 4 4 ## 2 4 4 ## 3 4 1 有时候程序员会将~toupper(.)简写成 toupper mtcars %>% select_at(vars(contains("ar")), toupper) %>% head(3) ## # A tibble: 3 x 2 ## GEAR CARB ## <dbl> <dbl> ## 1 4 4 ## 2 4 4 ## 3 4 1 22.4 正则表达式 words <- "the fattest cat." words %>% str_replace_all("t.", "-") ## [1] "-e fa-es-ca-" words %>% str_replace_all("t\\.", "-") ## [1] "the fattest ca-" 22.5 Unary funciton (只带一个参数的函数) mean_rm <- . %>% mean(na.rm = T) c(1, 2, 3, NA) %>% mean_rm() ## [1] 2 等价于 # is equivalent to c(1, 2, 3, NA) %>% mean(., na.rm = T) ## [1] 2 22.6 more placeholder iris %>% subset(1:nrow(.) %% 30 == 0) ## # A tibble: 5 x 5 ## Sepal.Length Sepal.Width Petal.Length Petal.Width ## <dbl> <dbl> <dbl> <dbl> ## 1 4.7 3.2 1.6 0.2 ## 2 5.2 2.7 3.9 1.4 ## 3 5.5 2.5 4 1.3 ## 4 6 2.2 5 1.5 ## 5 5.9 3 5.1 1.8 ## # ... with 1 more variable: Species <fct> 1:10 %>% { c(min(.), max(.)) } ## [1] 1 10 22.7 当mutate遇到map dplyr::mutate遇到purrr::map，情况就复杂很多了。然而，这种情况，tidyverse比比皆是。我就多说几句吧 iris %>% head(3) %>% mutate(., r_sum = pmap_dbl(select_if(., is.numeric), sum)) ## # A tibble: 3 x 6 ## Sepal.Length Sepal.Width Petal.Length Petal.Width ## <dbl> <dbl> <dbl> <dbl> ## 1 5.1 3.5 1.4 0.2 ## 2 4.9 3 1.4 0.2 ## 3 4.7 3.2 1.3 0.2 ## # ... with 2 more variables: Species <fct>, ## # r_sum <dbl> 这里mutate()行，有两个., 实际这两个.都是等待iris %>% head(3)传来的data.frame df <- tibble( mean = c(1, 2), sd = c(2, 4) ) df ## # A tibble: 2 x 2 ## mean sd ## <dbl> <dbl> ## 1 1 2 ## 2 2 4 df %>% dplyr::mutate(., rand = map(mean, ~ rnorm(5, .))) %>% tidyr::unnest_wider(rand) ## # A tibble: 2 x 7 ## mean sd ...1 ...2 ...3 ...4 ...5 ## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> ## 1 1 2 0.925 -0.802 2.99 0.937 2.66 ## 2 2 4 4.41 0.0853 2.25 2.58 2.95 • 第一个 .， 是df • 第二个 .， 是df中的mean df %>% dplyr::mutate(rand = map2(mean, sd, ~ rnorm(5, .x, .y))) %>% tidyr::unnest_wider(rand) ## # A tibble: 2 x 7 ## mean sd ...1 ...2 ...3 ...4 ...5 ## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> ## 1 1 2 0.227 1.37 0.326 1.93 4.11 ## 2 2 4 -4.65 2.15 6.33 -2.17 3.66 • mean传给 .x • sd传给 .y 再来一个变态的。（我们不一定要这样写，但我们尽可能的要明白它的意思。） df <- tribble( ~a, ~b, 1, 10, 2, 11 ) df %>% dplyr::mutate(., sum = purrr::pmap_dbl(., ~ sum(...))) ## # A tibble: 2 x 3 ## a b sum ## <dbl> <dbl> <dbl> ## 1 1 10 11 ## 2 2 11 13 22.8 Dot dot dot commas <- function(...) { stringr::str_c(..., collapse = ", ") } commas(letters[1:10]) ## [1] "a, b, c, d, e, f, g, h, i, j" 22.9 Don’t confuse 注意：有些函数的参数前缀是 . mutate_all(.tbl, .funs, ...) mutate_if(.tbl, .predicate, .funs, ...) mutate_at(.tbl, .vars, .funs, ..., .cols = NULL) select_all(.tbl, .funs = list(), ...) rename_all(.tbl, .funs = list(), ...) 22.10 小结 • tidyvere中 • 占位符(时常经常和 %>% 一起) • Lambda函数 • 一元函数（LHS） • 其他情形 • 回归公式 • 正则表达式 • 注意 • 有些函数参数以 . 前缀(不要混淆喔! ) 22.11 回答问题 现在回答本章开始的问题 read_csv("./demo_data/wages.csv") %>% dplyr::mutate(letter = str_extract(race, "(?<=h)(.)")) %>% dplyr::select(., -letter) %>% dplyr::mutate_at(vars(race), ~ as.factor(.)) %>% dplyr::mutate_at(vars(sex), ~ if_else(. == "male", 1, 0)) %>% dplyr::filter_if(~ is.numeric(.), all_vars(. != 0)) %>% split(.$sex) %>%
purrr::map(~ lm(earn ~ ., data = .)) %>%
purrr::map_dfr(., ~ broom::tidy(.), .id = "sex")
## # A tibble: 8 x 6
##   sex   term     estimate std.error statistic   p.value
##   <chr> <chr>       <dbl>     <dbl>     <dbl>     <dbl>
## 1 1     (Interc~ -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     racehis~     578.    7934.     0.0728  9.42e- 1
## 5 1     raceoth~   -2035.   11514.    -0.177   8.60e- 1
## 6 1     racewhi~   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行：路径中.代表当前位置，如果是..表示上一级目录
• 第2行：正则表达式，代表任何字符
• 第3行：占位符，等待数据框的传入，也可以简写select(-letter)
• 第4行: lambda函数，~ as.factor(.)也可以简写as.factor~(.)要么都写，要么都不写
• 第5行：同上,lambda函数
• 第6行：第一个.代表lambda函数; 第二个.也是lambda函数，但这里它是all_vars(expr)中expr的一种特有写法，代表所有数值型变量，*行方向构成的向量, all_vars(. != 0)函数返回TRUE或FALSE，从而帮助filter()是否筛选该行
• 第7行：占位符，代表上面传来的数据框
• 第8行：回归模型lm中，第一个.代表除因变量earn之外所有的变量，第二个.占位符，留给上面的数据框
• 第9行：第一个.是占位符，代表上面传来的list，第二个.lambda函数，依次对list的元素迭代处理，第二个.是参数名，.id是特有的一个符号。