# 資料組合
當處理資料時,有時候會新增刪減欄位、資料,之前我們介紹過 R 簡單的資料結構,並使用 data.frame 來儲存管理資料。一般來說如果單純要在 data.frame 中加上新的欄位,我們可以使用 ```cbind()``` (也就是 <font color="red">c</font>olumn <font color="red">bind</font>的意思,把欄位綁在一起),如下面的範例:
```{r}=
# 把 mtcars 加上一個新欄位,
# 並把內容填 'a'
> mtcars_addnc <- cbind(mtcars, 'a')
> head(mtcars_addnc)
mpg cyl disp hp drat wt qsec vs am gear carb "a"
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 a
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 a
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 a
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 a
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 a
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 a
```
如果要新增列呢?和 ```cbind()``` 很類似,可使用 ```rbind()``` 來新增列
```{r}=
> rbind(mtcars_addnc, 0)
```
除了這樣新增的方式之外,```cbind()``` 和 ```rbind()``` 常常使用於組合兩個不同的 data.frame,我們把 mtcars 拆成兩個子資料集,來練習使用資料的組合。
1. 先把 ```mtcars``` 拆成兩個不同的 data.frame,分別為 mtcars1 和 mtcars2,其中前者包含 mpg, cyl 和 disp;後者則是 hp, drat 以及 wt
```{r}=
# 把 mtcars 拆成兩個子資料集(subset)
> mtcars1 <- mtcars[, c('mpg','cyl','disp')]
> head(mtcars1)
mpg cyl disp
Mazda RX4 21.0 6 160
Mazda RX4 Wag 21.0 6 160
Datsun 710 22.8 4 108
Hornet 4 Drive 21.4 6 258
Hornet Sportabout 18.7 8 360
Valiant 18.1 6 225
> mtcars2 <- mtcars[, c('hp','drat','wt')]
> head(mtcars2)
hp drat wt
Mazda RX4 110 3.90 2.620
Mazda RX4 Wag 110 3.90 2.875
Datsun 710 93 3.85 2.320
Hornet 4 Drive 110 3.08 3.215
Hornet Sportabout 175 3.15 3.440
Valiant 105 2.76 3.460
```
2. 使用使用 cbind 把 mtcars1 和 mtcars2 合在一起
```{r}=
> mtcars_cbind <- cbind(mtcars1, mtcars2)
# 合併完之後看一下前幾筆是否正常
> head(mtcars_cbind)
mpg cyl disp hp drat wt
Mazda RX4 21.0 6 160 110 3.90 2.620
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875
Datsun 710 22.8 4 108 93 3.85 2.320
Hornet 4 Drive 21.4 6 258 110 3.08 3.215
Hornet Sportabout 18.7 8 360 175 3.15 3.440
Valiant 18.1 6 225 105 2.76 3.460
```
同樣的,```rbind()``` 也可以用來組合兩個以上的 data.frame。不過如果你直接使用 ```rbind(mtcars1, mtcars2)```是無法成功的,因為兩個 data.frame 的欄位名稱是不同的。所以在合併多個 data.frame 時,一定要有相同的欄位,以[關聯式資料庫(relational database)](https://zh.wikipedia.org/zh-tw/關聯式資料庫)的術語來說就是要有一組 key (鍵),這組鍵可以把不同的資料表(data table,在這裡我們指的是兩個或以上的資料集)連結起來,此稱之為 join。在 R 裡頭內建的 ```merge()``` 就是一種 join 的方式(即 inner join),另外還有 left join 以及 right join 等,其示意圖如圖一,假設有兩個 data.frame df1 及 df2,如果 join 的方式要保留 df1 中所有的元素,這稱之為 left join;同樣地如果要保留 df2 所有的元素,則稱為 right join;如果只保留有交集的元素,我們稱之為 inner join。下一節我們將介紹如何使用 merge 來合併資料。

圖一、不同類型的 join
## 使用 merge 來合併資料
如果你曾經使用過 Excel 或是類似的試算表軟體,有個功能 vlookup 和這裡的 merge 是非常類似的,都可以針對某個相同欄位來合併資料。我們把合併資料的概念圖繪製如下呈現:

圖二、針對相同欄位合併的示意圖
假設有兩個 data.frame ,其中各有一個欄位 X 都是相同的,我們就可以針對 X 把這兩個 data.frame 合併在一起。在 R 裡頭我們可以這樣做:
```{r}=
# 先建立虛擬的 data.frame
# x,y,z,a,b 都是一維的 vector
x <- c(1,3,5,7)
y <- c('a','b','a','c')
z <- rnorm(4)
a <- sample(4)
b <- c('F','F','T','T')
# 使用 cbind 組合起來,再轉成 data.frame 格式
df1 <- as.data.frame(cbind(x,y,z))
df2 <- as.data.frame(cbind(x,a,b))
```
在上面的程式碼中我們建立了兩個虛擬的 data.frame:df1 和 df2,最後只要用 ```merge(df1, df2)``` 就能把這兩個 data.frame 的資料合併起來。
```{r}=
# 顯示 df1
> df1
x y z
1 1 a -1.52367624427762
2 3 b 0.420508839197317
3 5 a -0.162735214516669
4 7 c -0.233717624528657
# 顯示 df2
> df2
x a b
1 1 4 F
2 3 3 F
3 5 2 T
4 7 1 T
# 合併 df1 和 df2
> merge(df1, df2)
x y z a b
1 1 a -1.52367624427762 4 F
2 3 b 0.420508839197317 3 F
3 5 a -0.162735214516669 2 T
4 7 c -0.233717624528657 1 T
```
## 進階的 merge: 不同的 join 方式
前一小節的範例中,df1 和 df2 共有的欄位 x 都有四個相同數值(也就是 1,3,5,7),可是如果 df1 少了一個數值(假設把第四列移除),和 df2 合併的時候結果會怎麼樣呢?示意圖請參照圖三

圖三、當 df1 沒有 df2 的數值時
所以如果要保留所有 df2 的 X 欄位,合併完的 Y 欄位和 Z 欄位的 x41 就對應不到 df1 中之數值,所以會留空。以 ```merge()``` 來說,預設會使用 inner join,也就是只有 df1 和 df2 交集的資料才會合併在一起:
```{r}=
# 把第四列數值拿掉
> df1 <- df1[-4,]
# 如果 df1 或 df2 其中有數值沒有對應到
# merge 預設會用 inner join (只有交集)
> merge(df1, df2)
x y z a b
1 1 a -1.52367624427762 4 F
2 3 b 0.420508839197317 3 F
3 5 a -0.162735214516669 2 T
```
如果要實作 left 或 right join 呢?```merge()``` 中只要加上參數 ```all.x = TRUE``` 或是 ```all.y = TRUE``` 即可,x 指的是第一個提到的 df1, y 則是 df2,也可以直接在 ```merge()``` 的參數中指定,例如 ```merge(x=df1, y=df2)```。
```{r}=
# left join
> merge(df1, df2, all.x = TRUE)
x y z a b
1 1 a -1.52367624427762 4 F
2 3 b 0.420508839197317 3 F
3 5 a -0.162735214516669 2 T
# right join
> merge(df1, df2, all.y = TRUE)
x y z a b
1 1 a -1.52367624427762 4 F
2 3 b 0.420508839197317 3 F
3 5 a -0.162735214516669 2 T
4 7 <NA> <NA> 1 T
```
除了 R 內建 base 的 merge 之外,你也可以嘗試使用 Wickham 寫的 [dplyr](https://cran.r-project.org/web/packages/dplyr/vignettes/dplyr.html) 套件中的 [```left_join()```](https://dplyr.tidyverse.org/reference/join.html)、[```right_join()```](https://dplyr.tidyverse.org/reference/join.html)及[```inner_join()```](https://dplyr.tidyverse.org/reference/join.html)等功能。在 dplyr 中 join 的使用可參見下表:
| 指令 | 說明 |
| ----------- | -------------------- |
| inner_join | join 之後會把所有 x 在 y 之中符合的元素抓出來 |
| left_join | 保留所有在 x 之中的元素,如果 x 的數值在 y 之中找不到,就會回傳 NA |
| right_join | 和 left_join 相反,保留所有 y 的元素 |
| full_join | 也就是 x 和 y 的聯集,如果沒有符合的數值,就會回傳 NA |
下方的範例是使用 dplyr 來 join 兩個 data.frame:
```{r}=
library(dplyr)
left_join(df1, df2)
```
結果如下:
```
Joining, by = "x"
x y z a b
1 1 a -1.52367624427762 4 F
2 3 b 0.420508839197317 3 F
3 5 a -0.162735214516669 2 T
```
也可以使用 dplyr/magrittr 特殊符號 ```%>%``` 來把左邊的執行 code 導向到右邊的,即:
```{r}=
df1 %>% left_join(df2)
# 等同於 left_join(df1, df2)
```
[本節範例 code 下載](https://raw.githubusercontent.com/mutolisp/ecoinformatics_course/master/demos/data_merge.R)
## 作業
1. 請使用 ```merge()``` 來合併 [twforest](https://raw.githubusercontent.com/mutolisp/ecoinformatics_course/master/data/twforest.csv) 和 [twforest_families](https://raw.githubusercontent.com/mutolisp/ecoinformatics_course/master/data/twforest_families.csv) 這兩個檔案
2. 請練習使用 ```merge()``` 或是 ```dplyr::left_join()```, ```dplyr::right_join()``` 把 [twforest_families](https://raw.githubusercontent.com/mutolisp/ecoinformatics_course/master/data/twforest_families.csv) 和 [apgiv_families](https://raw.githubusercontent.com/mutolisp/ecoinformatics_course/master/data/apgiv_families.csv) 合併成下列的格式
| family | family_zh | species |
| --------- | ---------- | ------- |
| Pentaphyllaceae | 五列木科 | 台灣楊桐 |
| Aquifoliaceae | 冬青科 | 燈稱花 |
| ... | ... | ... |
並請依照 family 的字母排序