2  数据变换

可视化是生成图形的重要工具,但很少能直接获得所需形式的数据来制作理想的图表。在本章中,你将学习如何完成所有这些(以及更多!)操作,本章将介绍使用dplyr包和 2013 年从纽约市出发的航班新数据集进行数据转换。

dplyr包提供了一组用于数据转换的函数,这些函数可以组合使用以实现复杂的数据操作。dplyr中的大多数函数都遵循相同的语法结构:

  1. 第一个参数是数据框(或 tibble)。
  2. 后续的参数通常是变量名(不带引号),用来指定要操作的列。
  3. 大多数函数返回一个数据框(或 tibble)。

2.1 行操作

  1. filter()选择满足条件的行: 可以使用任意逻辑符号,==, !=, <, >, <=, >=, &, |, !

  2. arrange()根据列的值对行进行排序: 排序所依据的列可以是单列,也可以是多列。可以使用desc()函数对结果进行降序排序。

  3. distinct()查找数据集中的所有唯一行。在需要找出特定变量组合时,可以提供列名,即找出符合提供列名组合的行。如果您想找到出现次数,最好将 distinct() 换成 count(),同时使用 sort = TRUE 参数,可以按出现次数降序排列。有关count() 的更多信息,请参见后面的章节 小节 9.1

|==可以合并为%in%
# 查找数据集中的唯一行
flights |>
  distinct()
# A tibble: 336,776 × 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     1      517            515         2      830            819
 2  2013     1     1      533            529         4      850            830
 3  2013     1     1      542            540         2      923            850
 4  2013     1     1      544            545        -1     1004           1022
 5  2013     1     1      554            600        -6      812            837
 6  2013     1     1      554            558        -4      740            728
 7  2013     1     1      555            600        -5      913            854
 8  2013     1     1      557            600        -3      709            723
 9  2013     1     1      557            600        -3      838            846
10  2013     1     1      558            600        -2      753            745
# ℹ 336,766 more rows
# ℹ 11 more variables: 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>
# 查找特定列组合的唯一行,注意与`select()`的区别
flights |>
  distinct(origin, dest)
# A tibble: 224 × 2
   origin dest 
   <chr>  <chr>
 1 EWR    IAH  
 2 LGA    IAH  
 3 JFK    MIA  
 4 JFK    BQN  
 5 LGA    ATL  
 6 EWR    ORD  
 7 EWR    FLL  
 8 LGA    IAD  
 9 JFK    MCO  
10 LGA    ORD  
# ℹ 214 more rows
# 计算特定列组合的出现次数,并按次数降序排列
flights |>
  count(origin, dest, sort = TRUE)
# A tibble: 224 × 3
   origin dest      n
   <chr>  <chr> <int>
 1 JFK    LAX   11262
 2 LGA    ATL   10263
 3 LGA    ORD    8857
 4 JFK    SFO    8204
 5 LGA    CLT    6168
 6 EWR    ORD    6100
 7 JFK    BOS    5898
 8 LGA    MIA    5781
 9 JFK    MCO    5464
10 EWR    BOS    5327
# ℹ 214 more rows

2.2 列操作

  1. select()选择特定列: 可以使用列名或列编号。有许多的辅助函数:
  • starts_with()选择以指定字符串开头的列。
  • ends_with()选择以指定字符串结尾的列。
  • contains()选择包含指定字符串的列。
  • 使用?select()可以产看所有辅助函数及其用法。

一旦我们了解了正则表达式(?sec-regular-expressions), 我们就可以使用matches()函数来选择符合正则表达式模式的列。

  1. rename()重命名列: 可以使用列名对列进行重命名。
  1. mutate()创建新列: 可以使用任意公式对列进行计算。默认情况下,新列会添加到数据集的最后一列,在函数中传入参数.before1.after可以调整新列的位置,如.before = 1 表示新列添加到原数据集的第一列之前,即第一列。
  • mutate()有一个非常有用的参数,.keep,用于控制在创建新列后保留哪些列:
    • .keep = "all": 保留所有列(默认值)。
    • .keep = "used": 仅保留用于计算新列的列以及新列本身。最常用。
    • .keep = "unused": 仅保留未用于计算新列的列。
    • .keep = "none": 仅保留新列。
flights |>
  mutate(
    gain = dep_delay - arr_delay,
    hours = air_time / 60,
    .keep = "used"
  )
# A tibble: 336,776 × 5
   dep_delay arr_delay air_time  gain hours
       <dbl>     <dbl>    <dbl> <dbl> <dbl>
 1         2        11      227    -9 3.78 
 2         4        20      227   -16 3.78 
 3         2        33      160   -31 2.67 
 4        -1       -18      183    17 3.05 
 5        -6       -25      116    19 1.93 
 6        -4        12      150   -16 2.5  
 7        -5        19      158   -24 2.63 
 8        -3       -14       53    11 0.883
 9        -3        -8      140     5 2.33 
10        -2         8      138   -10 2.3  
# ℹ 336,766 more rows

以上代码中生成新变量用到了dep_delayarr_delayair_time3个变量,由于使用了参数.keep = "used" ,因此最终结果只保留了这3个变量和新生成的变量gainhours

  1. relocate()更改列排序。
flights |>
  relocate(time_hour, air_time)
# A tibble: 336,776 × 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
# ℹ 336,766 more rows
# ℹ 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>

2.3 分组

  1. group_by()对数据集进行分组: 可以使用列名对数据集进行分组。
  • 可以同时以多行为基准进行分组操作。
  1. summarise()对分组后的结果进行汇总: 可以使用任意公式对分组后的结果进行汇总。
  • 在使用summarise() 时,可以省略group_by(),并在函数中加入.by参数。

    • .by参数是dplyr在1.10版本中新加入summarise()函数的参数,与group_by()效果一致,它还有一个优点是无需在分组后使用.groupungroup函数,同时可以起到简化代码的作用。值得注意的是,.by参数的结果不会改变原数据的类型,而group_by会将结果转化为tibble。具体可参看dplyr 1.1.0的博客介绍
  • 使用n()函数可以计算分组后的行数,这有点类似于data.table中的.N

  • 在进行分组汇总计算时,要特别注意缺失值。在R中,缺失值是会传染的。

  1. 可以针对多个变量进行分组,在此情况下,每次汇总计算都会针对分组的最后一个变量,group_by()也会返回一个提示信息,我们可以通过设置.groups参数来控制这个信息的显示。.groups参数有四个选项:
  • "drop_last": 删除最后一个分组变量(默认值)。
  • "drop": 删除所有分组变量。
  • "keep": 保留所有分组变量。
  • "rowwise": 将结果转换为按行分组的tibble

2.3.1 切片

  • slice_head(df, n = 1) 从每组中取第一行。

  • slice_tail(df, n = 1) 取每组的最后一行。

  • slice_min(x, n = 1) 取 x 列中数值最小的一行。slice_max(x, n = 1) 取 x 列中数值最大的一行。

    • slice_min()slice_max() 会保留并列值,因此 n = 1 表示返回所有具有最高值的行。如果你希望每组只有一条记录,可以设置 with_ties = FALSE
  • slice_sample(df, n = 1) 随机取一行。

  • 以上所有函数中的参数n均可以使用prop代替,即使用比例来指定切片的行数量。

2.4 案例-aggregates and sample size

每当进行聚合(group_by()+summarise())操作时,最终结果包含计数结果(n())总是很有用的。我们用Lahman包中的Batting数据集来演示这一点。Batting数据集包含了从1871年到2016年期间所有大联盟棒球运动员的击球数据。

batters <- Lahman::Batting |>
  group_by(playerID) |>
  summarise(
    performance = sum(H, na.rm = TRUE) / sum(AB, na.rm = TRUE),
    n = n()
  )

batters
# A tibble: 24,011 × 3
   playerID  performance     n
   <chr>           <dbl> <int>
 1 aardsda01      0          9
 2 aaronha01      0.305     23
 3 aaronto01      0.229      7
 4 aasedo01       0         13
 5 abadan01       0.0952     3
 6 abadfe01       0.111     12
 7 abadijo01      0.224      2
 8 abbated01      0.254     10
 9 abbeybe01      0.169      6
10 abbeych01      0.281      5
# ℹ 24,001 more rows

在上面的代码中,我们计算了每个球员的击球表现(performance),即击中次数(H)与击球次数(AB)的比值。同时,我们还计算了每个球员的比赛场次(n)。这样做的好处是,我们可以根据比赛场次来评估击球表现的可靠性。例如,一个球员如果只参加了几场比赛,那么他的击球表现可能并不具有代表性。

2.5 总结

本章介绍了dplyr包中的一些常用数据转换函数,包括行操作、列操作和分组操作。通过这些函数,我们可以轻松地对数据进行过滤、排序、选择、重命名、创建新列以及分组汇总等操作。这些技能对于数据分析和可视化都是非常重要的基础。

在第三部分变量类型和变量操作工具中,我们将针对特定的变量,对本章介绍的内容进行更深入的探讨。


  1. . 符号表示为函数的参数,而不是要创建新列的变量名称。↩︎