資料合併 (merge) 的方法整理

這邊的資料合併,指的是利用兩個資料表當中具相同名稱,且內容可以互相對應的欄位,來合併兩個資料表。
舉個例子,
我們手上有兩個表,其中一個是 survey,包含外出調查時記錄到的鳥種英文俗名 (Common Name) 以及調查到的隻數 (Count)。

Survey
Common Name Count
Kentish Plover 150
Common Teal 300
Little Egret 20
Common Teal 200

另外一個表是 waterbird,包含水鳥的分類資訊,英文俗名 (Common Name) 以及科名 (Family)。

waterbird
Common Name Family
Common Teal Anatidae
Little Egret Ardeidae
Kentish Plover Charadriidae

這邊說的資料合併就是像下面這樣,依據兩個表都有的 Common Name,把第二個表 (waterbird) 的 Family 資訊加到第一個表 (survey) 相對應的物種上。在做資料合併時,提供額外資訊的表 不可以 有重複項目在內,像這邊的例子,在 waterbird 這個表格中 Common Name 的欄位內就不可有重複的物種。

Final Table
Common Name Count Family
Kentish Plover 150 Charadriidae
Common Teal 300 Anatidae
Little Egret 20 Ardeidae
Common Teal 200 Anatidae

Excel 內,可以透過 VLOOKUP 來做,那在 R 裡面也有許多的 package 提供相同的功能。
以下就來介紹幾個在資料合併中常見的 package~

base::merge(x, y, by, …)

位在 R 內建的 base 裡面,在使用時有幾個參數的設定要注意。

x, y

分別放兩個要合併表格

by/ by.x/ by.y

用來指定兩個表格中相同的欄位名稱,也就是合併的依據,如果沒有特別指定,merge 也會自己去找相同的欄位來合併資料。另外如果兩個表格中要用來合併的欄位名稱不同的時候,可以分別用by.x = “欄位A”以及by.y = “欄位B”,來告訴 merge 合併的依據分別是表格 x 的 A 欄位以及表格 y 的 B 欄位。指定欄位的時候記得都要用“”來包住欄位名稱。

all /all.x / all.y

這三個分別用來指定回傳資料的內容,當 all = TRUE 的時候,R 會回傳兩個表格中所有的資料,包含沒有互相對應到的內容。反之如果 all = FALSE,就只會回傳兩個表格中都有對應到的內容。而 all.x = TRUE 指的則是保留所有表格 x 的值,不管有沒有對應到 y 表格。

先創造範例會用到的兩個 data.frame,survey 跟 waterbird。

library(data.table)
survey <- 
  data.table(`Common Name` = 
               c("Kentish Plover", "Common Teal", "Little Egret", 
                 "Common Teal", "Light-vented Bulbul"),
             `Count` = 
               c(150, 300, 20, 200, 5))
survey
##            Common Name Count
## 1:      Kentish Plover   150
## 2:         Common Teal   300
## 3:        Little Egret    20
## 4:         Common Teal   200
## 5: Light-vented Bulbul     5
waterbird <- 
  data.table(`Common Name` = 
               c("Kentish Plover", "Common Teal", 
                 "Little Egret", "Pacific Golden Plover"),
             Family = 
               c("Charadriidae", "Anatidae", 
                 "Ardeidae", "Charadriidae"))
waterbird
##              Common Name       Family
## 1:        Kentish Plover Charadriidae
## 2:           Common Teal     Anatidae
## 3:          Little Egret     Ardeidae
## 4: Pacific Golden Plover Charadriidae


EX1 all = FALSE

merge(survey, waterbird)
##       Common Name Count       Family
## 1:    Common Teal   300     Anatidae
## 2:    Common Teal   200     Anatidae
## 3: Kentish Plover   150 Charadriidae
## 4:   Little Egret    20     Ardeidae

在merge()當中,all 的預設為 FALSE,所以survey 裡面的 Light-vented Bulbul,跟 waterbird裡面的 Pacific Golden Plover 因為在另一個表單中沒有,所以不會出現在合併的表格中。

EX2 all = TRUE

merge(survey, waterbird, all = TRUE)
##              Common Name Count       Family
## 1:           Common Teal   300     Anatidae
## 2:           Common Teal   200     Anatidae
## 3:        Kentish Plover   150 Charadriidae
## 4:   Light-vented Bulbul     5           NA
## 5:          Little Egret    20     Ardeidae
## 6: Pacific Golden Plover    NA Charadriidae

如果改成 all = TRUE 的話,就會回傳所有的資料,並且沒有對應到的部分會是 NA。

EX3 all.x = TRUE

merge(survey, waterbird, all.x = TRUE)
##            Common Name Count       Family
## 1:         Common Teal   300     Anatidae
## 2:         Common Teal   200     Anatidae
## 3:      Kentish Plover   150 Charadriidae
## 4: Light-vented Bulbul     5           NA
## 5:        Little Egret    20     Ardeidae

如果寫成 all.x = TRUE,則只會回傳 survey 當中,在 waterbird 內有資料的物種,在 waterbird 沒有對應到的 survey 物種資料不會保留下來。

dplyr :: _ join 系列

在package dplyr也有一系列跟資料合併有關的函數,不過跟merge不同的是,在dplyr中是直接用不同的函數來指定回傳的內容。

inner_join(df1, df2, by)

資料合併後,回傳 df1 與 df2 在對應欄位中,互相有對應到的內容,等同於 merge(df1, df2, all = FALSE)

left_join(df1, df2, by)

資料合併後,回傳在左邊 df1 的對應欄位中有的內容,不管有沒有對應到 df2,等同於 merge(df1, df2, all.x = TRUE)

right_join(df1, df2, by)

資料合併後,回傳在右邊 df2 的對應欄位中有的內容,不管有沒有對應到 df1,等同於 merge(df1, df2, all.y = TRUE)

full_join(df1, df2, by)

回傳 df1 與 df2 合併後的所有內容,等同於 merge(df1, df2, all = TRUE)

semi_join(df1, df2, by)

只回傳 df1 當中與 df2 在對應欄位中有對應到的 df1 內容。

anti_join(df1, df2, by)

回傳 df1 當中 沒有 對應到 df2 對應欄位的內容。
semi_join 跟 anti_join 在資料合併時相對比較少使用,但我覺得在除錯或是檢查資料的時候還蠻好用的,可以很快速地幫你找到沒有對應到的資料。

library(dplyr)

這邊只看 anti_join 跟 semi_join,其他跟 merge 功能重複的就不列入示範了,另外 by 參數的設定跟 merge 一樣,用來指定欄位名稱,若沒有指定的話,R 也會自己去找兩個資料中相同名稱的欄位。

EX1 anti_join()

anti_join(survey, waterbird)
## Joining, by = "Common Name"
##           Common Name Count
## 1 Light-vented Bulbul     5

回傳有在 survey 的物種名稱欄位中,但沒有在 waterbird 裡面的 survey 資料。

EX2 semi_join()

semi_join(survey, waterbird)
## Joining, by = "Common Name"
##      Common Name Count
## 1 Kentish Plover   150
## 2    Common Teal   300
## 3   Little Egret    20
## 4    Common Teal   200

回傳有在 waterbird 物種名稱欄位中的 survey 資料,沒有合併上 waterbird 的欄位。

data.table DT1[DT2, on = " “]

在 data.table 中,也可以做資料合併,不過功能上沒有 dplyr 的 join 系列來的多元,只有 inner_join 與 left_join / right_join。在data.table中,放在 [ ] 內的 DT2 為主要的資料,用nomatch =來告訴 R 在 DT1 的資料中,沒有對應到 DT2 的部分要不要保留下來,另外是在這邊指定欄位的 on = 不能省略。

EX1 left/ right join

waterbird[survey, on = "Common Name"]
##            Common Name       Family Count
## 1:      Kentish Plover Charadriidae   150
## 2:         Common Teal     Anatidae   300
## 3:        Little Egret     Ardeidae    20
## 4:         Common Teal     Anatidae   200
## 5: Light-vented Bulbul           NA     5

沒有在 waterbird 裡面的物種,Family 會是 NA

EX2 inner join

survey[waterbird, on = "Common Name", nomatch = 0]
##       Common Name Count       Family
## 1: Kentish Plover   150 Charadriidae
## 2:    Common Teal   300     Anatidae
## 3:    Common Teal   200     Anatidae
## 4:   Little Egret    20     Ardeidae

nomatch = 0 表示兩個表當中沒有對應到的內容不會回傳,等同於 inner_join()

rbind() 與 cbind

另外還有一種資料合併,不是利用相同的欄位名稱做對應,而是直接接上其他資料的欄或著是列

EX1 rbind()

假設今天除了 survey 之外,還有一份 survey.2 的調查資料。

survey.2 <- 
  data.table(`Common Name` = 
               c("Black-faced Spoonbill", "Black-headed Gull", "Kentish Plover"),
             `Count` = 
               c(20, 500, 100))
survey.2
##              Common Name Count
## 1: Black-faced Spoonbill    20
## 2:     Black-headed Gull   500
## 3:        Kentish Plover   100

因為兩個資料具有相同的欄位名稱,所以就可以用 rbind() 來合併兩個資料。合併完後 survey.2 會接在 survey 的下方。

rbind(survey, survey.2)
##              Common Name Count
## 1:        Kentish Plover   150
## 2:           Common Teal   300
## 3:          Little Egret    20
## 4:           Common Teal   200
## 5:   Light-vented Bulbul     5
## 6: Black-faced Spoonbill    20
## 7:     Black-headed Gull   500
## 8:        Kentish Plover   100


EX2 cbind()

在 waterbird 資料中,原本只有俗名跟科名,當我們手上有屬名 Genus 的資料,如果確定每一筆資料的順序跟 waterbird 是一樣的時候,就可以用 cbind 來合併。合併完後 waterbird 會接在 Genus 的左側。

Genus <- data.table(Genus = c("Charadrius", "Anas", "Egretta", "Pluvialis"))

cbind(waterbird, Genus)
##              Common Name       Family      Genus
## 1:        Kentish Plover Charadriidae Charadrius
## 2:           Common Teal     Anatidae       Anas
## 3:          Little Egret     Ardeidae    Egretta
## 4: Pacific Golden Plover Charadriidae  Pluvialis


我沒有比較過這些函數之間的效率誰高誰低,不過因為在處理大量資料的時候,都是用data.table,所以資料合併上也習慣用data.table的方法來寫。不過抓錯的話 dplyr::anti_join() 不錯用~ 另外如果覺得資料對應上可能會需要某種程度的 工人智慧,我也是會跳回excel用vlookup來處理的XD“。