joinでテーブル結合(1).R

要約

・複数のテーブルを共通項(キー列)でつなぐのがjoin
・元々はリレーショナルデータベースの話
・Rではdplyr::に含まれ、left_join()、right_join()、inner_join()、full_joim()の4種類
・キー列の指定はby = を使い、より明示的に

参考. リレーショナル(関係)データベース

joinの分類(ただしSQL)

 ExcelのVBAでも使えるので、たまにお世話になってました。あざーす。
 簡単には、テーブル同士を共通項(キー)でつなぐのがjoin。
 left/right join、inner/outer joinの4種類があってそれぞれ挙動が異なる。
 意図的に2つに分けたのは挙動の類似性だが、挙動の詳細は上記の引用記事が分かりやすいと思う。
 joinの出番としては、例えば、社員IDとアンケート結果がならんだテーブルに人事情報を加えたいときがある。社員IDと人事データ等々が並んだテーブル(人事マスタ)を先のテーブルにjoinすると効率的にそういうことができる。すると、人事マスタから引っ張って来た年齢・最終学歴などを元にアンケート結果を解析することができる。
 なお、このようなjoinを、やってはいけない組み合わせでやってしまったのが、例のリクナビ事件だったりする。

Rでのjoin

 これらの各joinはmutating joinと呼ばれる一群の関数として、おなじみdplyr::パッケージに含まれている。
 left/right joinにはleft/right_join()、inner/outer joinにはinner/full_join()が対応する。
 どうやらbase::merge()でもjoin可能らしいのだけれど、dplyr::は常に使っているので、ここでもdplyr::の方でやっていく。

joinに不可欠な引数 by = "キー列"

 同じキーを持つ行同士を突き当てながらデータを合体していくのがjoinで、各関数は突き当て方のルール、特に突き当てられなかったデータの処理が異なる。
 一方の共通部分、まさしくjoinを使う鍵であるキーが書かれたキー列の指定がby = "列名"だ。

 キーとなる値は、社員IDやシリアル番号などと同じで、単一の事物と1対1で結びつく単一の番号だ。不具合について製造元に問い合わせるときにシリアル番号を聞かれるのは、シリアル番号と不具合が発生した製品が1対1で結びついているためだ。

 例えば、メーカー内ではシリアル番号列と製造日列・製造条件列等々を持ったテーブルで管理されており、一度製造されたものの値は二度と変えられない。従って、シリアル番号が分かれば製造日などが特定できて不具合の原因究明に役立てられるし、同一製造日・ロットで同様の不具合が発生するかもしれないと考えて必要に応じてリコールなどの処置が取られたりもする。
 一方、アメリカ大統領のTwitterアカウント(@POTUS)は大統領交代のたびに前大統領のツイートは別アカウントに移すという措置が取られていて、オバマ時代のツイートは第44代なので@POTUS44へ、トランプ時代のは@POTUS45へ移されている。そのため、アメリカ大統領のアカウントであるにもかかわらず、@POTUSに紐づいた個人が書き換えられてしまっているため、@POTUSを見ても過去の大統領の発言は分からないのだ。
 ここで新大統領が就任するたびに最初から@POTUS##を割り当てておけよと思った人は、データ管理業務の適性がある。
 ちなみに、@POTUS45はアーカイブとしてちゃんと残っている。永久凍結されたのは個人アカウントの方だ。

by = を使った具体的な指定方法

 by = "キー列名"と指定しても良いし、実は指定しなくても勝手に同じ列名を探してきてキー列にしてくれる。
 これが使えるのは、joinするテーブルに同じ名前の列がそれぞれ存在していてそこにキーが格納されている場合だ。
 例えば、社員番号がどのテーブルにもある社員番号列に格納されているのであれば、by = "社員番号"と指定すれば良いし、たいていの場合は社員番号列は一つしかないだろうから、指定しなくてもよい。
 一方、社員番号列が複数ある場合にはどの社員番号列かを指定する必要がある。
 例えば、社員を班分けしておいて1行に1班を記載するような表だと、班に属する社員番号を横に並べて記載してしまうかもしれない。この時、それぞれの列がすべて社員番号という名前の列だと、どの社員番号列を使えばよいか関数が一意に認識してくれない。このような時には、社員番号列それぞれを社員番号.リーダー、社員番号.書記、etc.などのように列の名前を一意になるよう決めなおしてやることもできる。
 まあ、dpyr::pivot_longer()でテーブルを変換して、社員番号列を一つにまとめるのが普通だし、そもそも社員を縦に並べておいて班番号割り振るからlong形式で出てくるよね。現実は甘くない。

 では、テーブルごとでキー列の名前が違う場合はどうか。
 実のところ、そういうことはよくあって、社員番号を社員IDと書いたり社員コードと書いたりIDだけだったりする。各テーブルを作った人間が別々ならともかく、同じであってもそういうことは起きる。にんげんだもの。
 そのような時には以下のように指定する。

> left_join(左テーブル, 右テーブル, 
   by = c("左テーブルでのキー列名" = "右テーブルでのキー列名"))

 このやり方では、同一とみなす列名をc()を使って指定している。これでテーブルごとで列名が異なっても問題なく結合ができる。
 個人的には、キー列はより明示的に扱う方がいいと考えているので、仮に同じ名前であっても省略等せずにこのやり方で記載することにしている(記事内では可読性のために省略するけれども)。

 同様にc()を使って指定する場合がある。2つの値をつなげてキーに用いる場合だ。
 例えば、本の売上集計で、シリーズタイトル列と巻号列が分かれている場合を考える。シリーズ名だけで集計するのか、巻号ごとに集計するのかで、キー列の選び方が異なる。シリーズごとであればシリーズタイトルだけをキーにすればよいし、巻号ごとに集計するのであればシリーズタイトル+巻号をキーにする必要がある。

> left_join(左テーブル, 右テーブル, 
   by = c("左テーブルでのキー列名1" = "右テーブルでのキー列名1", 
   "左テーブルでのキー列名2" = "右テーブルでのキー列名2"))

> left_join(左テーブル, 右テーブル, by = c("キー列名1", "キー列名2"))

 いずれも、結合してキーにする2つの列を記載している。
 上は列名がテーブルで異なる場合で、下は列名を同一にしてある場合だ。
 実務上はよく出くわすので、あわせてこちらも覚えておきたい。

 次は、各joinでの出力の違いを見てみる。

今日はここまで。

 

この記事が気に入ったらサポートをしてみませんか?