※2019/10:v2.5.0の追記を行いました。
#はじめに
世のデータ分析の流行はネコも杓子もPy.Py.Pythonです。
Rユーザーには肩身が狭くなりました(体感で7:3 = Python:R, 2018)。
そんなわけでRのdplyrでデータ処理していた私もPythonを学ぶことにし、Rのデータフレーム同様のpandasの使い方を調べてみましたが、あまり見つからなかったのと分かりづらかったので自分で調べてまとめてみることにしました。
#dplyrとは
データフレームの操作に特化したパッケージで、Rでの分析作業には必須のパッケージです。
詳しくは私のRの師であるmatuou1氏の記事をご覧あれ!
#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氏に敬意を表し、氏と同様に相模原市オープンデータライブラリで公開されている駅別乗降人員の推移を使用しています。
路線名=trainline、駅名=station、年度=year_YYYYに適宜列名を変更して使用しています。
##select ⇔ filter
列を選択します。
dataframe %>% select(trainline, station, year_1975)
dataframe.filter(['trainline', 'station', 'year_1975'])
trainline station year_1975
0 横浜線 橋本駅 31954.0
1 横浜線 相模原駅 29144.0
2 横浜線 矢部駅 6706.0
##filter ⇔ query
行を選択(絞り込み)します。
dataframe %>% filter(station == "町田駅(参考)")
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条件(|)は同様に使えます。
dataframe %>% filter(station == "町田駅(参考)" & trainline == "横浜線")
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の場所で説明します。
#平均値と行数を集計
dataframe %>% summarise(mean = mean(year_1975, na.rm=T), n = n())
mean n
1 29822.88 25
dataframe.agg({"year_1975":"mean","station":"count"})
year_1975 29822.882353
station 25.000000
dtype: float64
Rでは平均値としてmean、行数としてnが新しい列名として指定通り返してきました。一方Pythonでは新しい列名を指定できず、集計対象に指定した列名で、かつstackして返して来ます。そのため同じ列名に別の集約関数を適用すると、最後に適用した結果しか返しません。
dataframe.agg({"year_1975":"mean","year_1975":"count"})
year_1975 17
dtype: float64
##補足:summarise_at ⇔ 代用:agg
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
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と同様です。指定列したのユニークな組み合わせごとに集計等を行うことができます。
#沿線別に集計
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
dataframe.groupby(['trainline']).agg({"year_1975":"mean"})
year_1975
trainline
中央本線 NaN
京王線 NaN
小田急線 67854.600000
横浜線 26004.333333
相模線 1948.333333
##NamedAgg
v2.5.0から導入された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)
#新しい列名"new_column"を作成
dataframe %>% mutate(new_column = year_1975 * 2) %>% dplyr::select(new_column)
new_column
1 63908
2 58288
3 13412
... ...
#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={}を使用することです。
#列名を変更する
dataframe %>% rename(railline = trainline)
railline
1 横浜線
... ...
dataframe.rename(columns={'trainline':'rail'})
railline
0 横浜線
... ...
#まとめ
基本的な操作方法をまとめてみました。実際もっとかしこく楽に操作する方法があるみたいですが、これから数回に分けてまとめていこうと考えています。こうしてみるとRとdplyrは非常に便利で賢いデータ操作を可能にしているものだとあらためて認識できます。