PySparkでこういう場合はどうしたらいいのかをまとめた逆引きPySparkシリーズの結合編です。
(随時更新予定です。)
- 原則としてApache Spark 3.3のPySparkのAPIに準拠していますが、一部、便利なDatabricks限定の機能も利用しています(利用しているところはその旨記載しています)。
- Databricks Runtime 11.3 上で動作することを確認しています。
- ノートブックをこちらのリポジトリ からReposにてご使用のDatabricksの環境にダウンロードできます。
- 逆引きPySparkの他の章については、こちらの記事をご覧ください。
5-1 Join
5-1-1 内部結合 (INNER JOIN)
内部結合 (INNER JOIN)は、結合のキーとなる列の値がマッチする行同士を連結することで2つのデータフレームを結合します。キーの列の値が片方のデータフレームにしか存在しない場合は、その行は出力されません。
データフレームのjoin()メソッドを使い、最初の引数として内部結合をしたい相手のデータフレームを指定します。
以下のデータフレーム1、データフレーム2をramen_idという列を結合のキーとして内部結合するとした場合について説明します。
ramen_id | name | price |
---|---|---|
1 | 醤油ラーメン | 600 |
2 | 塩ラーメン | 700 |
3 | 豚骨醤油ラーメン | 900 |
4 | 味噌ラーメン | 800 |
ramen_id | name |
---|---|
1 | チャーシュー |
2 | 味玉 |
3 | チャーシュー |
3 | ニンニク |
結合のキーとなる列名が両方のデータフレームで共通している場合、構文1のように列名を第2引数で指定します。
# 構文1
df_1.join( df_2, <結合のキーとなる列>, "inner" )
# 例文1
df_ramen.join( df_topping, "ramen_id", "inner" )
構文2のように、それぞれのデータフレームの列名を指定し、結合のための条件式を第2引数にすることもできます。
# 構文2
df_1.join( df_2, <結合の条件>, "inner" )
# 例文2
df_ramen.join( df_topping, df_ramen.ramen_id == df_topping.ramen_id, "inner" )
構文1の場合の出力例は以下になります。
ramen_id | name | price | name |
---|---|---|---|
1 | 醤油ラーメン | 600 | チャーシュー |
2 | 塩ラーメン | 700 | 味玉 |
3 | 豚骨醤油ラーメン | 900 | チャーシュー |
3 | 豚骨醤油ラーメン | 900 | ニンニク |
5-1-2 左外部結合 (LEFT OUTER JOIN)
左外部結合 (LEFT OUTER JOIN)は、結合の基準となる左側のデータフレームの行に、キーとなる列の値がマッチする右側のデータフレームの行を連結することで2つのデータフレームを結合します。左側のデータフレームのキーとなる列の値が右側のデータフレームに存在しない場合でも、左側のデータフレームの該当行は出力されます(右側のデータフレームの該当の情報はnullとして出力されます)。
左外部結合の基準となる左側のデータフレームのjoin()メソッドを使い、最初の引数として相手の(右側の)データフレームを指定します。
以下のデータフレーム1、データフレーム2をramen_idという列を結合のキーとして左外部結合するとした場合について説明します。
ramen_id | name | price |
---|---|---|
1 | 醤油ラーメン | 600 |
2 | 塩ラーメン | 700 |
3 | 豚骨醤油ラーメン | 900 |
4 | 味噌ラーメン | 800 |
ramen_id | name |
---|---|
1 | チャーシュー |
2 | 味玉 |
3 | チャーシュー |
3 | ニンニク |
結合のキーとなる列名が両方のデータフレームで共通している場合、構文1のように列名を第2引数で指定します。
# 構文1
df_1.join( df_2, <結合のキーとなる列>, "left" )
# 例文1
df_ramen.join( df_topping, "ramen_id", "left" )
構文2のように、それぞれのデータフレームの列名を指定し、結合のための条件式を第2引数にすることもできます。
# 構文2
df_1.join( df_2, <結合の条件>, "left" )
# 例文2
df_ramen.join( df_topping, df_ramen.ramen_id == df_topping.ramen_id, "left" )
構文1の場合の出力例は以下になります。
ramen_id | name | price | name |
---|---|---|---|
1 | 醤油ラーメン | 600 | チャーシュー |
2 | 塩ラーメン | 700 | 味玉 |
3 | 豚骨醤油ラーメン | 900 | ニンニク |
3 | 豚骨醤油ラーメン | 900 | チャーシュー |
4 | 味噌ラーメン | 800 | null |
5-1-3 クロス結合 (CROSS JOIN)
クロス結合 (CROSS JOIN)は、結合する両方のデータフレームの全ての行の組み合わせを出力します。結果として、両側のデータフレームそれぞれの行数を掛け合わせた数の行が出力されます。
データフレームのcrossJoin()メソッドを使い、引数としてクロス結合の相手のデータフレームを指定します。結合のキーは不要なため、引数は1つだけです。
以下、データフレーム1、データフレーム2をクロス結合するとした場合について説明します。
ramen_id | name | price |
---|---|---|
1 | 醤油ラーメン | 600 |
2 | 塩ラーメン | 700 |
3 | 豚骨醤油ラーメン | 900 |
4 | 味噌ラーメン | 800 |
ramen_id | name |
---|---|
1 | チャーシュー |
2 | 味玉 |
3 | チャーシュー |
3 | ニンニク |
結合のキーとなる列名が両方のデータフレームで共通している場合、構文1のように列名を第2引数で指定します。
# 構文1
df_1.crossJoin( df_2 )
# 例文1
df_ramen.crossJoin( df_topping )
ramen_id | name | price | ramen_id | name |
---|---|---|---|---|
1 | 醤油ラーメン | 600 | 1 | チャーシュー |
1 | 醤油ラーメン | 600 | 2 | 味玉 |
1 | 醤油ラーメン | 600 | 3 | チャーシュー |
1 | 醤油ラーメン | 600 | 3 | ニンニク |
2 | 塩ラーメン | 700 | 1 | チャーシュー |
2 | 塩ラーメン | 700 | 2 | 味玉 |
2 | 塩ラーメン | 700 | 3 | チャーシュー |
2 | 塩ラーメン | 700 | 3 | ニンニク |
3 | 豚骨醤油ラーメン | 900 | 1 | チャーシュー |
3 | 豚骨醤油ラーメン | 900 | 2 | 味玉 |
3 | 豚骨醤油ラーメン | 900 | 3 | チャーシュー |
3 | 豚骨醤油ラーメン | 900 | 3 | ニンニク |
4 | 味噌ラーメン | 800 | 1 | チャーシュー |
4 | 味噌ラーメン | 800 | 2 | 味玉 |
4 | 味噌ラーメン | 800 | 3 | チャーシュー |
4 | 味噌ラーメン | 800 | 3 | ニンニク |
5-2 Union
5-2-1 Union データフレームを縦方向に結合する
2つのデータフレームを縦方向に結合(Union結合)します。SQLのUNION ALLとは異なり重複行がある場合でも許容されます。そのため、結合結果から重複を削除したい場合はUnion結合したデータフレームに対してさらにdistinct()を実行する必要があります。また、列の結合は、列の順番のみが考慮されます。列名が同じもの同士をUnion結合したい場合はunionByName()を使う必要があります。
データフレームのunion()メソッドを使い、最初の引数としてUnion結合をしたい相手のデータフレームを指定します。
以下のデータフレーム1、データフレーム2をUnion結合するとした場合について説明します。
ramen_id | name | price |
---|---|---|
1 | 醤油ラーメン | 600 |
2 | 塩ラーメン | 700 |
3 | 豚骨醤油ラーメン | 900 |
4 | 味噌ラーメン | 800 |
ramen_id | price | name |
---|---|---|
5 | 700 | 鶏白湯ラーメン |
6 | 800 | 濃厚とんこつラーメン |
7 | 600 | 塩つけめん |
# 構文
df_1.union( df_2 )
# 例文
df_ramen.union( df_ramen_2 )
ramen_id | name | price |
---|---|---|
1 | 醤油ラーメン | 600 |
2 | 塩ラーメン | 700 |
3 | 豚骨醤油ラーメン | 900 |
4 | 味噌ラーメン | 800 |
5 | 700 | 鶏白湯ラーメン |
6 | 800 | 濃厚とんこつラーメン |
7 | 600 | 塩つけめん |
5-2-2 列名を考慮してUnion (データフレームを縦方向に結合する)
2つのデータフレームを縦方向に結合(Union結合)します。5-2-1とは異なり、列の順番に関わらず、結合相手のデータフレームで同じ列名の列をUnion結合します。SQLのUNION ALLとは異なり重複行がある場合でも許容されます。そのため、結合結果から重複を削除したい場合はUnion結合したデータフレームに対してさらにdistinct()を実行する必要があります。
データフレームのunionByName()メソッドを使い、最初の引数としてUnion結合をしたい相手のデータフレームを指定します。
以下のデータフレーム1、データフレーム2をUnion結合するとした場合について説明します。
ramen_id | name | price |
---|---|---|
1 | 醤油ラーメン | 600 |
2 | 塩ラーメン | 700 |
3 | 豚骨醤油ラーメン | 900 |
4 | 味噌ラーメン | 800 |
ramen_id | price | name |
---|---|---|
5 | 700 | 鶏白湯ラーメン |
6 | 800 | 濃厚とんこつラーメン |
7 | 600 | 塩つけめん |
# 構文
df_1.unionByName( df_2 )
# 例文
df_ramen.unionByName( df_ramen_2 )
ramen_id | name | price |
---|---|---|
1 | 醤油ラーメン | 600 |
2 | 塩ラーメン | 700 |
3 | 豚骨醤油ラーメン | 900 |
4 | 味噌ラーメン | 800 |
5 | 鶏白湯ラーメン | 700 |
6 | 濃厚とんこつラーメン | 800 |
7 | 塩つけめん | 600 |