第 6 章 数据处理

Hadley Wickhamt提出了数据科学tidy原则,我结合自己的理解,tidy思想体现在:

  • 一切都是数据框,任何数据都可以规整
  • 数据框的一列代表一个变量,数据框的一行代表一次观察
  • 函数处理数据时,数据框进数据框出

本章我们介绍tidyverse里数据处理的神器dplyr宏包。首先,我们加载该宏包

dplyr 定义了数据处理的规范语法,其中主要包含以下七个主要的函数。

  • mutate(), select(), filter()
  • summarise(), group_by(), arrange()
  • left_join(), right_join()full_join()

我们依次介绍

6.1 mutate()

假定我们有一数据框,包含三位学生的英语和数学

## # A tibble: 6 x 2
##   name  type   
##   <chr> <chr>  
## 1 Alice english
## 2 Alice math   
## 3 Bob   english
## 4 Bob   math   
## 5 Carol english
## 6 Carol math

这里有他们的考试成绩, 我们想增加到数据框里去

## [1] 80.2 90.5 92.2 90.8 82.5 84.6

语法这样写

## # A tibble: 6 x 3
##   name  type    score
##   <chr> <chr>   <dbl>
## 1 Alice english  80.2
## 2 Alice math     90.5
## 3 Bob   english  92.2
## 4 Bob   math     90.8
## 5 Carol english  82.5
## 6 Carol math     84.6

mutate() 函数第一参数是我们要处理的数据框,比如这里的df,紧跟着的是score = s,等号左边的score是我们打算创建一个新列,而取的列名;等号右边是装着学生成绩的向量(注意,向量 的长度要与数据框的行数相等,比如这里长度都是6)

6.2 管道 %>%

这里有必要介绍下管道操作符 %>% .

## [1] 55

与下面的写法是等价的,

## [1] 55

这条语句的意思,向量c(1:10) 通过管道操作符 %>% ,传递到函数sum()的第一个参数位置,即sum(c(1:10)), 这个%>%管道操作符还是很形象的, 且对执行多个函数操作的时候,就显得格外方便,代码可读性更强。

## [1] 10.49

那么,上面增加学生成绩的语句mutate(df, score = s)就可以写为

## # A tibble: 6 x 3
##   name  type    score
##   <chr> <chr>   <dbl>
## 1 Alice english  80.2
## 2 Alice math     90.5
## 3 Bob   english  92.2
## 4 Bob   math     90.8
## 5 Carol english  82.5
## 6 Carol math     84.6

是不是很赞?

6.3 select()

select()顾名思义选择,就是选择数据框的某一列,我们还是以学生成绩的数据框为例

## # A tibble: 6 x 3
##   name  type    score
##   <chr> <chr>   <dbl>
## 1 Alice english  80.2
## 2 Alice math     90.5
## 3 Bob   english  92.2
## 4 Bob   math     90.8
## 5 Carol english  82.5
## 6 Carol math     84.6

我们可以选择name列, 结果是只有一列的数据框(仍然数据框喔)

## # A tibble: 6 x 1
##   name 
##   <chr>
## 1 Alice
## 2 Alice
## 3 Bob  
## 4 Bob  
## 5 Carol
## 6 Carol

如果选取多列,就再写一个就行了

## # A tibble: 6 x 2
##   name  score
##   <chr> <dbl>
## 1 Alice  80.2
## 2 Alice  90.5
## 3 Bob    92.2
## 4 Bob    90.8
## 5 Carol  82.5
## 6 Carol  84.6

如果不想要某列, 可以在变量前面加-, 结果与上面的一样

## # A tibble: 6 x 2
##   name  score
##   <chr> <dbl>
## 1 Alice  80.2
## 2 Alice  90.5
## 3 Bob    92.2
## 4 Bob    90.8
## 5 Carol  82.5
## 6 Carol  84.6

6.4 filter()

select是列方向的选择, 我们还可以对数据行方向的选择和筛选,比如这里把成绩高于90分的同学筛选出来

## # A tibble: 3 x 3
##   name  type    score
##   <chr> <chr>   <dbl>
## 1 Alice math     90.5
## 2 Bob   english  92.2
## 3 Bob   math     90.8

也可以限定多个条件进行筛选, 英语成绩高于90分的筛选出来

## # A tibble: 1 x 3
##   name  type    score
##   <chr> <chr>   <dbl>
## 1 Bob   english  92.2

6.5 summarise()统计

summarise()主要用于统计,往往与其他函数配合使用,比如计算所有同学的考试成绩的均值

## # A tibble: 1 x 1
##   mean_score
##        <dbl>
## 1       86.8

还同时完成多个统计

## # A tibble: 1 x 2
##   mean_score median_score
##        <dbl>        <dbl>
## 1       86.8         87.6

6.6 group_by()分组

事实上,summarise()往往配合group_by()一起使用,即,先分组再统计。比如,我们想统计每个学生的平均成绩,那么就需要先按学生name分组,然后求平均

## # A tibble: 3 x 2
##   name  mean_score
##   <chr>      <dbl>
## 1 Alice       85.4
## 2 Bob         91.5
## 3 Carol       83.6

6.7 arrange()排序

这个很好理解的。比如我们按照考试成绩从低到高排序,然后输出

## # A tibble: 6 x 3
##   name  type    score
##   <chr> <chr>   <dbl>
## 1 Alice english  80.2
## 2 Carol english  82.5
## 3 Carol math     84.6
## 4 Alice math     90.5
## 5 Bob   math     90.8
## 6 Bob   english  92.2

如果从高到低排序呢,有两种方法:

## # A tibble: 6 x 3
##   name  type    score
##   <chr> <chr>   <dbl>
## 1 Bob   english  92.2
## 2 Bob   math     90.8
## 3 Alice math     90.5
## 4 Carol math     84.6
## 5 Carol english  82.5
## 6 Alice english  80.2

写成下面这种形式也是降序排列,但可读性更强些

## # A tibble: 6 x 3
##   name  type    score
##   <chr> <chr>   <dbl>
## 1 Bob   english  92.2
## 2 Bob   math     90.8
## 3 Alice math     90.5
## 4 Carol math     84.6
## 5 Carol english  82.5
## 6 Alice english  80.2

也可对多个变量先后排序。先按学科排,然后按照成绩从高到底排序

## # A tibble: 6 x 3
##   name  type    score
##   <chr> <chr>   <dbl>
## 1 Bob   english  92.2
## 2 Carol english  82.5
## 3 Alice english  80.2
## 4 Bob   math     90.8
## 5 Alice math     90.5
## 6 Carol math     84.6

6.8 left_join

数据框合并,假定我们已经统计了每个同学的平均成绩,存放在df1

## # A tibble: 3 x 2
##   name  mean_score
##   <chr>      <dbl>
## 1 Alice       85.4
## 2 Bob         91.5
## 3 Carol       83.6

我们有新一个数据框df2,包含同学们的年龄信息

## # A tibble: 2 x 2
##   name    age
##   <chr> <dbl>
## 1 Alice    12
## 2 Bob      13

可以用 left_join把两个数据框df1df2,合并连接再一起, 两个数据框是通过姓名name连接的,因此需要指定by = "name"

## # A tibble: 3 x 3
##   name  mean_score   age
##   <chr>      <dbl> <dbl>
## 1 Alice       85.4    12
## 2 Bob         91.5    13
## 3 Carol       83.6    NA

大家注意到最后一行Carol的年龄是NA, 大家想想为什么呢?

我们再试试right_join()

## # A tibble: 2 x 3
##   name  mean_score   age
##   <chr>      <dbl> <dbl>
## 1 Alice       85.4    12
## 2 Bob         91.5    13

Carol同学的信息没有了? 大家想想又为什么呢?

事实上,答案就在函数的名字上,left_join()是左合并,即以左边数据框df1中的学生姓名name为准,在右边数据框df2里,有AliceBob的年龄,那么就对应合并过来,没有Carol,就为缺失值NA

left_join()是右合并,即以右边数据框df2中的学生姓名name为准,只有AliceBob,因此而df1只需要把AliceBob的信息粘过来。

6.9 延伸阅读

Download nycflights.Rmd