第 6 章 数据处理

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

library(dplyr)

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

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

我们依次介绍

6.1 mutate()

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

df <- data.frame(
      name = c("Alice", "Alice", "Bob", "Bob", "Carol", "Carol"),
      type = c("english", "math", "english", "math", "english", "math")
)

df
##    name    type
## 1 Alice english
## 2 Alice    math
## 3   Bob english
## 4   Bob    math
## 5 Carol english
## 6 Carol    math

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

score2020 <- c(80.2, 90.5, 92.2, 90.8, 82.5, 84.6)
score2020
## [1] 80.2 90.5 92.2 90.8 82.5 84.6

使用传统的方法

df$newscore <- score2020
df
##    name    type newscore
## 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

dplyr语法这样写

mutate(df, newscore = score2020)
##    name    type newscore
## 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() 函数

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

6.2 管道 %>%

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

c(1:10)
##  [1]  1  2  3  4  5  6  7  8  9 10
sum(c(1:10))
## [1] 55

与下面的写法是等价的,

c(1:10) %>% sum()
## [1] 55

这条语句的意思,向量c(1:10) 通过管道操作符 %>% ,传递到函数sum()的第一个参数位置,即sum(c(1:10)), 这个%>%管道操作符还是很形象的,

当对执行多个函数操作的时候,就显得格外方便,代码可读性更强。

sqrt(sum(abs(c(-10:10))))
## [1] 10.49
# sqrt(sum(abs(c(-10:10)))) 
c(-10:10) %>% abs() %>% sum() %>% sqrt()
## [1] 10.49

那么,上面增加学生成绩的语句mutate(df, newscore = score2020)就可以使用管道

# 等价于
df %>% mutate(newscore = score2020)
##    name    type newscore
## 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

是不是很赞?

df <- df %>% mutate(newscore = score2020)
df
##    name    type newscore
## 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()顾名思义选择,就是选择数据框的某一列,或者某几列

我们还是以学生成绩的数据框为例

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

  • 使用传统的方法
df["name"]
##    name
## 1 Alice
## 2 Alice
## 3   Bob
## 4   Bob
## 5 Carol
## 6 Carol
  • dplyr 的方法
df %>% select(name)
##    name
## 1 Alice
## 2 Alice
## 3   Bob
## 4   Bob
## 5 Carol
## 6 Carol
  • 如果选取多列,用dplyr 就只是再写一个就行了
df %>% select(name, newscore)
##    name newscore
## 1 Alice     80.2
## 2 Alice     90.5
## 3   Bob     92.2
## 4   Bob     90.8
## 5 Carol     82.5
## 6 Carol     84.6

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

df %>% select(-type)
##    name newscore
## 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分的同学筛选出来

df %>% filter(newscore >= 90)
##    name    type newscore
## 1 Alice    math     90.5
## 2   Bob english     92.2
## 3   Bob    math     90.8

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

df %>% filter(type == "english", newscore >= 90)
##   name    type newscore
## 1  Bob english     92.2

6.5 summarise()统计

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

df %>% summarise( mean_score = mean(newscore))
##   mean_score
## 1       86.8

比如,计算所有同学的考试成绩的标准差

df %>% summarise( mean_score = sd(newscore))
##   mean_score
## 1      5.015

还同时完成多个统计

df %>% summarise(
  mean_score = mean(newscore),
  median_score = median(newscore),
  n = n(),
  sum = sum(newscore)
)
##   mean_score median_score n   sum
## 1       86.8        87.55 6 520.8

注意,mutate(), select()filter()是在原数据框的基础上增减, 而summarise()返回的是一个新的数据框。

6.6 group_by()分组

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

df %>% 
  group_by(name) %>% 
  summarise( 
    mean_score = mean(newscore),
   sd_score = sd(newscore)
  )
## # A tibble: 3 x 3
##   name  mean_score sd_score
##   <chr>      <dbl>    <dbl>
## 1 Alice       85.4    7.28 
## 2 Bob         91.5    0.990
## 3 Carol       83.6    1.48

6.7 arrange()排序

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

df %>% arrange(newscore)
##    name    type newscore
## 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

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

df %>% arrange(-newscore)
##    name    type newscore
## 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

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

df %>% arrange(desc(newscore))
##    name    type newscore
## 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

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

df %>% 
  arrange(type, desc(newscore))
##    name    type newscore
## 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

df1 <- df %>% 
  group_by(name) %>% 
  summarise( mean_score = mean(newscore) )

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

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

df2 <- tibble(
      name = c("Alice", "Bob"),
      age =  c(12, 13)
)

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

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

left_join(df1, df2, 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
# 
df1 %>% left_join(df2, 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, 大家想想为什么呢?

6.9 right_join()

我们再试试right_join()

df1 %>% right_join(df2, by = "name")
## # 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.10 延伸阅读

Download nycflights.Rmd