joinコマンドを使ってシェルでデータを結合する

シェルのコマンドの一つであるjoinの使い方を整理します。 複数のテーブルを結合することで複雑なデータも上手に処理できるようになります。
2021.11.11

コマンドの基本形

コマンドの基本形は以下のような感じです。 今回はBSD版のコマンドを利用しています。

コマンドの基本形

$ join fileA fileB

ファイル二つを引数として取り、その結合結果を出力します。 注意点として「使用するファイルの結合に使うキーがソートされている必要があります

今回使用するデータ

今回はGDPトップ3の国のリストと都市のデータを扱います。

country.csv

ID,国
0,アメリカ
1,中国
2,日本

city.csv

ID,都市,国ID
0,ニューヨーク,0
1,ロサンゼルス,0
2,シカゴ,0
3,北京,1
4,東京,2
5,大阪,2
6,パリ,3

ここでは説明のためにヘッダーが入っていますが、以下の説明ではヘッダーを削除したファイルを使用しているので注意してください。

内部結合

内部結合では結合に成功した行のみが出力されます。

内部結合

$ join -t , -o '0,1.2,2.2' -1 1 -2 3 country.csv city.csv
0,アメリカ,ニューヨーク
0,アメリカ,ロサンゼルス
0,アメリカ,シカゴ
1,中国,北京
2,日本,東京
2,日本,大阪

オプションについて軽く説明します。

  • -t: 区切り文字
  • -o: 出力するカラム
  • -1: 1つ目のファイルで結合に使用するカラム
  • -2: 2つ目のファイルで結合に使用するカラム

ここではcountry.csvの1番目のカラムであるIDとcity.csvの3番目のカラムである国IDを使用して結合を行っています。

パリは対応する国IDがないので出力されていません。

-oの書式についても軽く説明します。 0は特別で結合に使用したキーが入ります。 1.xは1つ目のファイルのX番目のカラムです。 2.xは2つ目のファイルのX番目のカラムです。

外部結合

左外部結合では1つ目のファイルの行は結合の結果によらず出力されます

左外部結合

$ join -t , -o '0,1.2,2.2' -a 1 -e NULL -1 1 -2 3 country.csv city.csv
0,アメリカ,ニューヨーク
0,アメリカ,ロサンゼルス
0,アメリカ,シカゴ
1,中国,北京
2,日本,東京
2,日本,大阪

逆に右外部結合の結果を見てみましょう

右外部結合

$ join -t , -o '0,1.2,2.2' -a 2 -e NULL -1 1 -2 3 country.csv city.csv
0,アメリカ,ニューヨーク
0,アメリカ,ロサンゼルス
0,アメリカ,シカゴ
1,中国,北京
2,日本,東京
2,日本,大阪
3,NULL,パリ

こちらはパリの行が出力されています。 つまりはcity.csvのすべての行が出力されているということです。

ここで新たに出てきたオプションについて説明します。

  • -a: 対応するものがなくても出力するファイル
  • -e: 対応する値がなかった場合に入れる値

ここでは-aでどちらのファイルの行を全て使うのかを指定しています。 便宜的に一つ目のファイルを元にした場合を左外部結合、2つ目のファイルの時は右外部結合と呼んでいます。

-eでは対応する値がなかった場合に代わりに入れる文字列を指定しています。

ソートしていないとどうなるか

joinは使用するカラムでソートされているという前提があります。 先ほどのcity.csvの順番をバラバラにして結合してみます。

city_not_sorted.csv

0,ニューヨーク,0
1,パリ,3
2,北京,1
3,ロサンゼルス,0
4,大阪,2
5,シカゴ,0
6,東京,2

未ソート

$ join -t , -o '0,1.2,2.2' -1 1 -2 3 country.csv city_not_sorted.csv
0,アメリカ,ニューヨーク

正しい結果が出力されていません。 ニューヨークはたまたま出力されていますが、他の行は全て抜けています。

未ソート

$ join -t , -o '0,1.2,2.2' -1 1 -2 3 country.csv <(sort -t , -k 3 city_not_sorted.csv)
0,アメリカ,ニューヨーク
0,アメリカ,ロサンゼルス
0,アメリカ,シカゴ
1,中国,北京
2,日本,大阪
2,日本,東京

プロセス置換を利用してファイルをソートしてから利用します。 こちらは期待通りの結果が得られました。

終わりに

joinコマンドを利用するとこで二つのデータを結合することができました。 ソート済みのファイルでないと正しい結果が出ないという点は注意が必要です。