dplyr使いのためのpandas マルチカラム操作編

#はじめに
今までの関連記事です。
[dplyr使いのためのpandas 基礎編](https://qiita.com/T_Shinomiya/items/d445ce2f3d95160ec944)
[dplyr使いのためのpandas スライスsliceとインデックスindex編](https://qiita.com/T_Shinomiya/items/f800ac988d7a1e25bf8b)

#マルチカラム？
pandasのdataframeはRのdataframeとの大きな違いにMultiIndexという構造があります。
階層型(hierarchical)とも、多重とも訳されているようです。どのようなものか、例を出した方が分かりやすいので、早速例を見ていきましょう。

##実例と使用データ
基礎編同様、[相模原市オープンデータライブラリ](http://opendata.city.sagamihara.kanagawa.jp/)で公開されている[駅別乗降人員](http://opendata.city.sagamihara.kanagawa.jp/dataset/jyoukou/resource/76699e97-2dbd-4c9c-9df5-74d493aeb644)の推移を使用しています。

路線名=trainline、駅名=station、年度=year_YYYYに適宜列名を変更して使用しています。

#MultiIndex
こう実行すると、

```Python:集計
import pandas as pd
import numpy as np

#trainlineとstationごとに集計（見やすさのため一部抜粋）
dataframe.\
    query('trainline=="相模線"').\
    groupby(['trainline','station']).\
    agg({"year_1975":"mean","year_1980":[sum,max,np.mean] })
```

こんな感じの出力結果になります。

|<br><br>trainline|<br><br>station|year_1975    <br>mean<br>　|year_1980    <br>sum<br>　|<br>max<br>              　|<br>             mean<br>　|
|:--|:--|:--------------------|:-------------------|:-------------------|:--------------------|
|**相模線**|**上溝駅**|                 4526|                4562|                4562|                 4562|
||**下溝駅**|                  836|                 856|                 856|                  856|
||**南橋本駅**|                 2536|                2006|                2006|                 2006|
||**原当麻駅**|                 1874|                2930|                2930|                 2930|
||**番田駅**|                 1218|                2334|                2334|                 2334|
||**相武台下駅**|                  700|                 794|                 794|                  794|

groupbyで指定したtrainlineとstationごとに集計されています。この太字の部分がMulitiIndexです。行のindexがgroupbyで指定したtrainline-station、列のindexがyear_1975-mean, year_1980-sum/max/meanと階層化されています。R：dataframeとの大きな違いはこの列の階層構造（マルチカラム）があることです。
R：dplyrにおけるdataframeは列名が操作の基本となり、列名は常に絶対的なものです。

##マルチカラムの操作
例として乗降人数が1980年のmaxが2500人未満かつ1975年の平均が1000人より多い駅を抽出します。

```R:R_dplyr：集計-マルチカラム操作
dataframe %>% 
    filter(trainline =="相模線") %>%
    group_by(trainline, station) %>%
    summarise(year_1975_mean = mean(year_1975, na.rm=T),
              year_1980_sum = sum(year_1980, na.rm=T),
              year_1980_max = max(year_1980),
              year_1980_mean = mean(year_1980, na.rm=T)) %>%
    filter(year_1975_mean > 1000 & year_1980_max < 2500)
```

```Python:Python_pandas：集計-マルチカラム操作
#マルチカラム操作時に必要なおまじない（スライスを可能にする）
idx = pd.IndexSlice

#集計後マルチカラムを指定して行選択
dataframe.\
    query('trainline=="相模線"').\
    groupby(['trainline','station']).\
    agg({"year_1975":"mean","year_1980":[sum,max,np.mean] }).tail(6).\
    loc[lambda df: idx[ df[('year_1975','mean')] > 1000 ] & idx[df[('year_1980','max')] < 2500] ]
```
集計後の行選択はloc[]とidx[]を使用します。どうやらquery()はマルチカラムを指定できないようです。

#補足
##loc? スライス？
[dplyr使いのためのpandas スライスsliceとインデックスindex編](https://qiita.com/T_Shinomiya/items/f800ac988d7a1e25bf8b)にまとめました。

##lambda
スライスなどで明示的にdataframeを指定しなければならない場合、lambda式を使うと便利です。lambdaは無名関数というもので、defの親戚のようなものです。使い方は式の先頭に以下のように記述すれば以降はdataframe名称を指定した引数（ここではdf）で代用できます。

```Python:lambda
#lambda 引数:条件式
lambda df:
#例：assignでカラムを追加する場合
dataframe.assign(lambda df: new_column = df.year_1975 * 2)
```

#まとめ
MultiIndexの操作はR：dataframeにはない機能です。
少々ややこしいですが、dplyr同様の操作ができるよう方法を調べてみました。