はじめに
Pysparkでデータをいじくっている際にjoinをする事があるのですが、joinの内容を毎回確認するので確認用のページを作成しようかと思い立ち。
SQLが頭に入っていれば問題ないのでしょうが、都度調べれば良いと思ってるので
pythonは3系、pysparkは2.0系(2.4あたり)を想定しています。
やること
以下のコードで作成したdf_1
とdf_2
について、df_1
に対してidでdf_2
をjoinします。
片方だけにidがあったり、重複してたり、など考慮してjoinの種類全パターンでの動作を確認できるようなDataFrameにしているつもりです。
df_1 = spark.createDataFrame(
[('a', "1_a1",),
('a', "1_a2", ),
('b', "1_b1", ),
('b', "1_b2", ),
('c', "1_c1", ),
('d', "1_d1", ),
('e', "1_e1", ),],
['id', 'data',])
df_2 = spark.createDataFrame(
[('a', "2_a1",),
('b', "2_b1", ),
('b', "2_b2", ),
('c', "2_c1", ),
('d', "2_d1", ),
('d', "2_d2", ),
('f', "2_f1", ),],
['id', 'data',])
作成されるDataframeはこんな感じ
df_1のDataframe
|
df_2のDataframe
|
以下コードの"how"の部分を変えて実行した際に出来上がるDataFrameを纏めていきます。
# howの種類はこれだけあり、同じ行のものは同じ動作をする(はず)
# inner
# cross,
# outer, full, full_outer
# left, left_outer
# right, right_outer
# left_semi
# left_anti
result = df_1.join(df_2, df_1.id == df_2.id, "how")
結果
inner
一言メモ
- とりあえず共通のものを残したい時に使う
- 両方のdfに共通のidが残る
- 該当行の組み合わせ分、データが増える
- df_1のa(2行) x df_2のa(1行) → aが2行
- df_1のb(2行) x df_2のb(2行) → bが4行
- df_1のd(1行) x df_2のd(2行) → dが2行
id | data | id | data |
---|---|---|---|
a | 1_a1 | a | 2_a1 |
a | 1_a2 | a | 2_a1 |
b | 1_b1 | b | 2_b1 |
b | 1_b1 | b | 2_b2 |
b | 1_b2 | b | 2_b1 |
b | 1_b2 | b | 2_b2 |
c | 1_c1 | c | 2_c1 |
d | 1_d1 | d | 2_d1 |
d | 1_d1 | d | 2_d2 |
cross
一言メモ
- innerと同じ(データ不備?)
id | data | id | data |
---|---|---|---|
a | 1_a1 | a | 2_a1 |
a | 1_a2 | a | 2_a1 |
b | 1_b1 | b | 2_b1 |
b | 1_b1 | b | 2_b2 |
b | 1_b2 | b | 2_b1 |
b | 1_b2 | b | 2_b2 |
c | 1_c1 | c | 2_c1 |
d | 1_d1 | d | 2_d1 |
d | 1_d1 | d | 2_d2 |
outer, full, full_outer
一言メモ
- とりあえず全て残したい時に使う
- 対応が無いものも残る
- inner + 対応の無いもの
- df_1のみe、df_2のみfがある → 対応の無いところはNoneで残る
| id | data | id | data |
|:--|:--|:--|:--|:--|
| None | None | f | 2_f1 |
| a | 1_a1 | a | 2_a1 |
| a | 1_a2 | a | 2_a1 |
| b | 1_b1 | b | 2_b1 |
| b | 1_b1 | b | 2_b2 |
| b | 1_b2 | b | 2_b1 |
| b | 1_b2 | b | 2_b2 |
| c | 1_c1 | c | 2_c1 |
| d | 1_d1 | d | 2_d1 |
| d | 1_d1 | d | 2_d2 |
| e | 1_e1 | None | None |
left, left_outer
一言メモ
- 左(df_1、joinされる方)を残したい時に使う
- 左を基準にjoin
- inner + 左で対応の無いもの
- df_1のみeがある → 対応の無いところはNoneで残る
id | data | id | data |
---|---|---|---|
a | 1_a1 | a | 2_a1 |
a | 1_a2 | a | 2_a1 |
b | 1_b1 | b | 2_b1 |
b | 1_b1 | b | 2_b2 |
b | 1_b2 | b | 2_b1 |
b | 1_b2 | b | 2_b2 |
c | 1_c1 | c | 2_c1 |
d | 1_d1 | d | 2_d1 |
d | 1_d1 | d | 2_d2 |
e | 1_e1 | None | None |
right, right_outer
一言メモ
- 右(df_2、joinする方)を残したい時に使う
- 右を基準にjoin
- inner + 右で対応の無いもの
- df_2のみfがある → 対応の無いところはNoneで残る
| id | data | id | data |
|--:|:--|:--|:--|:--|
| None | None | f | 2_f1 |
| a | 1_a1 | a | 2_a1 |
| a | 1_a2 | a | 2_a1 |
| b | 1_b1 | b | 2_b1 |
| b | 1_b2 | b | 2_b1 |
| b | 1_b1 | b | 2_b2 |
| b | 1_b2 | b | 2_b2 |
| c | 1_c1 | c | 2_c1 |
| d | 1_d1 | d | 2_d1 |
| d | 1_d1 | d | 2_d2 |
left_semi
一言メモ
- 左の共通部のみ残したい場合に使う
- 左を基準に、左と右とで共通のものが残る
- 右のデータは残らない
- df_1のid、data列が1つずつ残る
- idに重複があってもなくても動作は変わらない
- df_1のa(2行) x df_2のa(1行) → aが2行
- df_1のb(2行) x df_2のb(2行) → bが2行
id | data |
---|---|
a | 1_a1 |
a | 1_a2 |
b | 1_b1 |
b | 1_b2 |
c | 1_c1 |
d | 1_d1 |
left_anti
一言メモ
- 左の非共通部のみ残したい場合に使う
- 左を基準に、左と右とで共通ではないものが残る
- 右のデータは残らない
- df_1のid、data列が1つずつ残る
id | data |
---|---|
e | 1_e1 |
〆
- pysparkのバージョンによってhowが増えたり動作が変わってくる(と思われる)ので注意
- SQLと微妙に動作が異なる(innerとかでidが重複して残るとか)ので注意