R 數(shù)據(jù)處理(十三)—— tidyr

1. 前言

在本節(jié)中胸囱,我們將介紹數(shù)據(jù)的整潔之道康愤,以及 tidyverse 的核心包之一 tidyr

它提供了一系列工具來幫助整理混亂的數(shù)據(jù)隔披,也是我們本節(jié)的重點。

library(tidyverse)

2. 整潔數(shù)據(jù)

每份數(shù)據(jù)都可以有不同的表現(xiàn)方式寂拆。下面的例子中奢米,我們以四種不同的方式來展示相同的數(shù)據(jù)。

每個數(shù)據(jù)都包括 country, year, populationcases 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ī)則

  1. 每個變量必須要有自己的列
  2. 每個觀察值必須在自己行中
  3. 每個值必須在自己的單元格內(nèi)

下面的圖片也展示了這三條規(guī)則

image.png

這三條規(guī)則是相互關(guān)聯(lián)的,不可能只滿足這三條規(guī)則中的兩條茂装。這種相互關(guān)系產(chǎn)生了一套更簡單實用的說明

  1. 將每個數(shù)據(jù)集存為 tibble
  2. 每個變量代表一列

在上面的示例中怠蹂,只有 table1 是整齊的,每個變量表示為一列的形式

為什么要確保數(shù)據(jù)整潔呢少态?主要有以下兩個優(yōu)點

  1. 以一種一致的方式存儲數(shù)據(jù)是有優(yōu)勢的城侧,如果你的數(shù)據(jù)結(jié)構(gòu)保持一致,則更容易學(xué)習(xí)和使用這些工具函數(shù)

  2. 在每個變量作為一列有一個優(yōu)點彼妻,它能充分發(fā)揮 R 的向量化特性嫌佑。 R 大多數(shù)的內(nèi)置函數(shù)都可以與值向量一起使用,這使得對數(shù)據(jù)的轉(zhuǎn)換顯得特別自然

dplyr侨歉、ggplot2tidyverse 中所有的其他包都被設(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))
image

2.1 思考練習(xí)

  1. 計算 table2table4a + table4b 的比率 rate幽邓,你可能需要執(zhí)行下面 4 個操作
  • 提取每個國家每年的結(jié)核病病例數(shù)
  • 提取每個國家/地區(qū)每年匹配人口數(shù)
  • 將病例數(shù)除以人口數(shù)炮温,再乘以 10000
  • 然后將比率放在適合的位置上

哪種表示法最容易使用?最難的是哪一個?為什么?

  1. 使用 table2 代替 table1 重新創(chuàng)建病例隨時間變化的圖。您首先需要做什么牵舵?

3. 透視表

通常在真實的分析中柒啤,我們遇到的數(shù)據(jù)都需要進(jìn)一步進(jìn)行一些整理。

第一步需要弄清楚變量和觀察值是什么畸颅,有時候這很容易担巩,但是有時可能需要詢問數(shù)據(jù)提供者

第二步需要解決下面兩個常見問題:

  1. 一個變量可能分布在多個列上
  2. 一種觀察值可能分散在多行中

要解決這些問題,您可以使用 tidyr 提供的兩個最重要的函數(shù):pivot_longer()pivot_wider()没炒。

3.1 Longer

一個常見的問題是兵睛,數(shù)據(jù)集中的列名不是變量名而是變量的值

比如說表 table4a,它的列名 19992000 代表的是年份變量的值,而這兩列的值表示的是對應(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ù):

  • 列名是值而不是變量笨鸡,在本例中姜钳,是 19992000
  • 要將列名移至的變量名,在這里是 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() 樣式表示法指定哥桥。但是這里只有兩列,因此將它們單獨列出

注意激涤,19992000 是非語法名稱(因為它們不是以字母開頭)拟糕,所以我們必須用反勾號將它們括起來

因為 table4a 中不存在 yearcases 列名,因此我們將其名稱用引號引起來倦踢。

image.png

在最后的結(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

要將整理后的 table4atable4b 合并到一個 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)觀察值分散在多行中時,可以使用它园爷。

例如宠蚂,在 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() 的方式分析。不過腮介,這次我們只需要兩個參數(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
image.png

可能你已經(jīng)從它們的名字中猜到了,pivot_wilder()pivot_longer() 是互補的旅东。

pivot_longer() 使寬表變窄和變長灭抑; pivot_wider() 使長表變短和變寬

3.3 思考練習(xí)

  1. 為什么 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)。它的作用是什么?

  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. 如果你要加寬表會發(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
)
  1. 整理下面的簡單表访递。你需要將表變寬還是變長?變量要設(shè)置成什么同辣?
preg <- tribble(
  ~pregnant, ~male, ~female,
  "yes",     NA,    10,
  "no",      20,    12
)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拷姿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子旱函,更是在濱河造成了極大的恐慌响巢,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棒妨,死亡現(xiàn)場離奇詭異踪古,居然都是意外死亡,警方通過查閱死者的電腦和手機靶衍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門灾炭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颅眶,你說我怎么就攤上這事蜈出。” “怎么了涛酗?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵铡原,是天一觀的道長。 經(jīng)常有香客問我商叹,道長燕刻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任剖笙,我火速辦了婚禮卵洗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弥咪。我一直安慰自己过蹂,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布聚至。 她就那樣靜靜地躺著酷勺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扳躬。 梳的紋絲不亂的頭發(fā)上脆诉,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天甚亭,我揣著相機與錄音,去河邊找鬼击胜。 笑死亏狰,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的潜的。 我是一名探鬼主播骚揍,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼啰挪!你這毒婦竟也來了信不?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤亡呵,失蹤者是張志新(化名)和其女友劉穎抽活,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锰什,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡下硕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了汁胆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梭姓。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嫩码,靈堂內(nèi)的尸體忽然破棺而出誉尖,到底是詐尸還是另有隱情,我是刑警寧澤铸题,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布铡恕,位于F島的核電站,受9級特大地震影響丢间,放射性物質(zhì)發(fā)生泄漏探熔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一烘挫、第九天 我趴在偏房一處隱蔽的房頂上張望诀艰。 院中可真熱鬧,春花似錦饮六、人聲如沸其垄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捉捅。三九已至撤防,卻和暖如春虽风,著一層夾襖步出監(jiān)牢的瞬間棒口,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工辜膝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留无牵,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓厂抖,卻偏偏與公主長得像茎毁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子忱辅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容