R 数据处理 —— tidyr

1. 前言

在本节中,我们将介绍数据的整洁之道,以及 tidyverse 的核心包之一 tidyr

它提供了一系列工具来帮助整理混乱的数据,也是我们本节的重点。

library(tidyverse)

2. 整洁数据

每份数据都可以有不同的表现方式。下面的例子中,我们以四种不同的方式来展示相同的数据。

每个数据都包括 country, year, populationcases 4 个变量,且具有相同的值,但是它们组织数据的方式是不一样的。

table1
#> # A tibble: 6 x 4
#>   country      year  cases population
#>   <chr>       <int>  <int>      <int>
#> 1 Afghanistan  1999    745   19987071
#> 2 Afghanistan  2000   2666   20595360
#> 3 Brazil       1999  37737  172006362
#> 4 Brazil       2000  80488  174504898
#> 5 China        1999 212258 1272915272
#> 6 China        2000 213766 1280428583

table2
#> # A tibble: 12 x 4
#>   country      year type           count
#>   <chr>       <int> <chr>          <int>
#> 1 Afghanistan  1999 cases            745
#> 2 Afghanistan  1999 population  19987071
#> 3 Afghanistan  2000 cases           2666
#> 4 Afghanistan  2000 population  20595360
#> 5 Brazil       1999 cases          37737
#> 6 Brazil       1999 population 172006362
#> # … with 6 more rows

table3
#> # A tibble: 6 x 3
#>   country      year rate             
#> * <chr>       <int> <chr>            
#> 1 Afghanistan  1999 745/19987071     
#> 2 Afghanistan  2000 2666/20595360    
#> 3 Brazil       1999 37737/172006362  
#> 4 Brazil       2000 80488/174504898  
#> 5 China        1999 212258/1272915272
#> 6 China        2000 213766/1280428583

# Spread across two tibbles
table4a  # cases
#> # A tibble: 3 x 3
#>   country     `1999` `2000`
#> * <chr>        <int>  <int>
#> 1 Afghanistan    745   2666
#> 2 Brazil       37737  80488
#> 3 China       212258 213766

table4b  # population
#> # A tibble: 3 x 3
#>   country         `1999`     `2000`
#> * <chr>            <int>      <int>
#> 1 Afghanistan   19987071   20595360
#> 2 Brazil       172006362  174504898
#> 3 China       1272915272 1280428583

这些都是相同基础数据的表示形式,但它们并不都是易于使用的。

数据的整洁之道主要有下面三个相互关联的规则

  1. 每个变量必须要有自己的列
  2. 每个观察值必须在自己行中
  3. 每个值必须在自己的单元格内

下面的图片也展示了这三条规则

image.png

这三条规则是相互关联的,不可能只满足这三条规则中的两条。这种相互关系产生了一套更简单实用的说明

  1. 将每个数据集存为 tibble
  2. 每个变量代表一列

在上面的示例中,只有 table1 是整齐的,每个变量表示为一列的形式

为什么要确保数据整洁呢?主要有以下两个优点

  1. 以一种一致的方式存储数据是有优势的,如果你的数据结构保持一致,则更容易学习和使用这些工具函数

  2. 在每个变量作为一列有一个优点,它能充分发挥 R 的向量化特性。 R 大多数的内置函数都可以与值向量一起使用,这使得对数据的转换显得特别自然

dplyrggplot2tidyverse 中所有的其他包都被设计用于处理整洁的数据。下面是几个小示例,展示如何使用 table1

> table1 %>% 
+     mutate(rate = cases / population * 10000)
# A tibble: 6 x 5
  country      year  cases population  rate
  <chr>       <dbl>  <dbl>      <dbl> <dbl>
1 Afghanistan  1999    745   19987071 0.373
2 Afghanistan  2000   2666   20595360 1.29 
3 Brazil       1999  37737  172006362 2.19 
4 Brazil       2000  80488  174504898 4.61 
5 China        1999 212258 1272915272 1.67 
6 China        2000 213766 1280428583 1.67

> table1 %>% 
+     count(year, wt = cases)
# A tibble: 2 x 2
   year      n
  <dbl>  <dbl>
1  1999 250740
2  2000 296920

> ggplot(table1, aes(year, cases)) + 
+     geom_line(aes(group = country), colour = "grey50") + 
+     geom_point(aes(colour = country))

image

2.1 思考练习
  1. 计算 table2table4a + table4b 的比率 rate,你可能需要执行下面 4 个操作
  • 提取每个国家每年的结核病病例数
  • 提取每个国家/地区每年匹配人口数
  • 将病例数除以人口数,再乘以 10000
  • 然后将比率放在适合的位置上

哪种表示法最容易使用?最难的是哪一个?为什么?

  1. 使用 table2 代替 table1 重新创建病例随时间变化的图。您首先需要做什么?

3. 透视表

通常在真实的分析中,我们遇到的数据都需要进一步进行一些整理。

第一步需要弄清楚变量和观察值是什么,有时候这很容易,但是有时可能需要询问数据提供者

第二步需要解决下面两个常见问题:

  1. 一个变量可能分布在多个列上
  2. 一种观察值可能分散在多行中

要解决这些问题,您可以使用 tidyr 提供的两个最重要的函数:pivot_longer()pivot_wider()

3.1 Longer

一个常见的问题是,数据集中的列名不是变量名而是变量的值

比如说表 table4a,它的列名 19992000 代表的是年份变量的值,而这两列的值表示的是对应年份的 cases 变量的数值,而且每行是两个变量的值,而不是一个。

> table4a
# A tibble: 3 x 3
  country     `1999` `2000`
  <chr>        <dbl>  <dbl>
1 Afghanistan    745   2666
2 Brazil       37737  80488
3 China       212258 213766

要整理这样的数据集,我们需要将有问题的列透视到一对新的变量中。

为了描述该操作,我们需要三个参数:

  • 列名是值而不是变量,在本例中,是 19992000
  • 要将列名移至的变量名,在这里是 year
  • 要将列的值移动到的变量名称。在这里是 cases

将这些参数传递到 pivot_longer() 函数中

> table4a %>% 
+     pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "cases")
# A tibble: 6 x 3
  country     year   cases
  <chr>       <chr>  <dbl>
1 Afghanistan 1999     745
2 Afghanistan 2000    2666
3 Brazil      1999   37737
4 Brazil      2000   80488
5 China       1999  212258
6 China       2000  213766

列名可以使用 dplyr::select() 样式表示法指定。但是这里只有两列,因此将它们单独列出

注意19992000 是非语法名称(因为它们不是以字母开头),所以我们必须用反勾号将它们括起来

因为 table4a 中不存在 yearcases 列名,因此我们将其名称用引号引起来。

image.png

在最后的结果中,我们得到了两个新的列并删除了原来需要透视的两列。

pivot_longer() 通过增加行数和减少列数使数据集变长,当然这里说的长主要是相对原来的数据而言的。

我们可以使用 pivot_longer() 以类似的方式整理 table4b,唯一的区别是存储在单元格中的变量值

> table4b %>% 
+     pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "population")
# A tibble: 6 x 3
  country     year  population
  <chr>       <chr>      <int>
1 Afghanistan 1999    19987071
2 Afghanistan 2000    20595360
3 Brazil      1999   172006362
4 Brazil      2000   174504898
5 China       1999  1272915272
6 China       2000  1280428583

要将整理后的 table4atable4b 合并到一个 tible 中,我们可以使用 dplyr::left_join() 进行连接

> tidy4a <- table4a %>% 
+     pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "cases")
> tidy4b <- table4b %>% 
+     pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "population")
> left_join(tidy4a, tidy4b)
Joining, by = c("country", "year")
# A tibble: 6 x 4
  country     year   cases population
  <chr>       <chr>  <dbl>      <int>
1 Afghanistan 1999     745   19987071
2 Afghanistan 2000    2666   20595360
3 Brazil      1999   37737  172006362
4 Brazil      2000   80488  174504898
5 China       1999  212258 1272915272
6 China       2000  213766 1280428583
3.2 Wider

pivot_wilder()pivot_longer() 相反,当观察值分散在多行中时,可以使用它。

例如,在 table2type 观察值观察值分布在国家和年份的两行中

> table2
# A tibble: 12 x 4
   country      year type            count
   <chr>       <int> <chr>           <int>
 1 Afghanistan  1999 cases             745
 2 Afghanistan  1999 population   19987071
 3 Afghanistan  2000 cases            2666
 4 Afghanistan  2000 population   20595360
 5 Brazil       1999 cases           37737
 6 Brazil       1999 population  172006362
 7 Brazil       2000 cases           80488
 8 Brazil       2000 population  174504898
 9 China        1999 cases          212258
10 China        1999 population 1272915272
11 China        2000 cases          213766
12 China        2000 population 1280428583

我们首先以类似于 pivot_longer() 的方式分析。不过,这次我们只需要两个参数

  • 要从中获取变量名的列,在这里是 type
  • 要从中获取值的列,在这里是 count

一旦我们弄明白了这一点,我们就可以使用 pivot_wilder()

> table2 %>%
+     pivot_wider(names_from = type, values_from = count)
# A tibble: 6 x 4
  country      year  cases population
  <chr>       <int>  <int>      <int>
1 Afghanistan  1999    745   19987071
2 Afghanistan  2000   2666   20595360
3 Brazil       1999  37737  172006362
4 Brazil       2000  80488  174504898
5 China        1999 212258 1272915272
6 China        2000 213766 1280428583

image.png

可能你已经从它们的名字中猜到了,pivot_wilder()pivot_longer() 是互补的。

pivot_longer() 使宽表变窄和变长; pivot_wider() 使长表变短和变宽

3.3 思考练习
  1. 为什么 pivot_wilder()pivot_longer() 不完全对称?仔细思考下面的例子。
stocks <- tibble(
  year   = c(2015, 2015, 2016, 2016),
  half  = c(   1,    2,     1,    2),
  return = c(1.88, 0.59, 0.92, 0.17)
)
stocks %>% 
  pivot_wider(names_from = year, values_from = return) %>% 
  pivot_longer(`2015`:`2016`, names_to = "year", values_to = "return")

提示:注意变量类型并考虑列名

pivot_longer() 有一个 names_ptypes 参数,例如 names_ptypes=list(year=double)。它的作用是什么?

  1. 为什么下面的代码是错的?
> table4a %>% 
+     pivot_longer(c(1999, 2000), names_to = "year", values_to = "cases")
错误: Can't subset columns that don't exist.
x Locations 1999 and 2000 don't exist.
ℹ There are only 3 columns.
Run `rlang::last_error()` to see where the error occurred.
  1. 如果你要加宽表会发生什么?为什么?您如何添加新列来唯一标识每个值?
people <- tribble(
  ~name,             ~names,  ~values,
  #-----------------|--------|------
  "Phillip Woods",   "age",       45,
  "Phillip Woods",   "height",   186,
  "Phillip Woods",   "age",       50,
  "Jessica Cordero", "age",       37,
  "Jessica Cordero", "height",   156
)
  1. 整理下面的简单表。你需要将表变宽还是变长?变量要设置成什么?
preg <- tribble(
  ~pregnant, ~male, ~female,
  "yes",     NA,    10,
  "no",      20,    12
)

4. 分隔和合并

到目前为止,你已经学会了如何整理 table2table4

但是对于 table3,有一个不一样的问题:我们有一列(rate)包含两个变量(casespopulation)。

要解决这个问题,可以使用 separate() 函数。而如果单个变量在不同的列上,则可以使用 unite()

4.1 separate

separate() 通过在分隔符的位置上拆分数据,将一列拆分为多列。对于 table3

> table3
# A tibble: 6 x 3
  country      year rate             
* <chr>       <int> <chr>            
1 Afghanistan  1999 745/19987071     
2 Afghanistan  2000 2666/20595360    
3 Brazil       1999 37737/172006362  
4 Brazil       2000 80488/174504898  
5 China        1999 212258/1272915272
6 China        2000 213766/1280428583

rate 列同时包含了 casespopulation,我们需要将其拆分为两个变量

separate() 获取要分离的列的名称,以及要分离为列的名称

> table3 %>% 
+     separate(rate, into = c("cases", "population"))
# A tibble: 6 x 4
  country      year cases  population
  <chr>       <int> <chr>  <chr>     
1 Afghanistan  1999 745    19987071  
2 Afghanistan  2000 2666   20595360  
3 Brazil       1999 37737  172006362 
4 Brazil       2000 80488  174504898 
5 China        1999 212258 1272915272
6 China        2000 213766 1280428583

image.png

在默认情况下,separate() 会在看到非字母数字字符(即不是数字或字母的字符)的任何位置对值进行拆分。

例如,在上面的代码中,separate()rate 列的值用 / 分隔。如果要指定特定的分隔字符,可以将该字符传递给 separate()sep 参数。例如,我们可以将上面的代码重写

table3 %>% 
  separate(rate, into = c("cases", "population"), sep = "/")

注意sep 支持正则表达式

你可能也注意到 casespopulation 列都是字符类型,这是 separate() 函数的默认行为,保持列的类型不变。

然而,在这里它并不适用,因为这些实际上是数字。我们可以使用 convert=TRUEseparate() 尝试将值转换为更好的类型

> table3 %>% 
+     separate(rate, into = c("cases", "population"), convert = TRUE)
# A tibble: 6 x 4
  country      year  cases population
  <chr>       <int>  <int>      <int>
1 Afghanistan  1999    745   19987071
2 Afghanistan  2000   2666   20595360
3 Brazil       1999  37737  172006362
4 Brazil       2000  80488  174504898
5 China        1999 212258 1272915272
6 China        2000 213766 1280428583

也可以为 sep 传递整数向量,separate() 会将整数解释为需要拆分的位置,正数值表示从字符串最左侧的位置 1 开始;负数值表示从字符串最右侧的位置 -1 开始。

当使用整数分隔字符串时,sep 的长度应该比 into 中的名称数少一个。

我们可以使用这种方式来分隔年份,虽然会使数据不那么整洁,但是有时会很有用

> table3 %>% 
+     separate(year, into = c("century", "year"), sep = 2)
# A tibble: 6 x 4
  country     century year  rate             
  <chr>       <chr>   <chr> <chr>            
1 Afghanistan 19      99    745/19987071     
2 Afghanistan 20      00    2666/20595360    
3 Brazil      19      99    37737/172006362  
4 Brazil      20      00    80488/174504898  
5 China       19      99    212258/1272915272
6 China       20      00    213766/1280428583
4.2 unite

unite()separate() 的逆函数:它将多列合并为一列。与 separate() 相比,你用到它的频率要低得多,但它仍然是一个很有用的工具。

image

我们可以使用 unite() 重新合并上一个示例中创建的 centuryyear 列。该数据被保存为 tidyr::table5

unite() 接受一个数据框,需要创建的新的列名以及要组合的一组列,接受 select 样式。

> table5 %>% 
+     unite(new, century, year)
# A tibble: 6 x 3
  country     new   rate             
  <chr>       <chr> <chr>            
1 Afghanistan 19_99 745/19987071     
2 Afghanistan 20_00 2666/20595360    
3 Brazil      19_99 37737/172006362  
4 Brazil      20_00 80488/174504898  
5 China       19_99 212258/1272915272
6 China       20_00 213766/1280428583

在这种情况下,我们还需要使用 sep 参数。默认值将在不同列的值之间放置下划线(_)。在这里我们不需要任何分隔符,所以使用 ""

> table5 %>% 
+     unite(new, century, year, sep = "")
# A tibble: 6 x 3
  country     new   rate             
  <chr>       <chr> <chr>            
1 Afghanistan 1999  745/19987071     
2 Afghanistan 2000  2666/20595360    
3 Brazil      1999  37737/172006362  
4 Brazil      2000  80488/174504898  
5 China       1999  212258/1272915272
6 China       2000  213766/1280428583
4.3 思考练习
  1. extrafill 参数在 separate() 中的作用是什么?可以对以下两个数据集进行实验
tibble(x = c("a,b,c", "d,e,f,g", "h,i,j")) %>% 
  separate(x, c("one", "two", "three"))

tibble(x = c("a,b,c", "d,e", "f,g,i")) %>% 
  separate(x, c("one", "two", "three"))
  1. unite()separate() 都有一个 remove 参数,它有什么作用?为什么要把它设置为 FALSE

  2. 比较 separate()extract(),为什么分隔有三种变体(通过位置,分隔符和分组),但是 unite 只有一个?

5. 缺失值

更改数据的表现形式,可能会引入缺失值。以下两种方式之一都会引入缺失值

  • 显式:例如,存在 NA
  • 隐式:例如,数据中不存在的值

让我们用一个简单的数据来解释

stocks <- tibble(
  year   = c(2015, 2015, 2015, 2015, 2016, 2016, 2016),
  qtr    = c(   1,    2,    3,    4,    2,    3,    4),
  return = c(1.88, 0.59, 0.35,   NA, 0.92, 0.17, 2.66)
)

这个数据中存在两个缺失值:

  • 2015 年第四季度的回报率是缺失的,其对应的值为 NA
  • 2016 年第一季度的回报隐式地丢失了,它根本没有出现在数据中

数据集的表示方式可以使隐式的值显式化,例如,我们可以通过将年份放在列中来显示隐式的缺失值。

> stocks %>% 
+     pivot_wider(names_from = year, values_from = return)
# A tibble: 4 x 3
    qtr `2015` `2016`
  <dbl>  <dbl>  <dbl>
1     1   1.88  NA   
2     2   0.59   0.92
3     3   0.35   0.17
4     4  NA      2.66

由于这些显式缺失值在数据的其他表示形式中可能并不重要,因此可以在 pivot_longer() 中设置 values_drop_na = TRUE 将显式缺失值变为隐式

> stocks %>% 
+     pivot_wider(names_from = year, values_from = return) %>% 
+     pivot_longer(
+         cols = c(`2015`, `2016`), 
+         names_to = "year", 
+         values_to = "return", 
+         values_drop_na = TRUE
+     )
# A tibble: 6 x 3
    qtr year  return
  <dbl> <chr>  <dbl>
1     1 2015    1.88
2     2 2015    0.59
3     2 2016    0.92
4     3 2015    0.35
5     3 2016    0.17
6     4 2016    2.66

另一个显式标记缺失值的工具就是 complete()

> stocks %>% 
+     complete(year, qtr)
# A tibble: 8 x 3
   year   qtr return
  <dbl> <dbl>  <dbl>
1  2015     1   1.88
2  2015     2   0.59
3  2015     3   0.35
4  2015     4  NA   
5  2016     1  NA   
6  2016     2   0.92
7  2016     3   0.17
8  2016     4   2.66

complete() 接受一组列,并搜索所有唯一的组合。然后确保原始数据集包含所有这些值,并在必要时显式的填充 NA

还有另一个处理缺失值的工具,例如

treatment <- tribble(
  ~ person,           ~ treatment, ~response,
  "Derrick Whitmore", 1,           7,
  NA,                 2,           10,
  NA,                 3,           9,
  "Katherine Burke",  1,           4
)

你可以使用 fill() 来填充缺失值。它接收一组列,并在这组列中指定用于填补缺失值的值

> treatment %>% 
+     fill(person)
# A tibble: 4 x 3
  person           treatment response
  <chr>                <dbl>    <dbl>
1 Derrick Whitmore         1        7
2 Derrick Whitmore         2       10
3 Derrick Whitmore         3        9
4 Katherine Burke          1        4
5.1 思考练习
  1. fill 参数与 pivot()complete() 进行比较。

  2. fill()direction 参数有什么作用?

6. 案例分析

下面我们将上面介绍的知识结合起来,来解决一个数据整理的问题。

我们使用 tidyr::who 数据,其中包含按年,国家,年龄,性别和诊断方法的结核病(TB)病例

在这个数据集中有保存了丰富的流行病学信息,但直接使用它所提供的数据形式是不太好的。

> who
# A tibble: 7,240 x 60
   country iso2  iso3   year new_sp_m014 new_sp_m1524 new_sp_m2534 new_sp_m3544 new_sp_m4554
   <chr>   <chr> <chr> <int>       <int>        <int>        <int>        <int>        <int>
 1 Afghan… AF    AFG    1980          NA           NA           NA           NA           NA
 2 Afghan… AF    AFG    1981          NA           NA           NA           NA           NA
 3 Afghan… AF    AFG    1982          NA           NA           NA           NA           NA
 4 Afghan… AF    AFG    1983          NA           NA           NA           NA           NA
 5 Afghan… AF    AFG    1984          NA           NA           NA           NA           NA
 6 Afghan… AF    AFG    1985          NA           NA           NA           NA           NA
 7 Afghan… AF    AFG    1986          NA           NA           NA           NA           NA
 8 Afghan… AF    AFG    1987          NA           NA           NA           NA           NA
 9 Afghan… AF    AFG    1988          NA           NA           NA           NA           NA
10 Afghan… AF    AFG    1989          NA           NA           NA           NA           NA
# … with 7,230 more rows, and 51 more variables: new_sp_m5564 <int>, new_sp_m65 <int>,
#   new_sp_f014 <int>, new_sp_f1524 <int>, new_sp_f2534 <int>, new_sp_f3544 <int>, new_sp_f4554 <int>,
#   new_sp_f5564 <int>, new_sp_f65 <int>, new_sn_m014 <int>, new_sn_m1524 <int>, new_sn_m2534 <int>,
#   new_sn_m3544 <int>, new_sn_m4554 <int>, new_sn_m5564 <int>, new_sn_m65 <int>, new_sn_f014 <int>,
#   new_sn_f1524 <int>, new_sn_f2534 <int>, new_sn_f3544 <int>, new_sn_f4554 <int>,
#   new_sn_f5564 <int>, new_sn_f65 <int>, new_ep_m014 <int>, new_ep_m1524 <int>, new_ep_m2534 <int>,
#   new_ep_m3544 <int>, new_ep_m4554 <int>, new_ep_m5564 <int>, new_ep_m65 <int>, new_ep_f014 <int>,
#   new_ep_f1524 <int>, new_ep_f2534 <int>, new_ep_f3544 <int>, new_ep_f4554 <int>,
#   new_ep_f5564 <int>, new_ep_f65 <int>, newrel_m014 <int>, newrel_m1524 <int>, newrel_m2534 <int>,
#   newrel_m3544 <int>, newrel_m4554 <int>, newrel_m5564 <int>, newrel_m65 <int>, newrel_f014 <int>,
#   newrel_f1524 <int>, newrel_f2534 <int>, newrel_f3544 <int>, newrel_f4554 <int>,
#   newrel_f5564 <int>, newrel_f65 <int>

这是非常典型的真实数据,它包含冗余的列和许多缺失值,总之乱七八糟的。

通常处理这种数据需要用管道来组合 dplyrtidyr 中的操作函数。

通常,最好的着手点就是将非变量的列集合在一起,让我们来分析一下

  • 似乎 country, iso2iso3 这三列是冗余的国家信息
  • year 显然是一个变量
  • 我们还不知道其他所有列是什么,但是给定了变量名的结构(例如 new_sp_m014, new_ep_m014, new_ep_f014),这些很可能是值而是变量

因此,我们需要收集从 new_sp_m014newrel_f65 所有列。

我们还不知道这些值代表什么,所以我们给它们取一个通用的名称 。我们知道单元格代表病例数,因此我们将使用 cases 作为变量。

同时,当前的表示形式中有很多缺失值,所以我们使用 values_drop_na 只专注于已经存在的值

> who1 <- who %>% 
+     pivot_longer(
+         cols = new_sp_m014:newrel_f65, 
+         names_to = "key", 
+         values_to = "cases", 
+         values_drop_na = TRUE
+     )
> who1
# A tibble: 76,046 x 6
   country     iso2  iso3   year key          cases
   <chr>       <chr> <chr> <int> <chr>        <int>
 1 Afghanistan AF    AFG    1997 new_sp_m014      0
 2 Afghanistan AF    AFG    1997 new_sp_m1524    10
 3 Afghanistan AF    AFG    1997 new_sp_m2534     6
 4 Afghanistan AF    AFG    1997 new_sp_m3544     3
 5 Afghanistan AF    AFG    1997 new_sp_m4554     5
 6 Afghanistan AF    AFG    1997 new_sp_m5564     2
 7 Afghanistan AF    AFG    1997 new_sp_m65       0
 8 Afghanistan AF    AFG    1997 new_sp_f014      5
 9 Afghanistan AF    AFG    1997 new_sp_f1524    38
10 Afghanistan AF    AFG    1997 new_sp_f2534    36
# … with 76,036 more rows

然后,我们对新的列 key 进行统计

> who1 %>% 
+     count(key)
# A tibble: 56 x 2
   key              n
   <chr>        <int>
 1 new_ep_f014   1032
 2 new_ep_f1524  1021
 3 new_ep_f2534  1021
 4 new_ep_f3544  1021
 5 new_ep_f4554  1017
 6 new_ep_f5564  1017
 7 new_ep_f65    1014
 8 new_ep_m014   1038
 9 new_ep_m1524  1026
10 new_ep_m2534  1020
# … with 46 more rows

你可以用自己的方式来解析这列数据,但是幸运的是,我们手里有这一数据的一些信息:

  1. 每列的前三个字母表示该列包含的是新的还是旧的 TB 病例,在这个数据中,每列包含的都是新的病例

  2. 后面两个字符代表 TB 的类型

  • rel 代表复发病例
  • ep 代表肺外结核病例
  • sn 代表无法通过肺部涂片诊断的肺结核病例(涂片阴性)
  • sp 代表可以通过肺部涂片诊断的肺结核病例(涂片阳性)
  1. 第六个字符表示结核病患者的性别,男性(m)和女性(f

  2. 剩下的数字表示年龄分组,数据将年龄分为七个年龄组。

  • 014 = 0 – 14
  • 1524 = 15 – 24
  • 2534 = 25 – 34
  • 3544 = 35 – 44
  • 4554 = 45 – 54
  • 5564 = 55 – 64
  • 65 = 65 or older

我们先对列名的格式进行一个小小的修改,用 new_rel 替换 newrel,而其他都是以 _ 分隔的

> who2 <- who1 %>% 
+     mutate(key = stringr::str_replace(key, "newrel", "new_rel"))
> who2
# A tibble: 76,046 x 6
   country     iso2  iso3   year key          cases
   <chr>       <chr> <chr> <int> <chr>        <int>
 1 Afghanistan AF    AFG    1997 new_sp_m014      0
 2 Afghanistan AF    AFG    1997 new_sp_m1524    10
 3 Afghanistan AF    AFG    1997 new_sp_m2534     6
 4 Afghanistan AF    AFG    1997 new_sp_m3544     3
 5 Afghanistan AF    AFG    1997 new_sp_m4554     5
 6 Afghanistan AF    AFG    1997 new_sp_m5564     2
 7 Afghanistan AF    AFG    1997 new_sp_m65       0
 8 Afghanistan AF    AFG    1997 new_sp_f014      5
 9 Afghanistan AF    AFG    1997 new_sp_f1524    38
10 Afghanistan AF    AFG    1997 new_sp_f2534    36
# … with 76,036 more rows

我们可以通过两次 separate() 来分隔每个代码的值。

第一次在下划线处拆分代码

> who3 <- who2 %>% 
+     separate(key, c("new", "type", "sexage"), sep = "_")
> who3
# A tibble: 76,046 x 8
   country     iso2  iso3   year new   type  sexage cases
   <chr>       <chr> <chr> <int> <chr> <chr> <chr>  <int>
 1 Afghanistan AF    AFG    1997 new   sp    m014       0
 2 Afghanistan AF    AFG    1997 new   sp    m1524     10
 3 Afghanistan AF    AFG    1997 new   sp    m2534      6
 4 Afghanistan AF    AFG    1997 new   sp    m3544      3
 5 Afghanistan AF    AFG    1997 new   sp    m4554      5
 6 Afghanistan AF    AFG    1997 new   sp    m5564      2
 7 Afghanistan AF    AFG    1997 new   sp    m65        0
 8 Afghanistan AF    AFG    1997 new   sp    f014       5
 9 Afghanistan AF    AFG    1997 new   sp    f1524     38
10 Afghanistan AF    AFG    1997 new   sp    f2534     36
# … with 76,036 more rows

然后我们删除 new 这一列,这列值是一个常量。同时,我们也删除 iso2iso3 列,它们是多余的 country 信息。

> who3 %>% 
+     count(new)
# A tibble: 1 x 2
  new       n
  <chr> <int>
1 new   76046
> who4 <- who3 %>% 
+     select(-new, -iso2, -iso3)

接下来,我们将 sexage 列分割为 sexage

> who5 <- who4 %>% 
+     separate(sexage, c("sex", "age"), sep = 1)
> who5
# A tibble: 76,046 x 6
   country      year type  sex   age   cases
   <chr>       <int> <chr> <chr> <chr> <int>
 1 Afghanistan  1997 sp    m     014       0
 2 Afghanistan  1997 sp    m     1524     10
 3 Afghanistan  1997 sp    m     2534      6
 4 Afghanistan  1997 sp    m     3544      3
 5 Afghanistan  1997 sp    m     4554      5
 6 Afghanistan  1997 sp    m     5564      2
 7 Afghanistan  1997 sp    m     65        0
 8 Afghanistan  1997 sp    f     014       5
 9 Afghanistan  1997 sp    f     1524     38
10 Afghanistan  1997 sp    f     2534     36
# … with 76,036 more rows

现在,数据已经变得非常整洁了。

最后,我们使用管道操作符将上面的代码进行简化

who %>%
  pivot_longer(
    cols = new_sp_m014:newrel_f65, 
    names_to = "key", 
    values_to = "cases", 
    values_drop_na = TRUE
  ) %>% 
  mutate(
    key = stringr::str_replace(key, "newrel", "new_rel")
  ) %>%
  separate(key, c("new", "var", "sexage")) %>% 
  select(-new, -iso2, -iso3) %>% 
  separate(sexage, c("sex", "age"), sep = 1)
6.1 思考练习
  1. 在本例中,我们通过设置 values_drop_na = TRUE,方便检查数据是否具有正确的值。这样做合理吗?考虑一下该数据集中的缺失值代表的含义,是否存在隐式缺失值?NA0 有什么区别?

  2. 上面的示例中如果忽略 mutate() 这一步骤会发生什么?

  3. 计算每个国家、每年和每种性别的结核病例总数,并对数据进行可视化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

名本无名

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值