1. 前言
在本节中,我们将介绍数据的整洁之道,以及 tidyverse
的核心包之一 tidyr
。
它提供了一系列工具来帮助整理混乱的数据,也是我们本节的重点。
library(tidyverse)
2. 整洁数据
每份数据都可以有不同的表现方式。下面的例子中,我们以四种不同的方式来展示相同的数据。
每个数据都包括 country
, year
, population
和 cases
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
这些都是相同基础数据的表示形式,但它们并不都是易于使用的。
数据的整洁之道主要有下面三个相互关联的规则
- 每个变量必须要有自己的列
- 每个观察值必须在自己行中
- 每个值必须在自己的单元格内
下面的图片也展示了这三条规则
这三条规则是相互关联的,不可能只满足这三条规则中的两条。这种相互关系产生了一套更简单实用的说明
- 将每个数据集存为
tibble
- 每个变量代表一列
在上面的示例中,只有 table1
是整齐的,每个变量表示为一列的形式
为什么要确保数据整洁呢?主要有以下两个优点
-
以一种一致的方式存储数据是有优势的,如果你的数据结构保持一致,则更容易学习和使用这些工具函数
-
在每个变量作为一列有一个优点,它能充分发挥
R
的向量化特性。R
大多数的内置函数都可以与值向量一起使用,这使得对数据的转换显得特别自然
dplyr
、ggplot2
和 tidyverse
中所有的其他包都被设计用于处理整洁的数据。下面是几个小示例,展示如何使用 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))
2.1 思考练习
- 计算
table2
和table4a + table4b
的比率rate
,你可能需要执行下面4
个操作
- 提取每个国家每年的结核病病例数
- 提取每个国家/地区每年匹配人口数
- 将病例数除以人口数,再乘以
10000
- 然后将比率放在适合的位置上
哪种表示法最容易使用?最难的是哪一个?为什么?
- 使用
table2
代替table1
重新创建病例随时间变化的图。您首先需要做什么?
3. 透视表
通常在真实的分析中,我们遇到的数据都需要进一步进行一些整理。
第一步需要弄清楚变量和观察值是什么,有时候这很容易,但是有时可能需要询问数据提供者
第二步需要解决下面两个常见问题:
- 一个变量可能分布在多个列上
- 一种观察值可能分散在多行中
要解决这些问题,您可以使用 tidyr
提供的两个最重要的函数:pivot_longer()
和 pivot_wider()
。
3.1 Longer
一个常见的问题是,数据集中的列名不是变量名而是变量的值
比如说表 table4a
,它的列名 1999
和 2000
代表的是年份变量的值,而这两列的值表示的是对应年份的 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
要整理这样的数据集,我们需要将有问题的列透视到一对新的变量中。
为了描述该操作,我们需要三个参数:
- 列名是值而不是变量,在本例中,是
1999
和2000
列 - 要将列名移至的变量名,在这里是
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()
样式表示法指定。但是这里只有两列,因此将它们单独列出
注意,1999
和 2000
是非语法名称(因为它们不是以字母开头),所以我们必须用反勾号将它们括起来
因为 table4a
中不存在 year
和 cases
列名,因此我们将其名称用引号引起来。
在最后的结果中,我们得到了两个新的列并删除了原来需要透视的两列。
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
要将整理后的 table4a
和 table4b
合并到一个 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()
相反,当观察值分散在多行中时,可以使用它。
例如,在 table2
中 type
观察值观察值分布在国家和年份的两行中
> 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
可能你已经从它们的名字中猜到了,pivot_wilder()
和 pivot_longer()
是互补的。
pivot_longer()
使宽表变窄和变长; pivot_wider()
使长表变短和变宽
3.3 思考练习
- 为什么
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)
。它的作用是什么?
- 为什么下面的代码是错的?
> 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.
- 如果你要加宽表会发生什么?为什么?您如何添加新列来唯一标识每个值?
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
)
- 整理下面的简单表。你需要将表变宽还是变长?变量要设置成什么?
preg <- tribble(
~pregnant, ~male, ~female,
"yes", NA, 10,
"no", 20, 12
)
4. 分隔和合并
到目前为止,你已经学会了如何整理 table2
和 table4
。
但是对于 table3
,有一个不一样的问题:我们有一列(rate
)包含两个变量(cases
和 population
)。
要解决这个问题,可以使用 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
列同时包含了 cases
和 population
,我们需要将其拆分为两个变量
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
在默认情况下,separate()
会在看到非字母数字字符(即不是数字或字母的字符)的任何位置对值进行拆分。
例如,在上面的代码中,separate()
将 rate
列的值用 /
分隔。如果要指定特定的分隔字符,可以将该字符传递给 separate()
的 sep
参数。例如,我们可以将上面的代码重写
table3 %>%
separate(rate, into = c("cases", "population"), sep = "/")
注意:sep
支持正则表达式
你可能也注意到 cases
和 population
列都是字符类型,这是 separate()
函数的默认行为,保持列的类型不变。
然而,在这里它并不适用,因为这些实际上是数字。我们可以使用 convert=TRUE
让 separate()
尝试将值转换为更好的类型
> 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()
相比,你用到它的频率要低得多,但它仍然是一个很有用的工具。
我们可以使用 unite()
重新合并上一个示例中创建的 century
和 year
列。该数据被保存为 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 思考练习
extra
和fill
参数在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"))
-
unite()
和separate()
都有一个remove
参数,它有什么作用?为什么要把它设置为FALSE
? -
比较
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 思考练习
-
将
fill
参数与pivot()
和complete()
进行比较。 -
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>
这是非常典型的真实数据,它包含冗余的列和许多缺失值,总之乱七八糟的。
通常处理这种数据需要用管道来组合 dplyr
、tidyr
中的操作函数。
通常,最好的着手点就是将非变量的列集合在一起,让我们来分析一下
- 似乎
country
,iso2
和iso3
这三列是冗余的国家信息 year
显然是一个变量- 我们还不知道其他所有列是什么,但是给定了变量名的结构(例如
new_sp_m014
,new_ep_m014
,new_ep_f014
),这些很可能是值而是变量
因此,我们需要收集从 new_sp_m014
到 newrel_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
你可以用自己的方式来解析这列数据,但是幸运的是,我们手里有这一数据的一些信息:
-
每列的前三个字母表示该列包含的是新的还是旧的
TB
病例,在这个数据中,每列包含的都是新的病例 -
后面两个字符代表
TB
的类型
rel
代表复发病例ep
代表肺外结核病例sn
代表无法通过肺部涂片诊断的肺结核病例(涂片阴性)sp
代表可以通过肺部涂片诊断的肺结核病例(涂片阳性)
-
第六个字符表示结核病患者的性别,男性(
m
)和女性(f
) -
剩下的数字表示年龄分组,数据将年龄分为七个年龄组。
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
这一列,这列值是一个常量。同时,我们也删除 iso2
和 iso3
列,它们是多余的 country
信息。
> who3 %>%
+ count(new)
# A tibble: 1 x 2
new n
<chr> <int>
1 new 76046
> who4 <- who3 %>%
+ select(-new, -iso2, -iso3)
接下来,我们将 sexage
列分割为 sex
和 age
> 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 思考练习
-
在本例中,我们通过设置
values_drop_na = TRUE
,方便检查数据是否具有正确的值。这样做合理吗?考虑一下该数据集中的缺失值代表的含义,是否存在隐式缺失值?NA
和0
有什么区别? -
上面的示例中如果忽略
mutate()
这一步骤会发生什么? -
计算每个国家、每年和每种性别的结核病例总数,并对数据进行可视化。