2017年11月9日

变换数据

可视化是洞察数据的重要方式,但很多情况下可能会碰到数据的内容、形式不能直接可视化。而需要创建一些新的变量或统计值、重新命名变量、重新排序观测等,使得数据更容易被使用。
学习使用dplyr包进行数据变换。

library(dplyr)
Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union

数据集——flights

纽约2013年的航班离港数据

## install.packages("nycflights13")
library(nycflights13)
data("flights")

flights
# A tibble: 336,776 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1  2013     1     1      517            515         2      830
 2  2013     1     1      533            529         4      850
 3  2013     1     1      542            540         2      923
 4  2013     1     1      544            545        -1     1004
 5  2013     1     1      554            600        -6      812
 6  2013     1     1      554            558        -4      740
 7  2013     1     1      555            600        -5      913
 8  2013     1     1      557            600        -3      709
 9  2013     1     1      557            600        -3      838
10  2013     1     1      558            600        -2      753
# ... with 336,766 more rows, and 12 more variables: sched_arr_time <int>,
#   arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
#   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
#   minute <dbl>, time_hour <dttm>

tibble是dplyr包中专用的数据框(data frame),与基本的数据框仅有略微的不同。

  • int表示integers

  • dbl表示doubles或real numbers

  • chr表示character vectors或strings

  • dttm表示date-times (a date + a time)

  • lgl表示logical (TRUE或FALSE)

  • fctr表示factors

  • date表示dates

dplyr基础

掌握以下5个函数,可以处理大多数的数据变换问题。

  • filter(),通过观测值选取行

  • arrange(),重新排列行

  • select(),通过变量名选择列

  • mutate(),通过既有变量创建新的变量

  • summarise()summarize(),将多个值进行统计合并

上述5函数都可以和 group_by() 合并使用,以改变函数的应用范围,从原先的整个数据集到各个分组。

所有函数的使用方法相同

new_df <- filter(df, ...)

  • 第一个参数是数据框

  • 接下来的参数使用变量名称描述需要对数据框做什么

  • 输出结果是数据框

使用filter()选择行

filter()可以基于变量的值选取子集。比如选择1月1日的航班。

filter(flights, month == 1, day == 1)
# A tibble: 842 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1  2013     1     1      517            515         2      830
 2  2013     1     1      533            529         4      850
 3  2013     1     1      542            540         2      923
 4  2013     1     1      544            545        -1     1004
 5  2013     1     1      554            600        -6      812
 6  2013     1     1      554            558        -4      740
 7  2013     1     1      555            600        -5      913
 8  2013     1     1      557            600        -3      709
 9  2013     1     1      557            600        -3      838
10  2013     1     1      558            600        -2      753
# ... with 832 more rows, and 12 more variables: sched_arr_time <int>,
#   arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
#   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
#   minute <dbl>, time_hour <dttm>

filter语句将返回新的数据框而不会修改输入的数据框。因此如果想保存结果, 需要使用<-将结果赋值到新的数据框,否则会直接输出结果。

jan1 <- filter(flights, month == 1, day == 1)

如果既要输出结果,又要赋值, 可以将语句用括号括起来。

(dec25 <- filter(flights, month == 12, day == 25))
# A tibble: 719 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1  2013    12    25      456            500        -4      649
 2  2013    12    25      524            515         9      805
 3  2013    12    25      542            540         2      832
 4  2013    12    25      546            550        -4     1022
 5  2013    12    25      556            600        -4      730
 6  2013    12    25      557            600        -3      743
 7  2013    12    25      557            600        -3      818
 8  2013    12    25      559            600        -1      855
 9  2013    12    25      559            600        -1      849
10  2013    12    25      600            600         0      850
# ... with 709 more rows, and 12 more variables: sched_arr_time <int>,
#   arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
#   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
#   minute <dbl>, time_hour <dttm>

比较运算符

比较运算符:>>=<<=!===,与filter()结合使用。
注意在判断是否相等时不要使用=,而应使用==

filter(flights, month = 1)
Error: `month` (`month = 1`) must not be named, do you need `==`?

另外需要注意

sqrt(2) ^ 2 == 2
[1] FALSE
1/49 * 49 == 1
[1] FALSE

上述结果是由于变量保存的位数有限,正确的做法是用near()

near(sqrt(2) ^ 2,  2)
[1] TRUE
near(1 / 49 * 49, 1)
[1] TRUE

逻辑运算符

掌握逻辑运算符 &(和),|(或),!(非)。
比如选择11月或12月的航班。

filter(flights, month == 11 | month == 12)

注意不要错误的使用以下语句,否则会返回1月的航班。

filter(flights, month == 11 | 12)

更加简洁的写法是x %in% y

nov_dec <- filter(flights, month %in% c(11, 12))

缺失值

如果数据中有缺失值NA`(not availables),任何运算结果都是NA。

NA > 5
[1] NA
10 == NA
[1] NA
NA + 10
[1] NA
NA / 2
[1] NA

需要特别注意

NA == NA
[1] NA

如果需要判断某一值是否为缺省值,需要使用is.na()

is.na(x)

filter()只选择结果为TRUE的行,而排除结果为FALSENA的行。如果要保存NA的行,需要显性的声明。

df <- data.frame(x = c(1, NA, 3))
filter(df, x > 1)
  x
1 3
filter(df, is.na(x) | x > 1)
   x
1 NA
2  3

练习

  1. 寻找下列航班
    • 到达延误两小时以上
    • 飞往休斯顿 (IAH或HOU)
    • 由United(UA)或American(AA)或Delta(DL)航空公司运营
    • 在夏季离港(July,August,September)
    • 出发没有延误但到达延误两小时以上
    • 出发延误在1小时以上,但在飞行中挽回了30分钟以上的延误
    • 0点到6点间离开的 (包含0点和6点)
  2. between()是dplyr包中一个非常有用的函数,尝试自学该函数简化问题1中的部分语句?
  3. 有多少航班的实际出发时间缺失,这些航班中还有哪些列有缺失值,分别代表什么含义?

使用arrange()排列行

使用变量名依次进行升序排列。

arrange(flights, year, month, day)
# A tibble: 336,776 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1  2013     1     1      517            515         2      830
 2  2013     1     1      533            529         4      850
 3  2013     1     1      542            540         2      923
 4  2013     1     1      544            545        -1     1004
 5  2013     1     1      554            600        -6      812
 6  2013     1     1      554            558        -4      740
 7  2013     1     1      555            600        -5      913
 8  2013     1     1      557            600        -3      709
 9  2013     1     1      557            600        -3      838
10  2013     1     1      558            600        -2      753
# ... with 336,766 more rows, and 12 more variables: sched_arr_time <int>,
#   arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
#   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
#   minute <dbl>, time_hour <dttm>

使用desc()进行降序排列。

arrange(flights, desc(arr_delay))
# A tibble: 336,776 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1  2013     1     9      641            900      1301     1242
 2  2013     6    15     1432           1935      1137     1607
 3  2013     1    10     1121           1635      1126     1239
 4  2013     9    20     1139           1845      1014     1457
 5  2013     7    22      845           1600      1005     1044
 6  2013     4    10     1100           1900       960     1342
 7  2013     3    17     2321            810       911      135
 8  2013     7    22     2257            759       898      121
 9  2013    12     5      756           1700       896     1058
10  2013     5     3     1133           2055       878     1250
# ... with 336,766 more rows, and 12 more variables: sched_arr_time <int>,
#   arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
#   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
#   minute <dbl>, time_hour <dttm>

缺失值通常在排序的最后。

df <- data.frame(x = c(5, 2, NA))
arrange(df, x)
   x
1  2
2  5
3 NA
arrange(df, desc(x))
   x
1  5
2  2
3 NA

练习

  1. 按照延误最大进行排序。
  2. 按照离开最早进行排序。
  3. 按照飞行速度最快进行排序。
  4. 按照飞行时间从长到短排序。

使用select()选择列

很多情况下数据可能有数十甚至数百列。因此需要从中选择你所关心的列以缩小数据。select()可以根据变量名快速选择列。

select(flights, year, month, day)
# A tibble: 336,776 x 3
    year month   day
   <int> <int> <int>
 1  2013     1     1
 2  2013     1     1
 3  2013     1     1
 4  2013     1     1
 5  2013     1     1
 6  2013     1     1
 7  2013     1     1
 8  2013     1     1
 9  2013     1     1
10  2013     1     1
# ... with 336,766 more rows

选择year和day之间的所有变量。

select(flights, year:day)
# A tibble: 336,776 x 3
    year month   day
   <int> <int> <int>
 1  2013     1     1
 2  2013     1     1
 3  2013     1     1
 4  2013     1     1
 5  2013     1     1
 6  2013     1     1
 7  2013     1     1
 8  2013     1     1
 9  2013     1     1
10  2013     1     1
# ... with 336,766 more rows

选择year和day之外的所有变量。

select(flights, -(year:day))
# A tibble: 336,776 x 16
   dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
      <int>          <int>     <dbl>    <int>          <int>     <dbl>
 1      517            515         2      830            819        11
 2      533            529         4      850            830        20
 3      542            540         2      923            850        33
 4      544            545        -1     1004           1022       -18
 5      554            600        -6      812            837       -25
 6      554            558        -4      740            728        12
 7      555            600        -5      913            854        19
 8      557            600        -3      709            723       -14
 9      557            600        -3      838            846        -8
10      558            600        -2      753            745         8
# ... with 336,766 more rows, and 10 more variables: carrier <chr>,
#   flight <int>, tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>,
#   distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

下列函数可以帮助使用select()

  • starts_with("abc"):匹配以"abc"开头的变量名

  • ends_with("xyz"):匹配以"xyz"结尾的变量名

  • contains("ijk"):匹配包含"ijk"的变量名

  • matches("(.)\\1"):选择符合正则表达式的变量名

  • num_range("x", 1:3):匹配x1x2x3

select()everything()合并使用,可以把关心的变量移动到数据框的前几列。

select(flights, time_hour, air_time, everything())
# A tibble: 336,776 x 19
             time_hour air_time  year month   day dep_time sched_dep_time
                <dttm>    <dbl> <int> <int> <int>    <int>          <int>
 1 2013-01-01 05:00:00      227  2013     1     1      517            515
 2 2013-01-01 05:00:00      227  2013     1     1      533            529
 3 2013-01-01 05:00:00      160  2013     1     1      542            540
 4 2013-01-01 05:00:00      183  2013     1     1      544            545
 5 2013-01-01 06:00:00      116  2013     1     1      554            600
 6 2013-01-01 05:00:00      150  2013     1     1      554            558
 7 2013-01-01 06:00:00      158  2013     1     1      555            600
 8 2013-01-01 06:00:00       53  2013     1     1      557            600
 9 2013-01-01 06:00:00      140  2013     1     1      557            600
10 2013-01-01 06:00:00      138  2013     1     1      558            600
# ... with 336,766 more rows, and 12 more variables: dep_delay <dbl>,
#   arr_time <int>, sched_arr_time <int>, arr_delay <dbl>, carrier <chr>,
#   flight <int>, tailnum <chr>, origin <chr>, dest <chr>, distance <dbl>,
#   hour <dbl>, minute <dbl>

利用remane()对变量进行重命名。

rename(flights, tail_num = tailnum)
# A tibble: 336,776 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1  2013     1     1      517            515         2      830
 2  2013     1     1      533            529         4      850
 3  2013     1     1      542            540         2      923
 4  2013     1     1      544            545        -1     1004
 5  2013     1     1      554            600        -6      812
 6  2013     1     1      554            558        -4      740
 7  2013     1     1      555            600        -5      913
 8  2013     1     1      557            600        -3      709
 9  2013     1     1      557            600        -3      838
10  2013     1     1      558            600        -2      753
# ... with 336,766 more rows, and 12 more variables: sched_arr_time <int>,
#   arr_delay <dbl>, carrier <chr>, flight <int>, tail_num <chr>,
#   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
#   minute <dbl>, time_hour <dttm>

练习

  1. 尝试使用多种方法选择dep_timedep_delayarr_timearr_delay列。
  2. 尝试在select()函数参数中重复选择同一变量名,看看会发生什么?
  3. 尝试one_of()函数的用法,看看与下面的向量vars一起使用能实现什么功能?

    vars <- c("year", "month", "day", "dep_delay", "arr_delay")
  4. 看看下面语句的运行结果,尝试改变contains()函数中大小写的默认设置?

    select(flights, contains("TIME"))
    select(flights, contains("time"))

使用mutate()增加新的变量

flights_sml <- select(flights, year:day, ends_with("delay"), 
                      distance, air_time)
(flights_sml <- mutate(flights_sml, gain = arr_delay - dep_delay, 
                       speed = distance / air_time * 60))
# A tibble: 336,776 x 9
    year month   day dep_delay arr_delay distance air_time  gain    speed
   <int> <int> <int>     <dbl>     <dbl>    <dbl>    <dbl> <dbl>    <dbl>
 1  2013     1     1         2        11     1400      227     9 370.0441
 2  2013     1     1         4        20     1416      227    16 374.2731
 3  2013     1     1         2        33     1089      160    31 408.3750
 4  2013     1     1        -1       -18     1576      183   -17 516.7213
 5  2013     1     1        -6       -25      762      116   -19 394.1379
 6  2013     1     1        -4        12      719      150    16 287.6000
 7  2013     1     1        -5        19     1065      158    24 404.4304
 8  2013     1     1        -3       -14      229       53   -11 259.2453
 9  2013     1     1        -3        -8      944      140    -5 404.5714
10  2013     1     1        -2         8      733      138    10 318.6957
# ... with 336,766 more rows

可以直接引用刚定义的变量名称。

(flights_sml <- mutate(flights_sml, gain = arr_delay - dep_delay, 
                       hours = air_time / 60, gain_per_hour = gain / hours))
# A tibble: 336,776 x 11
    year month   day dep_delay arr_delay distance air_time  gain    speed
   <int> <int> <int>     <dbl>     <dbl>    <dbl>    <dbl> <dbl>    <dbl>
 1  2013     1     1         2        11     1400      227     9 370.0441
 2  2013     1     1         4        20     1416      227    16 374.2731
 3  2013     1     1         2        33     1089      160    31 408.3750
 4  2013     1     1        -1       -18     1576      183   -17 516.7213
 5  2013     1     1        -6       -25      762      116   -19 394.1379
 6  2013     1     1        -4        12      719      150    16 287.6000
 7  2013     1     1        -5        19     1065      158    24 404.4304
 8  2013     1     1        -3       -14      229       53   -11 259.2453
 9  2013     1     1        -3        -8      944      140    -5 404.5714
10  2013     1     1        -2         8      733      138    10 318.6957
# ... with 336,766 more rows, and 2 more variables: hours <dbl>,
#   gain_per_hour <dbl>

如果仅需要保留新生成的变量,可以使用transmute()

transmute(flights, gain = arr_delay - dep_delay, 
          hours = air_time / 60, gain_per_hour = gain / hours)
# A tibble: 336,776 x 3
    gain     hours gain_per_hour
   <dbl>     <dbl>         <dbl>
 1     9 3.7833333      2.378855
 2    16 3.7833333      4.229075
 3    31 2.6666667     11.625000
 4   -17 3.0500000     -5.573770
 5   -19 1.9333333     -9.827586
 6    16 2.5000000      6.400000
 7    24 2.6333333      9.113924
 8   -11 0.8833333    -12.452830
 9    -5 2.3333333     -2.142857
10    10 2.3000000      4.347826
# ... with 336,766 more rows

可以用于计算新的变量的函数包括:

  • 算术运算符:+-*/^。比如 air_time / 60hours * 60 + minute

  • 算数运算符可以和汇总函数一起使用。比如x / sum(x)计算在总量中所占的比例,y - mean(y)计算与均值的差。

  • 模运算:%/%整数除法,%%取余,即x == y * (x %/% y) + (x %% y)

  • 模运算可以将整数分割。比如使用dep_time计算hourminute

    transmute(flights, dep_time, hour = dep_time %/% 100, 
              minute = dep_time %% 100)
  • 对数: log()log2()log10()

  • 偏移:lead()lag()可以引用同一变量在该行观测值之前或之后的观测值。比如x - lag(x)计算该行观测值与上一行的观测值差异 ,x != lag(x)寻找观测值在哪一行发生了改变。
    后面还会讲到与group_by()函数的结合使用。
(x <- 1:10)
 [1]  1  2  3  4  5  6  7  8  9 10
lag(x)
 [1] NA  1  2  3  4  5  6  7  8  9
lead(x)
 [1]  2  3  4  5  6  7  8  9 10 NA

  • 累加统计:cumsum()cumprod()cummin()cummax()cummean()分别用于累加求和,求乘积,求最小值,求最大值,求均值。
x
 [1]  1  2  3  4  5  6  7  8  9 10
cumsum(x)
 [1]  1  3  6 10 15 21 28 36 45 55
cummean(x)
 [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5

  • 逻辑比较:<<=>>=!=

  • 排序:排序函数很多,最常用的是min_rank()。默认从小到大排序,使用desc(x)可改为从大到小排序。

y <- c(1, 2, 2, NA, 3, 4)
min_rank(y)
[1]  1  2  2 NA  4  5
min_rank(desc(y))
[1]  5  3  3 NA  2  1
  • 其他排序函数包括:row_number()dense_rank()percent_rank()ntile(),可作了解。

y
[1]  1  2  2 NA  3  4
row_number(y)
[1]  1  2  3 NA  4  5
dense_rank(y)
[1]  1  2  2 NA  3  4
percent_rank(y)
[1] 0.00 0.25 0.25   NA 0.75 1.00

练习

  1. 尽管dep_timesched_dep_time变量非常易读(比如将6:50记为650),但它们实际并不是连续变量, 不能直接用于计算,尝试将其转变为从0:00开始的分钟数(即将6:50记为6*60+50=410)。
  2. 比较air_timearr_time - dep_time,预期的结果和实际的结果有什么区别,如何修正?
  3. 比较dep_timesched_dep_timedep_delay,它们之间的关系是什么?
  4. 使用排序函数寻找10个延误最大的航班,注意min_rank()row_number()dense_rank()函数的区别。
  5. 自己探索R里面有哪些三角函数。

使用summarise()进行分组统计

summarise()summarize()可以将整个数据框合并统计到单独的行。

summarise(flights, delay = mean(dep_delay, na.rm = TRUE))
# A tibble: 1 x 1
     delay
     <dbl>
1 12.63907

summarise()group_by()组合使用可以将统计单元从整个数框变为每个分组。比如将flights数据按天分组后,同样应用summarise()函数,可以得到每天的平均延误。

by_day <- group_by(flights, year, month, day)
summarise(by_day, delay = mean(dep_delay, na.rm = TRUE))
# A tibble: 365 x 4
# Groups:   year, month [?]
    year month   day     delay
   <int> <int> <int>     <dbl>
 1  2013     1     1 11.548926
 2  2013     1     2 13.858824
 3  2013     1     3 10.987832
 4  2013     1     4  8.951595
 5  2013     1     5  5.732218
 6  2013     1     6  7.148014
 7  2013     1     7  5.417204
 8  2013     1     8  2.553073
 9  2013     1     9  2.276477
10  2013     1    10  2.844995
# ... with 355 more rows

使用pipe合并多步运算

分析从纽约机场出发到每个目的地机场的航班数量、平均距离和平均延误。
注意过滤掉噪音点和Honolulu机场(HNL),由于该机场到纽约机场的距离过远。

by_dest <- group_by(flights, dest)
delay <- summarise(by_dest, count = n(), 
                   dist = mean(distance, na.rm = TRUE), 
                   delay = mean(arr_delay, na.rm = TRUE))
(delay <- filter(delay, count > 20, dest != "HNL"))
# A tibble: 96 x 4
    dest count      dist     delay
   <chr> <int>     <dbl>     <dbl>
 1   ABQ   254 1826.0000  4.381890
 2   ACK   265  199.0000  4.852273
 3   ALB   439  143.0000 14.397129
 4   ATL 17215  757.1082 11.300113
 5   AUS  2439 1514.2530  6.019909
 6   AVL   275  583.5818  8.003831
 7   BDL   443  116.0000  7.048544
 8   BGR   375  378.0000  8.027933
 9   BHM   297  865.9966 16.877323
10   BNA  6333  758.2135 11.812459
# ... with 86 more rows

观察距离和延误的关系。

ggplot(data = delay, mapping = aes(x = dist, y = delay)) + 
    geom_point(aes(size = count), alpha = 1/3) + 
    geom_smooth(se = FALSE)

上代码需要生成一些存储中间过程的数据框,代码不够简洁,且存储这些数据框会占用不必要的内存。
可以用pipe(%>%)解决上述问题。

(delays <- flights %>% group_by(dest) %>% 
    summarise(count = n(), dist = mean(distance, na.rm = TRUE),
              delay = mean(arr_delay, na.rm = TRUE)) %>% 
    filter(count > 20, dest != "HNL"))
# A tibble: 96 x 4
    dest count      dist     delay
   <chr> <int>     <dbl>     <dbl>
 1   ABQ   254 1826.0000  4.381890
 2   ACK   265  199.0000  4.852273
 3   ALB   439  143.0000 14.397129
 4   ATL 17215  757.1082 11.300113
 5   AUS  2439 1514.2530  6.019909
 6   AVL   275  583.5818  8.003831
 7   BDL   443  116.0000  7.048544
 8   BGR   375  378.0000  8.027933
 9   BHM   297  865.9966 16.877323
10   BNA  6333  758.2135 11.812459
# ... with 86 more rows

缺失值

如果不用na.rm参数会发生什么?只要输入中包含缺失值,任何聚合函数的输出都会为缺失值。
dep_delay中的缺失值表示被取消的航班。

flights %>% group_by(year, month, day) %>% 
    summarise(mean = mean(dep_delay))
# A tibble: 365 x 4
# Groups:   year, month [?]
    year month   day  mean
   <int> <int> <int> <dbl>
 1  2013     1     1    NA
 2  2013     1     2    NA
 3  2013     1     3    NA
 4  2013     1     4    NA
 5  2013     1     5    NA
 6  2013     1     6    NA
 7  2013     1     7    NA
 8  2013     1     8    NA
 9  2013     1     9    NA
10  2013     1    10    NA
# ... with 355 more rows

所有聚合函数都有na.rm参数。

flights %>% group_by(year, month, day) %>% 
    summarise(mean = mean(dep_delay, na.rm = TRUE))
# A tibble: 365 x 4
# Groups:   year, month [?]
    year month   day      mean
   <int> <int> <int>     <dbl>
 1  2013     1     1 11.548926
 2  2013     1     2 13.858824
 3  2013     1     3 10.987832
 4  2013     1     4  8.951595
 5  2013     1     5  5.732218
 6  2013     1     6  7.148014
 7  2013     1     7  5.417204
 8  2013     1     8  2.553073
 9  2013     1     9  2.276477
10  2013     1    10  2.844995
# ... with 355 more rows

也可以先排除缺失值,保存到新的数据框,再进行后续分析,比如计算平均延误。

not_cancelled <- flights %>% 
    filter(!is.na(dep_delay), !is.na(arr_delay))
not_cancelled %>% group_by(year, month, day) %>% 
    summarise(mean = mean(dep_delay))
# A tibble: 365 x 4
# Groups:   year, month [?]
    year month   day      mean
   <int> <int> <int>     <dbl>
 1  2013     1     1 11.435620
 2  2013     1     2 13.677802
 3  2013     1     3 10.907778
 4  2013     1     4  8.965859
 5  2013     1     5  5.732218
 6  2013     1     6  7.145959
 7  2013     1     7  5.417204
 8  2013     1     8  2.558296
 9  2013     1     9  2.301232
10  2013     1    10  2.844995
# ... with 355 more rows

计数

使用聚合函数时,通常会选择包含计数n()或者非缺失值计数sum(!is.na(x)),避免结论是从很少的数据中得出。
比如寻找平均延误最大的飞机(使用机尾编号tail number区分)。

(delays <- not_cancelled %>% group_by(tailnum) %>% 
    summarise(delay = mean(arr_delay)))
# A tibble: 4,037 x 2
   tailnum      delay
     <chr>      <dbl>
 1  D942DN 31.5000000
 2  N0EGMQ  9.9829545
 3  N10156 12.7172414
 4  N102UW  2.9375000
 5  N103US -6.9347826
 6  N104UW  1.8043478
 7  N10575 20.6914498
 8  N105UW -0.2666667
 9  N107US -5.7317073
10  N108UW -1.2500000
# ... with 4,027 more rows

ggplot(data = delays, mapping = aes(x = delay)) + 
    geom_freqpoly(binwidth = 10)

可以看出有些飞机的平均延误到了5个小时(300分钟)!

绘制飞机执行的航班数量和平均延误的散点图。可以看出,执行航班数量少时,平均延误波动非常大。

delays <- not_cancelled %>% group_by(tailnum) %>% 
    summarise(delay = mean(arr_delay, na.rm = TRUE), n = n())
ggplot(data = delays, mapping = aes(x = n, y = delay)) + 
    geom_point(alpha = 1/10)

通过排除掉样本量少的观测值,更好的关注数据分布模式而消除极端值的影响。

delays %>% filter(n > 25) %>% 
    ggplot(mapping = aes(x = n, y = delay)) + geom_point(alpha = 1/10)

NBA球员得分,也需要排除样本量很少的点。

有用的聚合函数

除了mean()n()sum(),R还有很多有用的聚合函数。比如由于均值mean()易受极端值的影响,可以用中位数median()描述数据的中间位置。

另外也可以将聚合函数应用于数据的子集。

not_cancelled %>% group_by(year, month, day) %>% 
    summarise(avg_delay1 = mean(arr_delay), 
              avg_delay2 = mean(arr_delay[arr_delay > 0]))
# A tibble: 365 x 5
# Groups:   year, month [?]
    year month   day avg_delay1 avg_delay2
   <int> <int> <int>      <dbl>      <dbl>
 1  2013     1     1 12.6510229   32.48156
 2  2013     1     2 12.6928879   32.02991
 3  2013     1     3  5.7333333   27.66087
 4  2013     1     4 -1.9328194   28.30976
 5  2013     1     5 -1.5258020   22.55882
 6  2013     1     6  4.2364294   24.37270
 7  2013     1     7 -4.9473118   27.76132
 8  2013     1     8 -3.2275785   20.78909
 9  2013     1     9 -0.2642777   25.63415
10  2013     1    10 -5.8988159   27.34545
# ... with 355 more rows

描述数据分布的函数有标准差sd(x)(standard deviation),四分位距IQR(x)(interquartile range)。
IQR(x) = quantile(x, 3/4) - quantile(x, 1/4),当存在异常值的时候,IQR(x)会非常有用。
比如查看为什么到达有些目的地机场的飞行距离波动更大。

not_cancelled %>% group_by(dest) %>% 
    summarise(distance_sd = sd(distance)) %>% arrange(desc(distance_sd))
# A tibble: 104 x 2
    dest distance_sd
   <chr>       <dbl>
 1   EGE   10.542765
 2   SAN   10.350094
 3   SFO   10.216017
 4   HNL   10.004197
 5   SEA    9.977993
 6   LAS    9.907786
 7   PDX    9.873299
 8   PHX    9.862546
 9   LAX    9.657195
10   IND    9.458066
# ... with 94 more rows

描述数据顺序的函数有最小值min(x),分位数quantile(x, 0.25),最大值max(x)
比如查看每天最早和最晚离开的航班。

not_cancelled %>% group_by(year, month, day) %>% 
    summarise(first = min(dep_time), last = max(dep_time))
# A tibble: 365 x 5
# Groups:   year, month [?]
    year month   day first  last
   <int> <int> <int> <dbl> <dbl>
 1  2013     1     1   517  2356
 2  2013     1     2    42  2354
 3  2013     1     3    32  2349
 4  2013     1     4    25  2358
 5  2013     1     5    14  2357
 6  2013     1     6    16  2355
 7  2013     1     7    49  2359
 8  2013     1     8   454  2351
 9  2013     1     9     2  2252
10  2013     1    10     3  2320
# ... with 355 more rows

描述数据位置的函数有first(x)nth(x, 2)last(x), 功能和x[1]x[2]x[length(x)]类似,但是可以在位置不存在时返回一个预先设置好的默认值。
比如查看每天最早和最晚离开的航班。

not_cancelled %>% group_by(year, month, day) %>% 
    summarise(first_dep = first(dep_time), last_dep = last(dep_time))
# A tibble: 365 x 5
# Groups:   year, month [?]
    year month   day first_dep last_dep
   <int> <int> <int>     <int>    <int>
 1  2013     1     1       517     2356
 2  2013     1     2        42     2354
 3  2013     1     3        32     2349
 4  2013     1     4        25     2358
 5  2013     1     5        14     2357
 6  2013     1     6        16     2355
 7  2013     1     7        49     2359
 8  2013     1     8       454     2351
 9  2013     1     9         2     2252
10  2013     1    10         3     2320
# ... with 355 more rows

这些描述数据位置的函数是filter()函数的补充,使用filter()函数可以输出更多的观测值。

not_cancelled %>% group_by(year, month, day) %>% 
    mutate(r = min_rank(desc(dep_time))) %>% filter(r %in% 1:5)
# A tibble: 1,859 x 20
# Groups:   year, month, day [365]
    year month   day dep_time sched_dep_time dep_delay arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1  2013     1     1     2327           2250        37       32
 2  2013     1     1     2343           1724       379      314
 3  2013     1     1     2353           2359        -6      425
 4  2013     1     1     2353           2359        -6      418
 5  2013     1     1     2356           2359        -3      425
 6  2013     1     2     2334           2129       125       33
 7  2013     1     2     2337           2155       102      222
 8  2013     1     2     2347           2245        62       58
 9  2013     1     2     2351           2359        -8      427
10  2013     1     2     2354           2359        -5      413
# ... with 1,849 more rows, and 13 more variables: sched_arr_time <int>,
#   arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
#   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
#   minute <dbl>, time_hour <dttm>, r <int>

前面已经用了n()计算样本数量。另外可以使用sum(!is.na(x))计算非缺失值的样本数量。可以使用n_distinct(x)计算排除重复值后的样本数量。
比如计算哪个目的地机场有最多的航空公司。

not_cancelled %>% group_by(dest) %>% 
    summarise(carriers = n_distinct(carrier)) %>% arrange(desc(carriers))
# A tibble: 104 x 2
    dest carriers
   <chr>    <int>
 1   ATL        7
 2   BOS        7
 3   CLT        7
 4   ORD        7
 5   TPA        7
 6   AUS        6
 7   DCA        6
 8   DTW        6
 9   IAD        6
10   MSP        6
# ... with 94 more rows

由于计数非常常用,dplyr包提供了一个更加简单的计数方法。

not_cancelled %>% count(dest)
# A tibble: 104 x 2
    dest     n
   <chr> <int>
 1   ABQ   254
 2   ACK   264
 3   ALB   418
 4   ANC     8
 5   ATL 16837
 6   AUS  2411
 7   AVL   261
 8   BDL   412
 9   BGR   358
10   BHM   269
# ... with 94 more rows

也可以增加一个表示权重(weight)的变量。
比如计算每架飞机飞行的总里程。

not_cancelled %>% count(tailnum, wt = distance)
# A tibble: 4,037 x 2
   tailnum      n
     <chr>  <dbl>
 1  D942DN   3418
 2  N0EGMQ 239143
 3  N10156 109664
 4  N102UW  25722
 5  N103US  24619
 6  N104UW  24616
 7  N10575 139903
 8  N105UW  23618
 9  N107US  21677
10  N108UW  32070
# ... with 4,027 more rows

对逻辑型变量进行计数和计算比例,比如:sum(x > 10)mean(y == 0)。 当使用数值函数时,TRUE会转换为1,FALSE会转换为0。 因此sum(x)返回的是xTRUE的数量,mean(x)返回的是xTRUE的比例。
比如计算有多少航班是在5点前离开的(这些航班通常是从前一天延误的)。

not_cancelled %>% group_by(year, month, day) %>% 
    summarise(n_early = sum(dep_time < 500))
# A tibble: 365 x 4
# Groups:   year, month [?]
    year month   day n_early
   <int> <int> <int>   <int>
 1  2013     1     1       0
 2  2013     1     2       3
 3  2013     1     3       4
 4  2013     1     4       3
 5  2013     1     5       3
 6  2013     1     6       2
 7  2013     1     7       2
 8  2013     1     8       1
 9  2013     1     9       3
10  2013     1    10       3
# ... with 355 more rows

延误超过1个小时的航班所占比例有多少?

not_cancelled %>% group_by(year, month, day) %>% 
    summarise(hour_perc = mean(arr_delay > 60))
# A tibble: 365 x 4
# Groups:   year, month [?]
    year month   day  hour_perc
   <int> <int> <int>      <dbl>
 1  2013     1     1 0.07220217
 2  2013     1     2 0.08512931
 3  2013     1     3 0.05666667
 4  2013     1     4 0.03964758
 5  2013     1     5 0.03486750
 6  2013     1     6 0.04704463
 7  2013     1     7 0.03333333
 8  2013     1     8 0.02130045
 9  2013     1     9 0.02015677
10  2013     1    10 0.01829925
# ... with 355 more rows

使用多个变量进行分组

当有多个变量进行多层分组时,每一次summarise会从后往前依次剥去一层分组。

daily <- group_by(flights, year, month, day)
(per_day   <- summarise(daily, flights = n()))
# A tibble: 365 x 4
# Groups:   year, month [?]
    year month   day flights
   <int> <int> <int>   <int>
 1  2013     1     1     842
 2  2013     1     2     943
 3  2013     1     3     914
 4  2013     1     4     915
 5  2013     1     5     720
 6  2013     1     6     832
 7  2013     1     7     933
 8  2013     1     8     899
 9  2013     1     9     902
10  2013     1    10     932
# ... with 355 more rows

(per_month <- summarise(per_day, flights = sum(flights)))
# A tibble: 12 x 3
# Groups:   year [?]
    year month flights
   <int> <int>   <int>
 1  2013     1   27004
 2  2013     2   24951
 3  2013     3   28834
 4  2013     4   28330
 5  2013     5   28796
 6  2013     6   28243
 7  2013     7   29425
 8  2013     8   29327
 9  2013     9   27574
10  2013    10   28889
11  2013    11   27268
12  2013    12   28135

(per_year  <- summarise(per_month, flights = sum(flights)))
# A tibble: 1 x 2
   year flights
  <int>   <int>
1  2013  336776

取消分组

使用ungroup()取消对数据框的分组。

daily %>% ungroup() %>% summarise(flights = n())
# A tibble: 1 x 1
  flights
    <int>
1  336776

练习

  1. 寻找准时到达的概率占99%以上的航班。
  2. 尝试用另外的代码(即不用count())实现与下列代码(not_cancelled %>% count(dest)not_cancelled %>% count(tailnum, wt = distance))同样的输出结果。
  3. 计算每天延误的航班数量,看看是否有模式可寻,每天延误航班的比例和平均延误时间有相关性吗?
  4. 计算哪家航空公司的延误最严重,可以排除掉机场的影响吗?
  5. 尝试使用一下count()函数中的sort参数,想想在什么时候可能会用到它。

group_by与mutate或filter一起使用

group_by()通常与summarise()一起使用,但有时也可以和mutate()filter()一起使用。
比如寻找每天延误最大的5个航班。

flights_sml %>% group_by(year, month, day) %>% 
    filter(rank(desc(arr_delay)) <= 5)
# A tibble: 1,805 x 11
# Groups:   year, month, day [365]
    year month   day dep_delay arr_delay distance air_time  gain    speed
   <int> <int> <int>     <dbl>     <dbl>    <dbl>    <dbl> <dbl>    <dbl>
 1  2013     1     1       853       851      184       41    -2 269.2683
 2  2013     1     1       290       338     1134      213    48 319.4366
 3  2013     1     1       260       263      266       46     3 346.9565
 4  2013     1     1       255       250      589      115    -5 307.3043
 5  2013     1     1       379       456     1092      222    77 295.1351
 6  2013     1     2       268       288     1092      203    20 322.7586
 7  2013     1     2       334       323      937      150   -11 374.8000
 8  2013     1     2       337       368     2586      346    31 448.4393
 9  2013     1     2       379       359     1620      228   -20 426.3158
10  2013     1     2       175       252     1325      286    77 277.9720
# ... with 1,795 more rows, and 2 more variables: hours <dbl>,
#   gain_per_hour <dbl>

寻找到达航班超过一定数量的目的地。

(popular_dests <- flights %>% group_by(dest) %>% 
     mutate(count = n()) %>% filter(count > 365))
# A tibble: 332,577 x 20
# Groups:   dest [77]
    year month   day dep_time sched_dep_time dep_delay arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1  2013     1     1      517            515         2      830
 2  2013     1     1      533            529         4      850
 3  2013     1     1      542            540         2      923
 4  2013     1     1      544            545        -1     1004
 5  2013     1     1      554            600        -6      812
 6  2013     1     1      554            558        -4      740
 7  2013     1     1      555            600        -5      913
 8  2013     1     1      557            600        -3      709
 9  2013     1     1      557            600        -3      838
10  2013     1     1      558            600        -2      753
# ... with 332,567 more rows, and 13 more variables: sched_arr_time <int>,
#   arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
#   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
#   minute <dbl>, time_hour <dttm>, count <int>

练习

  1. 哪架飞机(tailnum)的准点率最差?
  2. 乘坐哪个小时起飞的航班能够尽可能的避免延误?
  3. 计算到达每个目的地机场的全部航班的总延误时间。
  4. 绘图探索航班的延误时间与之前起飞的航班的延误时间的关系(使用lag())。
  5. 寻找到达每个目的地机场的航班中飞行异常快的航班(这些航班可能存在数据输入错误)。
  6. 计算每个航班的飞行时间与到达相同目的地机场飞行时间最短航班的飞行时间的差距,并从中筛选出空中延误最大的航班。
  7. 寻找至少有两家航空公司执飞的目的地机场。