dplyr使いのためのpandas 基本編

**※2019/10：v2.5.0の追記を行いました。**

#はじめに
世のデータ分析の流行はネコも杓子もPy.Py.Pythonです。
Rユーザーには肩身が狭くなりました（体感で7:3 = Python:R, 2018）。
そんなわけでRのdplyrでデータ処理していた私もPythonを学ぶことにし、Rのデータフレーム同様のpandasの使い方を調べてみましたが、あまり見つからなかったのと分かりづらかったので自分で調べてまとめてみることにしました。


#dplyrとは
データフレームの操作に特化したパッケージで、Rでの分析作業には必須のパッケージです。
詳しくは私のRの師である[matuou1氏の記事](https://qiita.com/matsuou1/items/db6e8c48fcfd791dd876)をご覧あれ！

#dplyr⇔pandasの比較
pandasによるデータフレーム操作の基本はdplyrと同じです。
二次元テーブルの列名を　”列名”　として説明します。いろいろと操作の方法はありますが、``dplyr使いがpandasを初めて操作するときになじみやすい方法をあげています。``

| dplyr                  | pandas    | 説明        |
|:-----------------------|:--------  |:-----------|
|%>%                     |.         |  pipeを使う方法は別途 |
|select(列名)            | filter(['列名']) | 列選択関数。filterだけど列選択なのでdplyr使いは困惑。[['列名']] でも可 |
|filter(抽出条件式)       | query('抽出条件式')  | 行選択関数。dplyrと使い方は一緒。やり方はいろいろあるけど、最初はこれがとっつきやすい  |
|summarise()             |代用：agg()   | 集計関数。一番大きな違いがある（後述）  |
|group_by(列名)           |groupby(['列名'])     | group関数。使い方は一緒  |
|mutate(新列名 = 〇〇)     |eval(新列名 = 〇〇)    | ほぼ一緒（assign()も可）  |
|rename(新列名 = 旧列名)   |rename(columns={'旧列名' : '新列名')     | ほぼ一緒 |

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

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

##select ⇔ filter
列を選択します。

```R:R：select
dataframe %>% select(trainline, station, year_1975)
```
```Python:Python：filter
dataframe.filter(['trainline', 'station', 'year_1975'])

	trainline    station    year_1975
0	横浜線        橋本駅     31954.0	
1	横浜線        相模原駅   29144.0	
2	横浜線        矢部駅     6706.0
```

##filter ⇔ query
行を選択（絞り込み）します。

```R:R：filter
dataframe %>% filter(station == "町田駅（参考）")
```
```Python:Python：query
dataframe.query('station == "町田駅（参考）"')

	trainline    station        year_1975    year_1980    ...
6	横浜線        町田駅（参考）  54506.0      79366.0      ...
20	小田急線      町田駅（参考）  154831.0     197393.0     ...
```
**ただqueryの問題点としてNoneや欠損値NaNがある列に対して適用しようとするとエラーになる。**
それはそれで欠損データを把握できることになりますが、dplyrに慣れている身としては不便。
fillna(0), fillna({'列名': '置き換え文字'}) 等で置き換えしておく必要があるようです。


複数の条件を指定した場合も可能です。
AND条件（&）、OR条件（|）は同様に使えます。

```R:R：filter：AND条件
dataframe %>% filter(station == "町田駅（参考）" & trainline == "横浜線")
```
```Python:Python：query：AND条件
dataframe.query('station == "町田駅（参考）" & trainline == "横浜線"')

	trainline    station        year_1975    year_1980    ...
6	横浜線        町田駅（参考）  54506.0      79366.0      ...
```

##summarise ⇔ 代用：agg
pandasでsummariseを行う場合、全く同じ方法はありません。
例として以下に、平均値と行数を集計するコードを挙げます

**※2019/10 追記
v2.5.0からNamedAgg機能が追加されよりdplyr的になりました。NamedAggの場所で説明します。**

```R:R：summarise
#平均値と行数を集計
dataframe %>% summarise(mean = mean(year_1975, na.rm=T), n = n())
  mean      n
1 29822.88  25
```
```Python:Python：agg
dataframe.agg({"year_1975":"mean","station":"count"})

year_1975    29822.882353
station         25.000000
dtype: float64
```
Rでは平均値としてmean、行数としてnが新しい列名として指定通り返してきました。一方Pythonでは新しい列名を指定できず、集計対象に指定した列名で、かつstackして返して来ます。そのため同じ列名に別の集約関数を適用すると、最後に適用した結果しか返しません。

```Python:Python：agg_同じ列名に関数を適用すると？
dataframe.agg({"year_1975":"mean","year_1975":"count"})

year_1975         17
dtype: float64
```

##補足：summarise_at ⇔ 代用：agg
summarise_at（複数の指定列に、複数の集約関数を適用する）を行う場合を補足します。

```R:R：summarise_at
#和、最小値、平均値を集計
dataframe %>% summarise_at(vars(year_1975),funs(sum,min,mean),na.rm=T)

     sum min     mean
1 506989 700 29822.88

dataframe %>% summarise_at(vars(year_1975,year_1980),funs(sum,min,mean),na.rm=T)

  year_1975_sum year_1980_sum year_1975_min year_1980_min year_1975_mean year_1980_mean
1        506989        599739           700           794       29822.88       35278.76
```
```Python:Python：agg
import numpy as np

dataframe.agg({"year_1975":[sum,min,np.mean]})

            year_1975
sum     506989.000000
min     700.000000
mean	29822.882353

#和、最小値、平均値を集計（year_1975は平均値のみ）
dataframe.agg({"year_1975":"mean","year_1980":[sum,min,np.mean] })

           year_1975	   year_1980
mean	29822.882353	35278.764706
min     NaN         	  794.000000
sum     NaN            599739.000000
```
ある列に集約関数を複数適用する場合、Rではsummariseでもsumarise_atでも適用することができ、結果は新しい列名として返ります。
一方Pythonではstackした結果を返すため、集計対象の列は一度しか指定できません。

##group_by ⇔ groupby
dplyrと同様です。指定列したのユニークな組み合わせごとに集計等を行うことができます。

```R:R：group_byしてsummarise
#沿線別に集計
dataframe %>% group_by(trainline) %>% summarise(mean1975 = mean(year_1975, na.rm=T))

  trainline  mean1975
1    横浜線 26004.333
2    京王線       NaN
3  小田急線 67854.600
4    相模線  1948.333
5  中央本線       NaN
```
```Python:Python：groupbyしてagg
dataframe.groupby(['trainline']).agg({"year_1975":"mean"})

            year_1975
trainline	
中央本線     NaN
京王線       NaN
小田急線     67854.600000
横浜線       26004.333333
相模線       1948.333333
```
##NamedAgg
**v2.5.0から導入されたNamedAgg機能で、集計後のカラム名を新しく設定することが可能になりました。**

```Python:Python：groupbyしてagg(NamedAgg)
dataframe.groupby(['trainline']).agg(new_column =("year_1975","mean")).reset_index()
# agg(新しいカラム名 =('集計対象カラム', '関数'))

trainline  new_column
中央本線     NaN
京王線       NaN
小田急線     67854.600000
横浜線       26004.333333
相模線       1948.333333
```
なぜかgroupbyをしないとエラーがでるのですが(多分バグ）。
NamedAggのおかげでかなりdplyr的になりました。

##mutate ⇔ eval ~~assign~~
dplyrと同様です。新しい列を作成することができます。
以前はpythonでの置き換えをassign()としていましたが、eval()の方がよりdplyr的でしたので追記（2018-12-20）

```R:R：mutate
#新しい列名"new_column"を作成
dataframe %>% mutate(new_column = year_1975 * 2) %>% dplyr::select(new_column)

   new_column
1       63908
2       58288
3       13412
...     ...
```
```Python:Python：eval
#eval
dataframe.eval('new_column = year_1975 * 2').filter(['new_column'])
#assign（一応）
dataframe.assign(new_column = dataframe.year_1975 * 2).filter(['new_column'])

	new_column
0	63908.0
1	58288.0
2	13412.0
```
new_column作成のために「year_1975列*2」を指定しています。
python - assignでは . で処理しているにもかかわらず、操作しているデータフレーム内の列を認識してはくれず、「dataframe.year_1975 * 2」のように指定してやらなければいけません（pipeについては別途）



##rename ⇔ rename
dplyrと同様です。列名を変更することができます。違いは旧列名と新列名の順番の違いとcolumns={}を使用することです。

```R:R：rename
#列名を変更する
dataframe %>% rename(railline = trainline)

   railline
1    横浜線
...    ...
```
```Python:Python：rename
dataframe.rename(columns={'trainline':'rail'})

   railline
0    横浜線
...    ...
```

#まとめ
基本的な操作方法をまとめてみました。実際もっとかしこく楽に操作する方法があるみたいですが、これから数回に分けてまとめていこうと考えています。こうしてみるとRとdplyrは非常に便利で賢いデータ操作を可能にしているものだとあらためて認識できます。