Python>pandas>DataFrame:マルチインデックス・マルチカラム構造のDataFrameの操作

# <a id ="1">1.概要</a>
`groupby`メソッドを使ってデータをまとめると、マルチインデックスを持ったDataFrameのデータが出てくることがよくあるが、マルチインデックスのデータの取り扱いになれていなく操作がわからなくなることが多発。そこでいったん自分なりに整理した。

# <a id ="2">2.マルチインデックス・マルチカラムのDataFrame準備</a>
* サンプルデータ+`groupby`+`agg`メソッド<br>
カラム(`col1`,`col2`,`col3`)ごとに合計値(`sum`)、最大値(`max`)、最小値(`min`)をまとめる。<br>
　⇒マルチインデックスとマルチカラムをもつDataFrame

```python
df = pd.DataFrame({'col1':['a','b','c','a','a','b','b','b','c','a'],
                   'col2':['A','A','A','A','A','B','B','B','B','B'],
                   'col3':['AA','AA','BB','BB','AA','AA','BB','BB','CC','CC'],
                   'val1':np.arange(11,21),
                   'val2':np.arange(21,31)})

grouped = df.groupby(['col1','col2','col3'])
df_multi = grouped.agg([sum,max,min])
df_multi
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/d405b882-de1f-b521-4ed0-481929fbd932.png)


# <a id ="3">3.マルチインデックスの操作</a>

* マルチインデックスの操作として、以下を確認。
 * マルチインデックスの解除・再マルチインデックス化
 * レベルの変更・並び替え

## <a id ="3.1">3.1.マルチインデックスの解除</a>

```python
df_multi_reset = df_multi.reset_index()
df_multi_reset
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/31325be8-136b-f0bc-557d-1e8c78598a5c.png)


**<マルチインデックスに戻す>**
* 以下を実行すればOK。

```python
df_multi_reset.set_index(['col1','col2','col3'])
```

**<カラムのマルチ構造を解除？>**
* シンプルなコマンドでマルチカラムを解除する方法は見つけられず。。
　転置してマルチインデックスと同様の操作をできるようにするか、タプルにするか。。？

<転置する>

```python
df_multi.T
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/60a116ba-6935-763c-e51b-ec04091cdf52.png)
転置すればマルチインデックス(行)と同じ操作ができる。


<タプルにする>

```python
df_multi_tplc  = df_multi.copy()
df_multi_tplc.columns = df_multi_tplc.columns.values
df_multi_tplc
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/344947ed-cf44-5334-936a-f8b6e03b0b84.png)

タプルにすると、シングルカラムと同様に列を選んだりできる。

```python
df_multi_tplc[[('val1', 'sum'),('val2', 'max'),]]
```
※注`'(val1, sum)'`でなく`('val1', 'sum')`になる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/e5b2afc3-dba6-71e7-25c5-1885e03055ba.png)



タプルからマルチカラムにもどすコードは以下の通り。

```python
df_multi_tplc.columns = pd.MultiIndex.from_tuples(df_multi_tplc.columns)
df_multi_tplc
```
※`columns`を`index`に置き換えれば行についても同様のことができる。

## <a id ="3.2">3.2.並び替え</a>

行・列のレベルの入れ替えとレベルごとのデータ並び替えをして、データのまとまりを編集する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/82bdfd94-e400-abb2-9a65-9f61c8271a28.png)



### <a id ="3.2.1">3.2.1.マルチインデックスのレベル入れ替え＆ソート</a>

* `df_multi`の列は`col1`内のまとまり→`col2`のまとまり→`col3`のまとまりというような階層構造になっている。これを逆の構造にする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/cab25ad0-a5cd-a187-14af-2041323093e8.png)



```python
df_multi.swaplevel(0,2,axis=0).sort_index(axis=0)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/8cb0b4b0-730e-4378-9bcc-fe9ee474727c.png)


※`axis=1`を書かずに、`df_multi.swaplevel(0,2).sort_index()`としてもよい、

### <a id ="3.2.2">3.2.2.マルチカラムのレベル入れ替え＆ソート</a>
マルチカラムでも同様のことができる。

```python
df_multi.swaplevel(0,1,axis=1).sort_index(axis=1)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/634241b8-a616-6abb-2bae-b74be65a1c9c.png)






# <a id ="4">4.要素、DataFrameの抽出</a>

以下の赤枠の要素、青枠のDataFrameの抽出をする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/9d8bea8d-3426-7864-ca80-59e570a3cc23.png)




## <a id ="4.1">4.1.赤枠要素の抽出</a>
赤枠の「14」は、以下2通りで抽出可能。

```python
df_multi.loc[('a','A','BB'),('val1','min')]
```

マルチカラム、インデックスを無視して1行2列を抽出するとしてもOK。(左上端0行0列)

```python
df_multi.iloc[1,2]
```



## <a id ="4.2">4.2.青枠の部分DataFrameの抽出</a>
青枠部分は以下で抽出できる。


```python
df_multi.loc[('b','B'),('val2')]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/8d8c9c71-5286-7d37-5127-aed9063f728c.png)




# <a id ="5">5.マルチインデックスDataFrameのデータ編集</a>

マルチインデックスDataFrameのデータ追加・削除をする。


## <a id ="5.1">5.1.データの入力・更新</a>

```python
df_multi_edit =df_multi.copy()
df_multi_edit.loc[('d','D','DD')]=[1,2,3,4,5,6] # (1)行の追加
df_multi_edit.loc[:,('val3','sum')]=[1,2,3,4,5,6,7,8,9] # (2)列の追加
df_multi_edit.loc[('a','A','AA'),('val1','sum')]=100 # (3)値の書き換え
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/26c5e6bc-8b3e-f0e6-2b53-d498de548e55.png)


## <a id ="5.2">5.2.行・列の削除</a>

```python
df_multi_edit.drop(('val2'),axis=1,inplace=True) #val2列の削除
df_multi_edit.drop(('c','A','BB'),axis=0,inplace=True) #('c','A','BB')行の削除
df_multi_edit
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/b462610a-dfc6-4bfb-458b-aad6ac83545f.png)

## <a id ="5.3">5.3.データの結合</a>

結合させるデータの準備

```python
df_vol4 = pd.DataFrame({('vol4','a'):range(31,40),
             ('vol4','b'):np.arange(49,40,-1)},
             index= [('a', 'A', 'AA'),('a', 'A', 'BB'),('a', 'B', 'CC'), ('b', 'A', 'AA'),
                     ('b', 'B', 'AA'),('b', 'B', 'BB'),('c', 'A', 'BB'),('c', 'B', 'CC'),('d', 'D', 'DD')
                    ])
df_vol4.index = pd.MultiIndex.from_tuples(df_add.index,names=['col1','col2','col3'])
df_vol4.columns = pd.MultiIndex.from_tuples(df_add.columns)
df_vol4     
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/bd7e86ba-78aa-10a4-7042-0839866ba97c.png)

```python
pd.merge(df_multi_edit,df_vol4,on=('col1','col2','col3'),how ='outer')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/bee717d4-9bba-5b12-5a69-f4536de0c195.png)

# <a id ="6">6.参照資料</a>

以下の資料を勉強しながら記事を書きました。

3.1.マルチインデックスの解除
>* pandasのMultiindexの指定・追加・解除・ソート・レベル変更<br>https://note.nkmk.me/python-pandas-multiindex-set-reset-sort-swap/
* How to do Multi-Column from_tuples?<br>https://stackoverflow.com/questions/37835508/how-to-do-multi-column-from-tuples

3.2.並び替え
>* pandas.DataFrame.swaplevel<br>https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.swaplevel.html
* Swapping/Ordering multi-index columns in pandas<br>https://stackoverflow.com/questions/40411300/swapping-ordering-multi-index-columns-in-pandas


以上