1. 前言
在本節(jié)中胸囱,我們將介紹數(shù)據(jù)的整潔之道康愤,以及 tidyverse
的核心包之一 tidyr
。
它提供了一系列工具來幫助整理混亂的數(shù)據(jù)隔披,也是我們本節(jié)的重點。
library(tidyverse)
2. 整潔數(shù)據(jù)
每份數(shù)據(jù)都可以有不同的表現(xiàn)方式寂拆。下面的例子中奢米,我們以四種不同的方式來展示相同的數(shù)據(jù)。
每個數(shù)據(jù)都包括 country
, year
, population
和 cases
4
個變量漓库,且具有相同的值恃慧,但是它們組織數(shù)據(jù)的方式是不一樣的。
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
這些都是相同基礎(chǔ)數(shù)據(jù)的表示形式渺蒿,但它們并不都是易于使用的痢士。
數(shù)據(jù)的整潔之道主要有下面三個相互關(guān)聯(lián)的規(guī)則
- 每個變量必須要有自己的列
- 每個觀察值必須在自己行中
- 每個值必須在自己的單元格內(nèi)
下面的圖片也展示了這三條規(guī)則
這三條規(guī)則是相互關(guān)聯(lián)的,不可能只滿足這三條規(guī)則中的兩條茂装。這種相互關(guān)系產(chǎn)生了一套更簡單實用的說明
- 將每個數(shù)據(jù)集存為
tibble
- 每個變量代表一列
在上面的示例中怠蹂,只有 table1
是整齊的,每個變量表示為一列的形式
為什么要確保數(shù)據(jù)整潔呢少态?主要有以下兩個優(yōu)點
以一種一致的方式存儲數(shù)據(jù)是有優(yōu)勢的城侧,如果你的數(shù)據(jù)結(jié)構(gòu)保持一致,則更容易學(xué)習(xí)和使用這些工具函數(shù)
在每個變量作為一列有一個優(yōu)點彼妻,它能充分發(fā)揮
R
的向量化特性嫌佑。R
大多數(shù)的內(nèi)置函數(shù)都可以與值向量一起使用,這使得對數(shù)據(jù)的轉(zhuǎn)換顯得特別自然
dplyr
侨歉、ggplot2
和 tidyverse
中所有的其他包都被設(shè)計用于處理整潔的數(shù)據(jù)屋摇。下面是幾個小示例,展示如何使用 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 思考練習(xí)
- 計算
table2
和table4a + table4b
的比率rate
幽邓,你可能需要執(zhí)行下面4
個操作
- 提取每個國家每年的結(jié)核病病例數(shù)
- 提取每個國家/地區(qū)每年匹配人口數(shù)
- 將病例數(shù)除以人口數(shù)炮温,再乘以
10000
- 然后將比率放在適合的位置上
哪種表示法最容易使用?最難的是哪一個?為什么?
- 使用
table2
代替table1
重新創(chuàng)建病例隨時間變化的圖。您首先需要做什么牵舵?
3. 透視表
通常在真實的分析中柒啤,我們遇到的數(shù)據(jù)都需要進(jìn)一步進(jìn)行一些整理。
第一步需要弄清楚變量和觀察值是什么畸颅,有時候這很容易担巩,但是有時可能需要詢問數(shù)據(jù)提供者
第二步需要解決下面兩個常見問題:
- 一個變量可能分布在多個列上
- 一種觀察值可能分散在多行中
要解決這些問題,您可以使用 tidyr
提供的兩個最重要的函數(shù):pivot_longer()
和 pivot_wider()
没炒。
3.1 Longer
一個常見的問題是兵睛,數(shù)據(jù)集中的列名不是變量名而是變量的值
比如說表 table4a
,它的列名 1999
和 2000
代表的是年份變量的值,而這兩列的值表示的是對應(yīng)年份的 cases
變量的數(shù)值祖很,而且每行是兩個變量的值笛丙,而不是一個。
> table4a
# A tibble: 3 x 3
country `1999` `2000`
<chr> <dbl> <dbl>
1 Afghanistan 745 2666
2 Brazil 37737 80488
3 China 212258 213766
要整理這樣的數(shù)據(jù)集假颇,我們需要將有問題的列透視到一對新的變量中胚鸯。
為了描述該操作,我們需要三個參數(shù):
- 列名是值而不是變量笨鸡,在本例中姜钳,是
1999
和2000
列 - 要將列名移至的變量名,在這里是
year
- 要將列的值移動到的變量名稱形耗。在這里是
cases
將這些參數(shù)傳遞到 pivot_longer()
函數(shù)中
> 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
列名,因此我們將其名稱用引號引起來倦踢。
在最后的結(jié)果中送滞,我們得到了兩個新的列并刪除了原來需要透視的兩列。
pivot_longer()
通過增加行數(shù)和減少列數(shù)使數(shù)據(jù)集變長辱挥,當(dāng)然這里說的長主要是相對原來的數(shù)據(jù)而言的犁嗅。
我們可以使用 pivot_longer()
以類似的方式整理 table4b
,唯一的區(qū)別是存儲在單元格中的變量值
> 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()
進(jìn)行連接
> 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()
相反褂微,當(dāng)觀察值分散在多行中時,可以使用它园爷。
例如宠蚂,在 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()
的方式分析。不過腮介,這次我們只需要兩個參數(shù)
- 要從中獲取變量名的列,在這里是
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
可能你已經(jīng)從它們的名字中猜到了,pivot_wilder()
和 pivot_longer()
是互補的旅东。
pivot_longer()
使寬表變窄和變長灭抑; pivot_wider()
使長表變短和變寬
3.3 思考練習(xí)
- 為什么
pivot_wilder()
和pivot_longer()
不完全對稱?仔細(xì)思考下面的例子抵代。
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
參數(shù)腾节,例如 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.
- 如果你要加寬表會發(fā)生什么庆冕?為什么?您如何添加新列來唯一標(biāo)識每個值劈榨?
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
)
- 整理下面的簡單表访递。你需要將表變寬還是變長?變量要設(shè)置成什么同辣?
preg <- tribble(
~pregnant, ~male, ~female,
"yes", NA, 10,
"no", 20, 12
)