第 18 章 tidyverse进阶

让我们继续聊聊,相见恨晚的tidyverse

18.1 scoped 函数

在第 6 章介绍了dplyr的一些函数(mutate(), select()等等),事实上,这些函数加上后缀 _all, _at, _if,形成三组变体函数,可以方便对特定的子集进行操作。比如

  • 对数据框所有列操作,可以用_all
  • 对数据框指定的几列操作,可以用_at
  • 对数据框符合条件的几列进行操作,可以用_if
Operate _all _at _if
select() select_all() select_at() select_if()
mutate() mutate_all() mutate_at() mutate_if()
rename() rename_all() rename_at() rename_if()
arrange() arrange_all() arrange_at() arrange_if()
filter() filter_all() filter_at() filter_if()
distinct() distinct_all() distinct_at() distinct_if()
group_by() group_by_all() group_by_at() group_by_if()
summarise() summarise_all() summarise_at() summarise_if()
map() map_all() map_at() map_if()
modify() modify_all() modify_at() modify_if()

下面选取其中几个函数加以说明

18.1.1 mutate_if

可以一次性增加多列

也可以把函数放在list()中,用 Purrr-style lambda 形式写出

18.1.2 select_if()

select_if 多个条件的情况

我们也可以写成函数的形式

18.2 summarise_if

18.3 filter_if()

事实上,filter已经很强大了,有了scoped函数,就如虎添翼了

mtcars是 R内置数据集,记录了32种不同品牌的轿车的的11个属性

filter_if()配合all_vars(), any_vars()函数,可以完成很酷的工作. 比如,要求一行中所有变量的值都大于150

比如,要求一行中至少有一个变量的值都大于150

filter_if(.tbl, .predicate, .vars_predicate) 相对复杂点,我这里多说几句。

filter_if() 有三个参数:

  • .tbl, 数据框
  • .predicate, 应用在列上的函数,一般作为列的选择条件
  • .vars_predicate, 应用在一行上的函数,通过 all_vars(), any_vars()返回值决定是否选取该行。

所以这里是,先通过.predicate = ~ all(floor(.) == .) 选取变量值为整数的列,然后再看选取的这些列的行方向,如果每一行的值.vars_predicate = all_vars(. != 0) ,都不为0,就保留下来,否则过滤掉。

简单点说,这段代码的意思,数值全部为整数的列,不能同时为0

18.4 group_by

group_by() 用的很多,所以要多讲讲

18.4.1 group_split(), group_map(), group_modify()

## [[1]]
## # A tibble: 50 x 5
##    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
##  4          4.6         3.1          1.5         0.2
##  5          5           3.6          1.4         0.2
##  6          5.4         3.9          1.7         0.4
##  7          4.6         3.4          1.4         0.3
##  8          5           3.4          1.5         0.2
##  9          4.4         2.9          1.4         0.2
## 10          4.9         3.1          1.5         0.1
## # ... with 40 more rows, and 1 more variable:
## #   Species <fct>
## 
## [[2]]
## # A tibble: 50 x 5
##    Sepal.Length Sepal.Width Petal.Length Petal.Width
##           <dbl>       <dbl>        <dbl>       <dbl>
##  1          7           3.2          4.7         1.4
##  2          6.4         3.2          4.5         1.5
##  3          6.9         3.1          4.9         1.5
##  4          5.5         2.3          4           1.3
##  5          6.5         2.8          4.6         1.5
##  6          5.7         2.8          4.5         1.3
##  7          6.3         3.3          4.7         1.6
##  8          4.9         2.4          3.3         1  
##  9          6.6         2.9          4.6         1.3
## 10          5.2         2.7          3.9         1.4
## # ... with 40 more rows, and 1 more variable:
## #   Species <fct>
## 
## [[3]]
## # A tibble: 50 x 5
##    Sepal.Length Sepal.Width Petal.Length Petal.Width
##           <dbl>       <dbl>        <dbl>       <dbl>
##  1          6.3         3.3          6           2.5
##  2          5.8         2.7          5.1         1.9
##  3          7.1         3            5.9         2.1
##  4          6.3         2.9          5.6         1.8
##  5          6.5         3            5.8         2.2
##  6          7.6         3            6.6         2.1
##  7          4.9         2.5          4.5         1.7
##  8          7.3         2.9          6.3         1.8
##  9          6.7         2.5          5.8         1.8
## 10          7.2         3.6          6.1         2.5
## # ... with 40 more rows, and 1 more variable:
## #   Species <fct>
## 
## attr(,"ptype")
## # A tibble: 0 x 5
## # ... with 5 variables: Sepal.Length <dbl>,
## #   Sepal.Width <dbl>, Petal.Length <dbl>,
## #   Petal.Width <dbl>, Species <fct>

简单点写,就是

## [[1]]
## # A tibble: 50 x 5
##    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
##  4          4.6         3.1          1.5         0.2
##  5          5           3.6          1.4         0.2
##  6          5.4         3.9          1.7         0.4
##  7          4.6         3.4          1.4         0.3
##  8          5           3.4          1.5         0.2
##  9          4.4         2.9          1.4         0.2
## 10          4.9         3.1          1.5         0.1
## # ... with 40 more rows, and 1 more variable:
## #   Species <fct>
## 
## [[2]]
## # A tibble: 50 x 5
##    Sepal.Length Sepal.Width Petal.Length Petal.Width
##           <dbl>       <dbl>        <dbl>       <dbl>
##  1          7           3.2          4.7         1.4
##  2          6.4         3.2          4.5         1.5
##  3          6.9         3.1          4.9         1.5
##  4          5.5         2.3          4           1.3
##  5          6.5         2.8          4.6         1.5
##  6          5.7         2.8          4.5         1.3
##  7          6.3         3.3          4.7         1.6
##  8          4.9         2.4          3.3         1  
##  9          6.6         2.9          4.6         1.3
## 10          5.2         2.7          3.9         1.4
## # ... with 40 more rows, and 1 more variable:
## #   Species <fct>
## 
## [[3]]
## # A tibble: 50 x 5
##    Sepal.Length Sepal.Width Petal.Length Petal.Width
##           <dbl>       <dbl>        <dbl>       <dbl>
##  1          6.3         3.3          6           2.5
##  2          5.8         2.7          5.1         1.9
##  3          7.1         3            5.9         2.1
##  4          6.3         2.9          5.6         1.8
##  5          6.5         3            5.8         2.2
##  6          7.6         3            6.6         2.1
##  7          4.9         2.5          4.5         1.7
##  8          7.3         2.9          6.3         1.8
##  9          6.7         2.5          5.8         1.8
## 10          7.2         3.6          6.1         2.5
## # ... with 40 more rows, and 1 more variable:
## #   Species <fct>
## 
## attr(,"ptype")
## # A tibble: 0 x 5
## # ... with 5 variables: Sepal.Length <dbl>,
## #   Sepal.Width <dbl>, Petal.Length <dbl>,
## #   Petal.Width <dbl>, Species <fct>

如果使用group_split(), 注意分组后,返回的是列表

## [[1]]
## # A tibble: 50 x 5
##    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
##  4          4.6         3.1          1.5         0.2
##  5          5           3.6          1.4         0.2
##  6          5.4         3.9          1.7         0.4
##  7          4.6         3.4          1.4         0.3
##  8          5           3.4          1.5         0.2
##  9          4.4         2.9          1.4         0.2
## 10          4.9         3.1          1.5         0.1
## # ... with 40 more rows, and 1 more variable:
## #   Species <fct>
## 
## [[2]]
## # A tibble: 50 x 5
##    Sepal.Length Sepal.Width Petal.Length Petal.Width
##           <dbl>       <dbl>        <dbl>       <dbl>
##  1          7           3.2          4.7         1.4
##  2          6.4         3.2          4.5         1.5
##  3          6.9         3.1          4.9         1.5
##  4          5.5         2.3          4           1.3
##  5          6.5         2.8          4.6         1.5
##  6          5.7         2.8          4.5         1.3
##  7          6.3         3.3          4.7         1.6
##  8          4.9         2.4          3.3         1  
##  9          6.6         2.9          4.6         1.3
## 10          5.2         2.7          3.9         1.4
## # ... with 40 more rows, and 1 more variable:
## #   Species <fct>
## 
## [[3]]
## # A tibble: 50 x 5
##    Sepal.Length Sepal.Width Petal.Length Petal.Width
##           <dbl>       <dbl>        <dbl>       <dbl>
##  1          6.3         3.3          6           2.5
##  2          5.8         2.7          5.1         1.9
##  3          7.1         3            5.9         2.1
##  4          6.3         2.9          5.6         1.8
##  5          6.5         3            5.8         2.2
##  6          7.6         3            6.6         2.1
##  7          4.9         2.5          4.5         1.7
##  8          7.3         2.9          6.3         1.8
##  9          6.7         2.5          5.8         1.8
## 10          7.2         3.6          6.1         2.5
## # ... with 40 more rows, and 1 more variable:
## #   Species <fct>
## 
## attr(,"ptype")
## # A tibble: 0 x 5
## # ... with 5 variables: Sepal.Length <dbl>,
## #   Sepal.Width <dbl>, Petal.Length <dbl>,
## #   Petal.Width <dbl>, Species <fct>

既然是列表,当然想到用前面讲到的purrr::map()家族

## [[1]]
## # A tibble: 2 x 5
##   term         estimate std.error statistic p.value
##   <chr>           <dbl>     <dbl>     <dbl>   <dbl>
## 1 (Intercept)     0.803    0.344       2.34  0.0238
## 2 Sepal.Length    0.132    0.0685      1.92  0.0607
## 
## [[2]]
## # A tibble: 2 x 5
##   term         estimate std.error statistic  p.value
##   <chr>           <dbl>     <dbl>     <dbl>    <dbl>
## 1 (Intercept)     0.185    0.514      0.360 7.20e- 1
## 2 Sepal.Length    0.686    0.0863     7.95  2.59e-10
## 
## [[3]]
## # A tibble: 2 x 5
##   term         estimate std.error statistic  p.value
##   <chr>           <dbl>     <dbl>     <dbl>    <dbl>
## 1 (Intercept)     0.610    0.417       1.46 1.50e- 1
## 2 Sepal.Length    0.750    0.0630     11.9  6.30e-16

上面这个代码,数据框分割成list, 处理完后再合并成数据框,难道不觉得折腾么? 为什么直接点? tidyverse不会让我们失望的,先看看group_map()

## [[1]]
## # A tibble: 2 x 5
##   term         estimate std.error statistic p.value
##   <chr>           <dbl>     <dbl>     <dbl>   <dbl>
## 1 (Intercept)     0.803    0.344       2.34  0.0238
## 2 Sepal.Length    0.132    0.0685      1.92  0.0607
## 
## [[2]]
## # A tibble: 2 x 5
##   term         estimate std.error statistic  p.value
##   <chr>           <dbl>     <dbl>     <dbl>    <dbl>
## 1 (Intercept)     0.185    0.514      0.360 7.20e- 1
## 2 Sepal.Length    0.686    0.0863     7.95  2.59e-10
## 
## [[3]]
## # A tibble: 2 x 5
##   term         estimate std.error statistic  p.value
##   <chr>           <dbl>     <dbl>     <dbl>    <dbl>
## 1 (Intercept)     0.610    0.417       1.46 1.50e- 1
## 2 Sepal.Length    0.750    0.0630     11.9  6.30e-16

数据框进来,然后分组,依次处理成一个个数据框,最后以列表形式(a list of tibble)输出。

事实上,group_map()是返回list形式,也就是说,可以是返回任何形式,(a list of tibble)是其中特殊形式。 可以看看下面这个

## [[1]]
## 
## Call:
## lm(formula = Petal.Length ~ Sepal.Length, data = .x)
## 
## Coefficients:
##  (Intercept)  Sepal.Length  
##        0.803         0.132  
## 
## 
## [[2]]
## 
## Call:
## lm(formula = Petal.Length ~ Sepal.Length, data = .x)
## 
## Coefficients:
##  (Intercept)  Sepal.Length  
##        0.185         0.686  
## 
## 
## [[3]]
## 
## Call:
## lm(formula = Petal.Length ~ Sepal.Length, data = .x)
## 
## Coefficients:
##  (Intercept)  Sepal.Length  
##         0.61          0.75

group_modify() 才是真正意义上的“数据框进、数据框出”。

为了大家方便查阅和记忆,我总结下表

函数 说明 常用组合 返回值 要求
map() 列表进、列表出 df %>%
group_split() %>%
map()
list
map_df() 列表进、数据框出 df %>%
group_split() %>%
map_df()
df
group_map() 数据框进、列表出 df %>%
group_by() %>%
group_map()
返回list(list1, list2, …)
特例list(df1, df2, …)
group_modify() 数据框进、数据框出 df %>%
group_by() %>%
group_modify()
返回grouped tibble .f返回df
walk 列表进 df %>%
group_split() %>%
walk()
side effects
group_walk() 数据框进 df %>%
group_by() %>%
group_walk()
side effects

我常用的批量出图的语句

18.4.2 其他group函数

group_nest(), group_data(), group_keys(), group_rows()

18.5 列名清理

数据框的列名,不要用有空格和中文。 如果拿到的原始数据中列比较多,手动修改麻烦,可以使用janitor::clean_names()函数

## Observations: 13
## Variables: 11
## $ `First Name`        <chr> "Jason", "Jason", "Ali...
## $ `Last Name`         <chr> "Bourne", "Bourne", "K...
## $ `Employee Status`   <chr> "Teacher", "Teacher", ...
## $ Subject             <chr> "PE", "Drafting", "Mus...
## $ `Hire Date`         <dbl> 39690, 39690, 37118, 2...
## $ `% Allocated`       <dbl> 0.75, 0.25, 1.00, 1.00...
## $ `Full time?`        <chr> "Yes", "Yes", "Yes", "...
## $ `do not edit! --->` <lgl> NA, NA, NA, NA, NA, NA...
## $ Certification...9   <chr> "Physical ed", "Physic...
## $ Certification...10  <chr> "Theater", "Theater", ...
## $ Certification...11  <lgl> NA, NA, NA, NA, NA, NA...
## Observations: 13
## Variables: 11
## $ first_name        <chr> "Jason", "Jason", "Alici...
## $ last_name         <chr> "Bourne", "Bourne", "Key...
## $ employee_status   <chr> "Teacher", "Teacher", "T...
## $ subject           <chr> "PE", "Drafting", "Music...
## $ hire_date         <dbl> 39690, 39690, 37118, 275...
## $ percent_allocated <dbl> 0.75, 0.25, 1.00, 1.00, ...
## $ full_time         <chr> "Yes", "Yes", "Yes", "Ye...
## $ do_not_edit       <lgl> NA, NA, NA, NA, NA, NA, ...
## $ certification_9   <chr> "Physical ed", "Physical...
## $ certification_10  <chr> "Theater", "Theater", "V...
## $ certification_11  <lgl> NA, NA, NA, NA, NA, NA, ...

18.6 缺失值检查与处理

18.6.1 purrr & dplyr 技巧

## $Ozone
## [1] 37
## 
## $Solar.R
## [1] 7
## 
## $Wind
## [1] 0
## 
## $Temp
## [1] 0
## 
## $Month
## [1] 0
## 
## $Day
## [1] 0

18.6.2 缺失值替换

18.7 标准化

如果所有的列,都是数值型

## Error in x - min(x): 二进列运算符中有非数值参数
  • 但这里数据中还有其他类型(fct, chr),所以这里 mutate_all() 会报错。
  • 这种情形,用mutate_if()