# 資料組合 當處理資料時,有時候會新增刪減欄位、資料,之前我們介紹過 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 來合併資料。 ![](https://github.com/mutolisp/ecoinformatics_course/blob/master/images/join.png?raw=true) 圖一、不同類型的 join ## 使用 merge 來合併資料 如果你曾經使用過 Excel 或是類似的試算表軟體,有個功能 vlookup 和這裡的 merge 是非常類似的,都可以針對某個相同欄位來合併資料。我們把合併資料的概念圖繪製如下呈現: ![](https://github.com/mutolisp/ecoinformatics_course/blob/master/images/data_merge.png?raw=true) 圖二、針對相同欄位合併的示意圖 假設有兩個 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 合併的時候結果會怎麼樣呢?示意圖請參照圖三 ![](https://github.com/mutolisp/ecoinformatics_course/blob/master/images/data_merge_na.png?raw=true) 圖三、當 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 的字母排序