dplyrを使いこなす!JOIN編

  • 66
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

はじめに

今回は、複数(主に2つ)のdata.frameを結合する方法についてまとめます。
実際に分析する際に、1つのdata.frameだけで完結することはあんまりなく、マスタデータの結合など複数のdata.frameを組み合わせて使う必要がありますが、またまたdplyrを使えば簡単にできます。

結合と言っても、結構色んな方法があるので、分かりやすいようにチートシートからイメージ図を拝借してきました。

1つのdata.frameを操作する場合は、↓の記事を参照してください。
dplyrを使いこなす!基礎編
dplyrを使いこなす!Window関数編

JOINの種類

JOINの種類は大きく分けて以下の3つあります。

種類 説明
Mutating joins マッチした行の列を、元のdata.frameに追加します。
Filtering joins マッチしたか否かで、元のdata.frameをフィルタリングします。
Set operations 2つのdata.frameを組み合わせます。

それでは、順番に見ていきましょう。

Mutating Joins

Mutating Joinsには、以下の4つの種類があります。
SQLで書くと、下表のようなイメージです。
inner join、left join等はSQLで使うことも多いのでイメージしやすいと思います。

dplyr SQLイメージ
inner_join SELECT * FROM x JOIN y ON x.a = y.a
left_join SELECT * FROM x LEFT JOIN y ON x.a = y.a
right_join SELECT * FROM x RIGHT JOIN y ON x.a = y.a
full_join SELECT * FROM x FULL JOIN y ON x.a = y.a

dplyr0.5.0では、non-equi joinのサポートが予定されているようです。
結合の条件が=だけではなく、><等が使えるようになります。(たぶん

inner_join

inner_joinは、xとyのbyで指定した列がマッチした行のみを返却します。
もし、xとyの間で複数マッチする場合は、すべての組み合わせが返却されます。

構文
> inner_join
function (x, y, by = NULL, copy = FALSE, ...) 

inner_join.png

a <- dplyr::data_frame(
  x1=c("A","B","C") ,
  x2=c(1,2,3)
)

b <- dplyr::data_frame(
  x1=c("A","B","D") ,
  x3=c(TRUE , FALSE , TRUE)
)

dplyr::inner_join(a , b ,by="x1")

     x1    x2    x3
1     A     1  TRUE
2     B     2 FALSE

オプション

Mutating Joinsのオプションは、すべて同じなのでここでまとめて解説します。
といっても、2種類しかなく、copyは特殊ケースの場合のみなので、byの使い方が理解できれば十分です。

オプション 説明
by JOINのキーに指定する変数。
NULLの場合、Natural JOINつまり、同じ名前の列を使用してJOINする。
異なる変数でJOINしたい場合、by = c("a" = "b") のように指定する。
copy データソースをDBとし、かつxとyが異なるデータソースである場合、yをxのデータソースにコピーするか否か。
データ量が多い場合は、重い操作になるので、基本は同じデータソースを使用する。
複数列をキーにJOIN
a <- dplyr::data_frame(
  x1=c("A","B","C") ,
  x2=c(1,1,1),
  x3=c(1,2,3)
)

b <- dplyr::data_frame(
  x1=c("A","B","D") ,
  x2=c(2,1,3),
  x4=c(TRUE , FALSE , TRUE)
)

dplyr::inner_join(a , b ,by=c("x1","x2"))
列名が異なるキーでJOIN
a <- dplyr::data_frame(
  x1=c("A","B","C") ,
  x2=c(1,2,3)
)

b <- dplyr::data_frame(
  x2=c("A","B","D") ,
  x3=c(TRUE , FALSE , TRUE)
)

dplyr::left_join(a , b ,by=c("x1" = "x2"))
列名が異なる複数列をキーでJOIN

a <- dplyr::data_frame(
  a1=c("A","B","C") ,
  a2=c(1,1,1),
  a3=c(1,2,3)
)

b <- dplyr::data_frame(
  b1=c("A","B","D") ,
  b2=c(2,1,3),
  b3=c(TRUE , FALSE , TRUE)
)

dplyr::inner_join(a , b ,by=c("a1" ="b1" , "a2" = "b2"))

left_join

すべてのxの行を返却し、yとマッチする行はyの列も返却されます。
yとマッチしない行のyの列は、NAとなります。
xとyの間で複数マッチする場合は、すべての組み合わせが返却されます。

構文
> left_join
function (x, y, by = NULL, copy = FALSE, ...) 

left_join.png

a <- dplyr::data_frame(
  x1=c("A","B","C") ,
  x2=c(1,2,3)
)

b <- dplyr::data_frame(
  x1=c("A","B","D") ,
  x3=c(TRUE , FALSE , TRUE)
)

dplyr::left_join(a , b ,by="x1")

right_join

left_joinのxとyが逆になったイメージですが、個人的にはleft_joinを使うためほとんど使用しません。
すべてのyの行を返却し、xとマッチする列はxの列も返却されます。
xとマッチしない行のxの列は、NAとなります。
xとyの間で複数マッチする場合は、すべての組み合わせが返却されます。

構文
> right_join
function (x, y, by = NULL, copy = FALSE, ...) 

right_join.png

a <- dplyr::data_frame(
  x1=c("A","B","C") ,
  x2=c(1,2,3)
)

b <- dplyr::data_frame(
  x1=c("A","B","D") ,
  x3=c(TRUE , FALSE , TRUE)
)

dplyr::right_join(a , b ,by="x1")

     x1    x2    x3
1     A     1  TRUE
2     B     2 FALSE
3     D    NA  TRUE

full_join

xとyのすべての行と列を返却します。
マッチしない箇所は、NAとなります。

構文
> full_join
function (x, y, by = NULL, copy = FALSE, ...) 

full_join.png

a <- dplyr::data_frame(
  x1=c("A","B","C") ,
  x2=c(1,2,3)
)

b <- dplyr::data_frame(
  x1=c("A","B","D") ,
  x3=c(TRUE , FALSE , TRUE)
)

dplyr::full_join(a , b ,by="x1")

     x1    x2    x3
1     A     1  TRUE
2     B     2 FALSE
3     C     3    NA
4     D    NA  TRUE

Filtering Joins

xの行のフィルタリングに、yとマッチしたか否かを使用します。

dplyr SQLイメージ
semi_join SELECT * FROM x WHERE EXISTS (SELECT 1 FROM y WHERE x.a = y.a)
anti_join SELECT * FROM x WHERE NOT EXISTS (SELECT 1 FROM y WHERE x.a = y.a)

semi_join

yとマッチしたすべてのxの行を返却します。
返却される列は、すべてxの列のみでyの列は返却されません(inner_joinと違うところです。)

構文
> semi_join
function (x, y, by = NULL, copy = FALSE, ...) 

semi_join.png

a <- dplyr::data_frame(
  x1=c("A","B","C") ,
  x2=c(1,2,3)
)

b <- dplyr::data_frame(
  x1=c("A","B","D") ,
  x3=c(TRUE , FALSE , TRUE)
)

dplyr::semi_join(a , b ,by="x1")

     x1    x2
1     A     1
2     B     2

anti_join

yとマッチしなかったすべてのxの行を返却します。
返却される列は、すべてxの列のみでyの列は返却されません。

構文
> anti_join
function (x, y, by = NULL, copy = FALSE, ...) 

anti_join.png

a <- dplyr::data_frame(
  x1=c("A","B","C") ,
  x2=c(1,2,3)
)

b <- dplyr::data_frame(
  x1=c("A","B","D") ,
  x3=c(TRUE , FALSE , TRUE)
)

dplyr::anti_join(a , b ,by="x1")

     x1    x2
1     C     3

Set Operations

xとyは、同じ列から構成されるdata.frameである必要があります。

dplyr SQLイメージ
intersect SELECT * FROM x INTERSECT SELECT * FROM y
union SELECT * FROM x UNION SELECT * FROM y
setdiff SELECT * FROM x EXCEPT SELECT * FROM y

intersect

xとyの両方にある列が返却されます。

構文
> intersect
function (x, y, ...) 

intersect.png

y <- dplyr::data_frame(
  x1=c("A","B","C") ,
  x2=c( 1 , 2 , 3 )
)

z <- dplyr::data_frame(
  x1=c("B","C","D"),
  x2=c( 2 , 3 , 4)
)

dplyr::intersect(y , z)

     x1    x2
1     B     2
2     C     3

union

xとyの重複がないユニークな行が返却されます。

構文
> union
function (x, y, ...) 

union.png

y <- dplyr::data_frame(
   x1=c("A","B","C") ,
   x2=c( 1 , 2 , 3 )
)

z <- dplyr::data_frame(
   x1=c("B","C","D"),
   x2=c( 2 , 3 , 4)
)

dplyr::union(y , z)

     x1    x2
  (chr) (dbl)
1     B     2
2     A     1
3     C     3
4     D     4

setdiff

yにはない、xの行が返却されます。

構文
> setdiff
function (x, y, ...) 

setdiff.png

y <- dplyr::data_frame(
  x1=c("A","B","C") ,
  x2=c( 1 , 2 , 3 )
)

z <- dplyr::data_frame(
  x1=c("B","C","D"),
  x2=c( 2 , 3 , 4)
)

dplyr::setdiff(y , z)

     x1    x2
1     A     1

まとめ

JOIN編は以上となります。
SQLに詳しい人は、SQLでできるJOINはほぼdplyrでもできることが分かったと思います。
inner_join、left_join、semi_join、anti_join辺りが使えれば、実務にはほぼ困らないのではないでしょうか。

dplyrの機能としては、DBとの接続周りを除けば、ざっくり解説できたと思うのでtidyrの解説に移りたいと思います。
dplyrとtidyrを使いこなせると、仕事が捗ること間違いなしです!