1. 程式人生 > >R|資料處理|merge資料詳解

R|資料處理|merge資料詳解

640?wx_fmt=gif作者簡介

Dwzb , R語言中文社群專欄作者,廈門大學統計專業學生。

知乎專欄:https://zhuanlan.zhihu.com/Data-AnalysisR 

前文推送

由於merge資料在現實應用中使用非常廣泛,而且真實的資料遠遠沒有我們練習時使用的資料那麼幹淨,所以有了這篇文章。本文會針對現實資料中可能遇到的一些問題,更深入地講解merge資料的使用。

資料:自己有針對性地建立,故意踩一些坑並解決

函式:包括dplyr包的join族函式,data.table包的[DT, ]方法,基礎及data.table包的merge函式

主要解決如下問題

  • 要合併的兩個資料集,合併所依據的列,名稱不同

  • 要合併的兩個資料集,有一些相同的列名,合併後需要更改防止兩列同名

  • 合併所依據的列不是完全相同的,展示多種取捨方法

  • 合併所依據的列中,內容不是唯一的,展示幾種情況的處理方法

全文分dplyr包和data.table包來講

資料建立

先簡單說明一下建立的資料

第一組一共四個資料框

  • data_map 有兩列可以用於merge,作為其他資料之間的橋樑,將所有資料merge在一起

  • data_good 用於merge的列形式很好,和data_map中的列一一對應,用於展示最簡單的資料框merge合併。但是為了演示一些引數,在列名上挖了兩個坑

  • data_diff 用於merge的列和data_map中的列有交集,都不完全包括對方,用於展示多種取捨方法在兩個包中的 實現

  • data_bad 用於merge的列有重複,與data_map這個沒有重複的資料來merge

第二組兩個資料框: bad1和bad2,用於merge的列都有重複,用於展示都有重複時的merge

這裡主要展示資料框建立程式碼,資料框長什麼樣子,在下面的例子中都會展示

library(dplyr) library(data.table) library(lubridate) # 設定隨機種子保證隨機試驗可以重複 set.seed(1234) # 兩列每個值都是唯一的 data_map <- data.table(name_id = letters[1:10], card_id = LETTERS[1:10]) m2 <- sample(letters[1:10], 15, replace = T) m1 <- sample(LETTERS[1:10], 10, replace = F) # object_id列值是唯一的,且正好和data_map的name_id列一一對應 data_good <- data.table(object_id = m1, # 與card_id列對應來合併,故意製造合併列名稱不同的情況                    name_id = paste(m1, m1, sep='')) # 故意製造同名情況 # name_id列值唯一,與data_map的name_id只有4個重合的 data_diff <- data.table(name_id = sample(letters, 10, replace = F),                        x1 = c(rep('m',4), rep('n',3), rep('p',3))) # name_id列值不唯一,與data_map的name_id只有7個重合的,但是data_bad中有的在data_map中都能找到 data_bad <- data.table(name_id = m2, # 與name_id對應來合併                    date = ymd('20171218') + ddays(rowid(m2)),                    content1 = paste(m2, 1:15, sep='_content'),                    content2 = paste(m2, 1:15, sep='another')) bad1 <- data.table(name_id = c(1,2,3,1,4,5,1,6),                   text1 = letters[1:8]) bad2 <- data.table(name_id = c(1,2,3,1,2,5,3,7),                   text12 = LETTERS[1:8])

join族函式

dplyr包中,join族函式分為如下幾個函式(對於 *_join(new, data_diff, by='name_id') 來說)

  • left_join 以new中name_id(記為A)為準。data_diff的name_id中,不在A中的全部捨棄,其餘的對應匹配上去

  • right_join 和left相反,以data_diff為準

  • inner_join 保留兩個資料name_id交叉的部分

  • full_join 兩個資料所有內容都保留,相當於left_join結果,加上right_join結果,減去inner_join的結果

  • semi_join 與 anti_join 其實是對行篩選,不算是merge合併

下面程式碼結果分別展示

1. 這部分展示

  • join族函式中的兩個引數 by suffix

  • 各個join函式有什麼區別

程式碼展示如下

new <- left_join(data_map, data_good, # 這是兩個完全匹配的資料                 by = c('card_id'='object_id'),  # 對應匹配的列名不同,在此指定                 suffix = c('', '_good')) # 兩個資料有相同列名,指定融合後,兩個資料集相同列名,分別加什麼樣的字尾 head(new) left_join(new, data_diff, by = 'name_id') right_join(new, data_diff, by = 'name_id') left_join(data_diff, new, by = 'name_id') # 除了列順序之外,內容和上一條是一樣的 inner_join(new, data_diff, by = 'name_id') full_join(new, data_diff, by = 'name_id') semi_join(new, data_diff, by = 'name_id') new[new$name_id %in% data_diff$name_id, ] # 同上 anti_join(new, data_diff, by = 'name_id') new[!new$name_id %in% data_diff$name_id, ] # 同上

結果展示如下

首先合併的是data_map和data_good得到new

  data_map                   data_good name_id  card_id    ||      object_id name_id      a        A    ||              I      II      b        B    ||              C      CC      c        C    ||              J      JJ      d        D    ||              B      BB      e        E    ||              G      GG      f        F    ||              F      FF      g        G    ||              E      EE      h        H    ||              A      AA      i        I    ||              H      HH      j        J    ||              D      DD                  new      card_id name_id name_id_good            A       a           AA            B       b           BB            C       c           CC            D       d           DD            E       e           EE            F       f           FF            G       g           GG            H       h           HH            I       i           II            J       j           JJ

下一步是new和data_diff的多種合併結果

           new  (name_id只有4個重合)  data_diff name_id card_id name_id_good   ||    name_id x1      a       A           AA   ||          v  m      b       B           BB   ||          n  m      c       C           CC   ||          z  m      d       D           DD   ||          t  m      e       E           EE   ||          b  n      f       F           FF   ||          j  n      g       G           GG   ||          f  n      h       H           HH   ||          w  p      i       I           II   ||          u  p      j       J           JJ   ||          d  p                 left_join(保留new) name_id card_id name_id_good   x1      a       A           AA <NA>      b       B           BB    n      c       C           CC <NA>      d       D           DD    p      e       E           EE <NA>      f       F           FF    n      g       G           GG <NA>      h       H           HH <NA>      i       I           II <NA>      j       J           JJ    n                  right_join(保留data_diff) name_id card_id name_id_good x1      v    <NA>         <NA>  m      n    <NA>         <NA>  m      z    <NA>         <NA>  m      t    <NA>         <NA>  m      b       B           BB  n      j       J           JJ  n      f       F           FF  n      w    <NA>         <NA>  p      u    <NA>         <NA>  p      d       D           DD  p                  inner_join(取交集) name_id card_id name_id_good x1      b       B           BB  n      d       D           DD  p      f       F           FF  n      j       J           JJ  n                 full_join(取並集) name_id card_id name_id_good   x1      a       A           AA <NA>      b       B           BB    n      c       C           CC <NA>      d       D           DD    p      e       E           EE <NA>      f       F           FF    n      g       G           GG <NA>      h       H           HH <NA>      i       I           II <NA>      j       J           JJ    n      v    <NA>         <NA>    m      n    <NA>         <NA>    m      z    <NA>         <NA>    m      t    <NA>         <NA>    m      w    <NA>         <NA>    p      u    <NA>         <NA>    p               semi_join(在new中篩選出data_diff有的) name_id card_id name_id_good      b       B           BB      d       D           DD      f       F           FF      j       J           JJ               anti_join(在new中篩選出data_diff沒有的) name_id card_id name_id_good      a       A           AA      c       C           CC      e       E           EE      g       G           GG      h       H           HH      i       I           II

2.接下來考慮一個數據merge列有重複的情況

# 只有一個數據的name_id有重複,就會自動擴充 left_join(new, data_bad, by = 'name_id') right_join(new, data_bad, by = 'name_id')

結果展示如下

       new name_id card_id name_id_good      a       A           AA      b       B           BB      c       C           CC      d       D           DD      e       E           EE      f       F           FF      g       G           GG      h       H           HH      i       I           II      j       J           JJ                data_bad name_id       date    content1   content2      b 2017-12-19  b_content1  banother1      g 2017-12-19  g_content2  ganother2      g 2017-12-20  g_content3  ganother3      g 2017-12-21  g_content4  ganother4      i 2017-12-19  i_content5  ianother5      g 2017-12-22  g_content6  ganother6      a 2017-12-19  a_content7  aanother7      c 2017-12-19  c_content8  canother8      g 2017-12-23  g_content9  ganother9      f 2017-12-19 f_content10 fanother10      g 2017-12-24 g_content11 ganother11      f 2017-12-20 f_content12 fanother12      c 2017-12-20 c_content13 canother13      j 2017-12-19 j_content14 janother14      c 2017-12-21 c_content15 canother15         left_join(new, data_bad, by = 'name_id') name_id card_id name_id_good       date    content1   content2      a       A           AA 2017-12-19  a_content7  aanother7      b       B           BB 2017-12-19  b_content1  banother1      c       C           CC 2017-12-19  c_content8  canother8      c       C           CC 2017-12-20 c_content13 canother13      c       C           CC 2017-12-21 c_content15 canother15      d       D           DD       <NA>        <NA>       <NA>      e       E           EE       <NA>        <NA>       <NA>      f       F           FF 2017-12-19 f_content10 fanother10      f       F           FF 2017-12-20 f_content12 fanother12      g       G           GG 2017-12-19  g_content2  ganother2      g       G           GG 2017-12-20  g_content3  ganother3      g       G           GG 2017-12-21  g_content4  ganother4      g       G           GG 2017-12-22  g_content6  ganother6      g       G           GG 2017-12-23  g_content9  ganother9      g       G           GG 2017-12-24 g_content11 ganother11      h       H           HH       <NA>        <NA>       <NA>      i       I           II 2017-12-19  i_content5  ianother5      j       J           JJ 2017-12-19 j_content14 janother14       right_join(new, data_bad, by = 'name_id') name_id card_id name_id_good       date    content1   content2      b       B           BB 2017-12-19  b_content1  banother1      g       G           GG 2017-12-19  g_content2  ganother2      g       G           GG 2017-12-20  g_content3  ganother3      g       G           GG 2017-12-21  g_content4  ganother4      i       I           II 2017-12-19  i_content5  ianother5      g       G           GG 2017-12-22  g_content6  ganother6      a       A           AA 2017-12-19  a_content7  aanother7      c       C           CC 2017-12-19  c_content8  canother8      g       G           GG 2017-12-23  g_content9  ganother9      f       F           FF 2017-12-19 f_content10 fanother10      g       G           GG 2017-12-24 g_content11 ganother11      f       F           FF 2017-12-20 f_content12 fanother12      c       C           CC 2017-12-20 c_content13 canother13      j       J           JJ 2017-12-19 j_content14 janother14      c       C           CC 2017-12-21 c_content15 canother15

當我們要把多張表merge到一起時,如果有幾張都是merge列有重複的情況,就會面臨要merge的兩個資料都是有重複的的情況

3.兩個資料集merge列都有重複

程式碼展示如下(直接使用left_join看預設情況即可)

> bad1   name_id text1 1:       1     a 2:       2     b 3:       3     c 4:       1     d 5:       4     e 6:       5     f 7:       1     g 8:       6     h > bad2   name_id text12 1:       1      A 2:       2      B 3:       3      C 4:       1      D 5:       2      E 6:       5      F 7:       3      G 8:       7      H > left_join(bad1, bad2, by = 'name_id')   name_id text1 text12 1        1     a      A 2        1     a      D 3        2     b      B 4        2     b      E 5        3     c      C 6        3     c      G 7        1     d      A 8        1     d      D 9        4     e   <NA> 10       5     f      F 11       1     g      A 12       1     g      D 13       6     h   <NA>

從上面結果可見,比如1重複,則bad1中的每個1都和bad2的每個1構成一行,所以產生了3*2=6個name_id是1的行。

這樣merge有一個缺點就是產生了大量重複資料,還有另一種處理方法,長表變寬表,這樣可以讓name_id不會重複,但是列就會增加很多。

這裡只展示data_bad如何將name_id唯一化的,即把date變到列上去,即看每個name_id在各個date上的content1和content2分別是什麼

> data_bad    name_id       date    content1   content2 1:       b 2017-12-19  b_content1  banother1 2:       g 2017-12-19  g_content2  ganother2 3:       g 2017-12-20  g_content3  ganother3 4:       g 2017-12-21  g_content4  ganother4 5:       i 2017-12-19  i_content5  ianother5 6:       g 2017-12-22  g_content6  ganother6 7:       a 2017-12-19  a_content7  aanother7 8:       c 2017-12-19  c_content8  canother8 9:       g 2017-12-23  g_content9  ganother9 10:       f 2017-12-19 f_content10 fanother10 11:       g 2017-12-24 g_content11 ganother11 12:       f 2017-12-20 f_content12 fanother12 13:       c 2017-12-20 c_content13 canother13 14:       j 2017-12-19 j_content14 janother14 15:       c 2017-12-21 c_content15 canother15 > data_bad %>% melt(id = c('name_id', 'date')) %>% +   dcast(name_id~date+variable)   name_id 2017-12-19_content1 2017-12-19_content2 2017-12-20_content1 2017-12-20_content2 1:       a          a_content7           aanother7                  NA                  NA 2:       b          b_content1           banother1                  NA                  NA 3:       c          c_content8           canother8         c_content13          canother13 4:       f         f_content10          fanother10         f_content12          fanother12 5:       g          g_content2           ganother2          g_content3           ganother3 6:       i          i_content5           ianother5                  NA                  NA 7:       j         j_content14          janother14                  NA                  NA   2017-12-21_content1 2017-12-21_content2 2017-12-22_content1 2017-12-22_content2 1:                  NA                  NA                  NA                  NA 2:                  NA                  NA                  NA                  NA 3:         c_content15          canother15                  NA                  NA 4:                  NA                  NA                  NA                  NA 5:          g_content4           ganother4          g_content6           ganother6 6:                  NA                  NA                  NA                  NA 7:                  NA                  NA                  NA                  NA   2017-12-23_content1 2017-12-23_content2 2017-12-24_content1 2017-12-24_content2 1:                  NA                  NA                  NA                  NA 2:                  NA                  NA                  NA                  NA 3:                  NA                  NA                  NA                  NA 4:                  NA                  NA                  NA                  NA 5:          g_content9           ganother9         g_content11          ganother11 6:                  NA                  NA                  NA                  NA 7:                  NA                  NA                  NA                  NA

這樣name_id唯一再merge到data_map表中,不會增加太多重複的行,但是這個方法也有一個很大的弊端,就是增加了太多列,產生了非常多的缺失值。

其實這種name_id不唯一的表,稱為動態表;而唯一的是靜態表

  • 靜態表表示這個id的靜態資料,特徵等,一個id就對應該指標的一個值

  • 動態表則反映這個id的動態情況,比如哪一天這個id發生了什麼,換一天又發生了什麼,這樣一個表就會出現多次同一個id,即name_id列有重複的內容

真正遇到這種問題時,其實沒有必要非要把靜態表和動態表合併在一起了,合併後資料格式都不好了,肯定也沒辦法分析

data.table

使用data.table執行結果和join族函式一樣,所以這裡只列程式碼

使用[]來合併,[]的在merge上的功能比較弱,而且彆扭,只能做下面這些事

new <- data_map[data_good, on="card_id==object_id"] new # data_map[data_good, name_id_good:= i.name_id,on="card_id==object_id"][] # 要改名就在原資料上修改,改名沒有join族函式方便 # 以data_diff為準 new[data_diff, on='name_id'] # 以new為準 data_diff[new, on='name_id']

但是[]中使用merge可以結合[]中其他位置的引數一起使用,在只需要簡單merge時,可以達到多步合併為一行的簡潔效果。

下面我們來看一下merge函式,data.table中的merge會比基礎包中merge更快,同時更改了一些預設選項,基本使用以及引數都沒有改變,這裡的資料因為建立時使用的是data.table函式,所以使用merge時自動用的是data.table包中的函式

new <- merge(data_map, data_good, by.x='card_id', by.y='object_id', suffixes=c('','.good')) new # 全部保留 merge(new, data_diff, by='name_id', all=T) # 保留new的 merge(new, data_diff, by='name_id', all.x=T) # 保留data_diff的 merge(new, data_diff, by='name_id', all.y=T) # 保留交集 merge(new, data_diff, by='name_id', all=F) merge(new, data_bad, by='name_id', all.x=T) merge(new, data_bad, by='name_id', all.y=T) merge(bad1,bad2, by='name_id', all.x=T)

大家都在看

640?wx_fmt=jpeg

公眾號後臺回覆關鍵字即可學習

回覆 爬蟲            爬蟲三大案例實戰  
回覆 
Python1小時破冰入門

回覆 資料探勘     R語言入門及資料探勘
回覆 
人工智慧     三個月入門人工智慧
回覆 資料分析師  資料分析師成長之路 
回覆 機器學習      機器學習的商業應用
回覆 資料科學      資料科學實戰
回覆 常用演算法      常用資料探勘演算法