MultiIndexを持つデータフレーム同士の結合

# 概要
`MultiIndex`を使用したデータフレームで`pandas.DataFrame.join`を実行する際に気をつけるべきことを記します。

# 準備


```python
# 必要なモジュールをインポート
import pandas as pd

# データフレームの作成
df = pd.DataFrame()
df["name"] = ["hoge"] * 6 + ["fuga"] * 6
df["year"] = (["2017"] * 2 + ["2018"] * 2 + ["2019"] * 2) * 2
df["season"] = ["summer", "winter"] * 6
df["height"] = [160, 161, 163, 166, 168, 169, 180, 180, 181, 181, 182, 183]
df["weight"] = [50, 52, 52, 55, 54, 56, 70, 72, 73, 75, 75, 78]
df
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>name</th>
      <th>year</th>
      <th>season</th>
      <th>height</th>
      <th>weight</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>hoge</td>
      <td>2017</td>
      <td>summer</td>
      <td>160</td>
      <td>50</td>
    </tr>
    <tr>
      <th>1</th>
      <td>hoge</td>
      <td>2017</td>
      <td>winter</td>
      <td>161</td>
      <td>52</td>
    </tr>
    <tr>
      <th>2</th>
      <td>hoge</td>
      <td>2018</td>
      <td>summer</td>
      <td>163</td>
      <td>52</td>
    </tr>
    <tr>
      <th>3</th>
      <td>hoge</td>
      <td>2018</td>
      <td>winter</td>
      <td>166</td>
      <td>55</td>
    </tr>
    <tr>
      <th>4</th>
      <td>hoge</td>
      <td>2019</td>
      <td>summer</td>
      <td>168</td>
      <td>54</td>
    </tr>
    <tr>
      <th>5</th>
      <td>hoge</td>
      <td>2019</td>
      <td>winter</td>
      <td>169</td>
      <td>56</td>
    </tr>
    <tr>
      <th>6</th>
      <td>fuga</td>
      <td>2017</td>
      <td>summer</td>
      <td>180</td>
      <td>70</td>
    </tr>
    <tr>
      <th>7</th>
      <td>fuga</td>
      <td>2017</td>
      <td>winter</td>
      <td>180</td>
      <td>72</td>
    </tr>
    <tr>
      <th>8</th>
      <td>fuga</td>
      <td>2018</td>
      <td>summer</td>
      <td>181</td>
      <td>73</td>
    </tr>
    <tr>
      <th>9</th>
      <td>fuga</td>
      <td>2018</td>
      <td>winter</td>
      <td>181</td>
      <td>75</td>
    </tr>
    <tr>
      <th>10</th>
      <td>fuga</td>
      <td>2019</td>
      <td>summer</td>
      <td>182</td>
      <td>75</td>
    </tr>
    <tr>
      <th>11</th>
      <td>fuga</td>
      <td>2019</td>
      <td>winter</td>
      <td>183</td>
      <td>78</td>
    </tr>
  </tbody>
</table>
</div>



# MultiIndexを設定
`"name", "year", "season"`をMultiIndexに持ち、`"height"`をカラムに持つデータフレームを作成します。


```python
df1 = df.set_index(["name", "year", "season"])[['height']]
df1
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th></th>
      <th></th>
      <th>height</th>
    </tr>
    <tr>
      <th>name</th>
      <th>year</th>
      <th>season</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th rowspan="6" valign="top">hoge</th>
      <th rowspan="2" valign="top">2017</th>
      <th>summer</th>
      <td>160</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>161</td>
    </tr>
    <tr>
      <th rowspan="2" valign="top">2018</th>
      <th>summer</th>
      <td>163</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>166</td>
    </tr>
    <tr>
      <th rowspan="2" valign="top">2019</th>
      <th>summer</th>
      <td>168</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>169</td>
    </tr>
    <tr>
      <th rowspan="6" valign="top">fuga</th>
      <th rowspan="2" valign="top">2017</th>
      <th>summer</th>
      <td>180</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>180</td>
    </tr>
    <tr>
      <th rowspan="2" valign="top">2018</th>
      <th>summer</th>
      <td>181</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>181</td>
    </tr>
    <tr>
      <th rowspan="2" valign="top">2019</th>
      <th>summer</th>
      <td>182</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>183</td>
    </tr>
  </tbody>
</table>
</div>



次に、MultiIndexの順番を`"name"`->`"season"`->`"year"`に変え、`"weight"`をカラムに持つデータフレームを作成します。


```python
df2 = df.set_index(["name", "season", "year"])[['weight']]
df2
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th></th>
      <th></th>
      <th>weight</th>
    </tr>
    <tr>
      <th>name</th>
      <th>season</th>
      <th>year</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th rowspan="6" valign="top">hoge</th>
      <th>summer</th>
      <th>2017</th>
      <td>50</td>
    </tr>
    <tr>
      <th>winter</th>
      <th>2017</th>
      <td>52</td>
    </tr>
    <tr>
      <th>summer</th>
      <th>2018</th>
      <td>52</td>
    </tr>
    <tr>
      <th>winter</th>
      <th>2018</th>
      <td>55</td>
    </tr>
    <tr>
      <th>summer</th>
      <th>2019</th>
      <td>54</td>
    </tr>
    <tr>
      <th>winter</th>
      <th>2019</th>
      <td>56</td>
    </tr>
    <tr>
      <th rowspan="6" valign="top">fuga</th>
      <th>summer</th>
      <th>2017</th>
      <td>70</td>
    </tr>
    <tr>
      <th>winter</th>
      <th>2017</th>
      <td>72</td>
    </tr>
    <tr>
      <th>summer</th>
      <th>2018</th>
      <td>73</td>
    </tr>
    <tr>
      <th>winter</th>
      <th>2018</th>
      <td>75</td>
    </tr>
    <tr>
      <th>summer</th>
      <th>2019</th>
      <td>75</td>
    </tr>
    <tr>
      <th>winter</th>
      <th>2019</th>
      <td>78</td>
    </tr>
  </tbody>
</table>
</div>



# データフレームの結合
上記で作成した二つのデータフレームをインデックスを`key`として結合します。


```python
df1.join(df2)

# NotImplementedError: merging with more than one level overlap on a multi-index is not implemented
```


すると上記のようなエラー文が発生します。原因はそう、インデックスの順番が異なっているからです。

# インデックスの順番を揃えて結合
インデックスの順番を揃え直し、再び結合してみます。


```python
df2.index = df2.index.swaplevel("season", "year")
df1.join(df2)
```

<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th></th>
      <th></th>
      <th>height</th>
      <th>weight</th>
    </tr>
    <tr>
      <th>name</th>
      <th>year</th>
      <th>season</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th rowspan="6" valign="top">hoge</th>
      <th rowspan="2" valign="top">2017</th>
      <th>summer</th>
      <td>160</td>
      <td>50</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>161</td>
      <td>52</td>
    </tr>
    <tr>
      <th rowspan="2" valign="top">2018</th>
      <th>summer</th>
      <td>163</td>
      <td>52</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>166</td>
      <td>55</td>
    </tr>
    <tr>
      <th rowspan="2" valign="top">2019</th>
      <th>summer</th>
      <td>168</td>
      <td>54</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>169</td>
      <td>56</td>
    </tr>
    <tr>
      <th rowspan="6" valign="top">fuga</th>
      <th rowspan="2" valign="top">2017</th>
      <th>summer</th>
      <td>180</td>
      <td>70</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>180</td>
      <td>72</td>
    </tr>
    <tr>
      <th rowspan="2" valign="top">2018</th>
      <th>summer</th>
      <td>181</td>
      <td>73</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>181</td>
      <td>75</td>
    </tr>
    <tr>
      <th rowspan="2" valign="top">2019</th>
      <th>summer</th>
      <td>182</td>
      <td>75</td>
    </tr>
    <tr>
      <th>winter</th>
      <td>183</td>
      <td>78</td>
    </tr>
  </tbody>
</table>
</div>



うまくいきました。

# まとめ
今回は簡単な例で説明したため、こんなことが本当に起こるのかと思われますが、実際に大量のデータセットで複雑な結合などをしているとこのような状態になることがあります（少なくとも僕はありました）。MultiIndexを使わずとも処理する方法はありますが、もし使う際には注意する必要があります。