Ching-Chuan Chen's Blogger

Statistics, Machine Learning and Programming

0%

introduction to tidyr

這是我自己在PTT PO的文,詳細介紹tidyr,以下是正文~~

本篇是最後一篇,主要介紹tidyr,以下是要介紹的內容:

  1. data.table:::dcast.data.table

  2. data.table:::melt

  3. tidyr:::gather

  4. tidyr:::spread

  5. tidyr:::separate

這一章主要講述注重在資料的呈現方式 - 橫表跟直表的呈現

還有一些表格整合的問題

  1. dcast.data.table

dcast提供直表加總的函數

學過統計的話,應該是contingency table (列聯表)

或是熟悉EXCEL,知道樞紐分析表,它其實就是樞紐分析表

Y就是列聯表中的列變數,X就是行變數

製作列聯表也可以說它的應用之一

這個function,需要先require(reshape2)

有人可能會問reshape2就有dcast為啥要用dcast.data.table

原因很簡單,因為dcast.data.table快更多!!

速度直接?dcast.data.table下面例子就有,直接來簡介怎麼用

第一個input是data.table,第二個是給一個公式

舉例來說,如果公式是 Y ~ X,Y的元素會展開在列,X就會在行

第三個input是加總函數,你如果有相同類別的X, Y

它會把相同類別的值用這個函數做加總,預設是length

先用一個簡單例子來說明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  set.seed(100)
DT = expand.grid(LETTERS[1:2], LETTERS[3:4]) %>% data.table %>%
setnames(c("col1","col2")) %>% '['(rep(1:nrow(.), 2)) %>%
'['(,values := rpois(8,2))
DT
# col1 col2 values
# 1: A C 1
# 2: B C 1
# 3: A D 2
# 4: B D 0
# 5: A C 2
# 6: B C 2
# 7: A D 3
# 8: B D 1

dcast.data.table(DT, col1~col2)
# col1 C D
# 1: A 2 2
# 2: B 2 2

dcast.data.table(DT, col1~col2, sum)
# col1 C D
# 1: A 3 5
# 2: B 3 1

產生資料的函數、operator,我們都講過了,往前找找看

我們專注到第一個dcast,dcast.data.table(DT, col1~col2)

可以看的出來 col1就在列,col2就在行展開,然後計算col1, col2有相同類別的length

第二個dcast就是把有相同的類別,把values做總和

但是,我們怎麼知道它加總的是values

它會告訴你自動找尋data.table,然後選定values做為加總的column

至於改法就是修改value.var這個input,舉例來說

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  DT[, values2 := rpois(8, 3)]
dcast.data.table(DT, col1~col2, sum, value.var = "values")
# col1 C D
# 1: A 3 5
# 2: B 3 1

dcast.data.table(DT, col1~col2, sum, value.var = "values2")
# col1 C D
# 1: A 5 7
# 2: B 3 9

DT[, col3 := rep(LETTERS[5:6],,,4)]
dcast.data.table(DT, col1+col2~col3, sum, value.var = "values")
# col1 col2 E F
# 1: A C 1 2
# 2: A D 2 3
# 3: B C 1 2
# 4: B D 0 1

dcast.data.table說明到此

  1. melt

dcast做直表加總,melt做橫表轉直表

舉個簡單的例子

我們有數個病人,每個病人有數個觀察值

表格紀錄的樣子是

id O1 O2
P1 12 18
P2 13 15
.. .. ..

我們想要轉成直表長這樣:

id O V
P1 O1 12
P1 O1 18
P2 O2 13
P2 O2 15
.. .. ..

那麼在R code可以這樣做:

1
2
3
4
5
6
7
8
9
10
11
  (DT = data.table(id = paste0("P", 1:2), O1 = c(12,13), O2 = c(18,15)))
# id O1 O2
# 1: P1 12 18
# 2: P2 13 15

(DT_long = melt(DT, "id", variable.name = "O", value.name = "V"))
# id O V
# 1: P1 O1 12
# 2: P2 O1 13
# 3: P1 O2 18
# 4: P2 O2 15

melt的第一個input是data.table (註一)

第二個input是id.vars,也就是你要展開的變數名稱

第三個input是measure.vars,你要展開的變數名稱

前面例子未指定的情況下,就是全部的column

前面我還有用到variable.name 跟 value.name

variable.name是指定集合其他columns之後的column name

在前例就是把 O1, O2兩個columns集合之後的變數名稱,我改成了”O”

value.name是你集合其他columns之後那些變數值的column

在前例就是把 O1, O2兩個columns集合之後的變數值,我改成了”V”

註一:此指data.table:::melt,跟reshape2:::melt的差異部分

請看data.table:::melt的help

我們再看一個複雜一點的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  DT = data.table(ID1 = paste0("ID1_", 1:20),
ID2 = sample(paste0("ID2_", 1:20)),
O1 = rnorm(20), O2 = rnorm(20), O3 = rnorm(20))

## 以ID1跟ID2作為展開,其他column (O1 ~ O3)會疊成一個變數
## 還會有一個新類別去label後面的value來自哪一個變數
melt(DT, c("ID1", "ID2"), c("O1", "O2", "O3"),
variable.name = "O", value.name = "V")

## 以ID1作為展開,其他column (O1 ~ O3)會疊成一個變數
## 還會有一個新類別去label後面的value來自哪一個變數
melt(DT, "ID1", c("O1", "O2", "O3"),
variable.name = "O", value.name = "V")

## 以ID1作為展開,其他column (O1 ~ O2)會疊成一個變數
## 還會有一個新類別去label後面的value來自哪一個變數
melt(DT, "ID1", c("O1", "O2"),
variable.name = "O", value.name = "V")
  1. gather

其實就是melt,只是比較好寫

我們把melt的例子改成用gather寫

只是melt一次到位的指令用gather寫之後

要用select跟filter做 (但是我覺得gather比較好寫)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  (DT = data.table(id = paste0("P", 1:2), O1 = c(12,13), O2 = c(18,15)))
# id O1 O2
# 1: P1 12 18
# 2: P2 13 15

(DT_long = gather(DT, O, V, -id))
# id O V
# 1: P1 O1 12
# 2: P2 O1 13
# 3: P1 O2 18
# 4: P2 O2 15

DT = data.table(ID1 = paste0("ID1_", 1:20),
ID2 = sample(paste0("ID2_", 1:20)),
O1 = rnorm(20), O2 = rnorm(20), O3 = rnorm(20))
gather(DT, O, V, -ID1, -ID2)
gather(DT, O, V, -ID1, -ID2) %>% select(-ID2)
gather(DT, O, V, -ID1, -ID2) %>% select(-ID2) %>% filter(O!="O3")
  1. spread

提供gather的反向操作

1
2
3
4
5
6
  DT = data.table(id = paste0("P", 1:2), O1 = c(12,13), O2 = c(18,15))
DT_long = gather(DT, O, V, -id)
DT_long %>% spread(O, V)
# id O1 O2
# 1: P1 12 18
# 2: P2 13 15
  1. separate

把特定column做strsplit,並設定成新的變數

一個簡單的例子

1
2
DT = data.table(x = paste0(sample(LETTERS, 5), ",", sample(LETTERS, 5)))
DT %>% separate(x, paste0("V", 1:2))

這個函數要注意的是以下的程式是會出現錯誤的

1
2
DT = data.table(x = paste0(sample(LETTERS, 5), sample(LETTERS, 5)))
DT %>% separate(x, paste0("V", 1:2))

separate無法分開沒有間隔字元的字串

你要分開這個只能做適當的轉換,像是:

1
2
3
DT = data.table(x = paste0(sample(LETTERS, 5), sample(LETTERS, 5)))
DT %<>% mutate(x = gsub("([A-Z])", "\\1, ", x))
DT %>% separate(x, paste0("V", 1:3)) %>% select(-V3)

如果有人有更好的方法,麻煩告知我一下,謝謝

資料介紹套件就到這裡結束

有任何問題,歡迎在板上回文詢問,我有看到都會回覆

(麻煩盡量不要用私信,希望可以讓板眾一起看問題該怎麼解決)

有任何補充或是建議也歡迎推文或回文,感謝大家

我並沒有講到plyr的 aply, lply, dply, rply系列

其實他們跟apply, lapply, tapply, replicate相對應,只是output型式不同

如果未來有機會寫有關*ply系列函數時,我再好好介紹plyr