pysparkのJoin個人的まとめ

## はじめに
Pysparkでデータをいじくっている際にjoinをする事があるのですが、joinの内容を毎回確認するので確認用のページを作成しようかと思い立ち。
~~SQLが頭に入っていれば問題ないのでしょうが、都度調べれば良いと思ってるので~~
pythonは3系、pysparkは2.0系(2.4あたり)を想定しています。

### やること
以下のコードで作成した```df_1```と```df_2```について、```df_1```に対してidで```df_2```をjoinします。
片方だけにidがあったり、重複してたり、など考慮してjoinの種類全パターンでの動作を確認できるようなDataFrameにしているつもりです。

```python:sample.py
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はこんな感じ

<table border=0>
    <tr>
        <td>
            df_1のDataframe
            <table border>
                <tr>
                    <th>id</th>
                    <th>data</th>
                </tr>
                <tr>
                    <td>a</td>
                    <td>1_a1</td>
                </tr>
                <tr>
                    <td>a</td>
                    <td>1_a2</td>
                </tr>
                <tr>
                    <td>b</td>
                    <td>1_b1</td>
                </tr>
                <tr>
                    <td>b</td>
                    <td>1_b2</td>
                </tr>
                <tr>
                    <td>c</td>
                    <td>1_c1</td>
                </tr>
                <tr>
                    <td>d</td>
                    <td>1_d1</td>
                </tr>
                <tr>
                    <td>e</td>
                    <td>1_e1</td>
                </tr>
            </table>
        </td>
        <td valign="top">
            df_2のDataframe
            <table border>
                <tr>
                    <th>id</th>
                    <th>data</th>
                </tr>
                <tr>
                    <td>a</td>
                    <td>2_a1</td>
                </tr>
                <tr>
                    <td>b</td>
                    <td>2_b1</td>
                </tr>
                <tr>
                    <td>b</td>
                    <td>2_b2</td>
                </tr>
                <tr>
                    <td>c</td>
                    <td>2_c1</td>
                </tr>
                <tr>
                    <td>d</td>
                    <td>2_d1</td>
                </tr>
                <tr>
                    <td>d</td>
                    <td>2_d2</td>
                </tr>
                <tr>
                    <td>f</td>
                    <td>2_f1</td>
                </tr>
            </table>
        </td>
    </tr>
</table>

以下コードの"how"の部分を変えて実行した際に出来上がるDataFrameを纏めていきます。

```python:sample.py

# 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 |Noneデータで残る
| 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が重複して残るとか)ので注意