ずっとデータの仕事をRで行っていたのですが、思い立ってPythonに乗り換えることにしました。Pythonという言語自体には以前から定期的に使っていたこともあり全く抵抗はありませんが、以降に際して乗り越えなければならない大きなハードルが大きく分けて4つあります*1。
- Rのdata.frame、dplyrで行っていた作業をpandasで出来るようにならなくてはならない
- ggplotで行っていた視覚化の作業をMatplotlibで出来るようにならなくてはならない
- RStudioではなくJupyter Notebookで作業を出来るようにならなくてはならない
- Rでやっていた統計処理(検定や分布に従った乱数の生成)をSciPyで出来るようにならなくてはならない
完全に移行するまでには頭も手も色々な事に慣れていく必要があると思います。この投稿にはそこで得た知識をメモしていく予定です。
Rのdataframe、dplyrからPythonのpandas等への移行
データに全体ランクやグループ毎ランクを付ける
データをいじっていると割とデータにランク付けをしたりします(例えば得点で順位を付けるとか。)それをデータ全体に対して行ったり、あるグループ毎に行ったりします。これdplyrだとやり方が美しくて好きなんですよね。
R
library(dplyr) df <- data.frame(Name = c("Cameron", "Bob", "Cameron", "Cameron", "Bob", "Alice", "Alice", "Alice", "Bob"), Score = c(160, 98, 109, 84, 91, 120, 98, 107, 82)) df %>% mutate(OverallRank = dense_rank(desc(Score))) %>% group_by(Name) %>% mutate(PersonalRank = dense_rank(desc(Score)))
Python
import numpy as np import pandas as pd df = pd.DataFrame({'Name': ["Cameron", "Bob", "Cameron", "Cameron", "Bob", "Alice", "Alice", "Alice", "Bob"], 'Score': [160, 98, 109, 84, 91, 120, 98, 107, 82]}) df['OverallRank'] = df.Score.rank(method='dense', ascending=False).astype(int) df['PersonalRank'] = df.groupby(['Name'])['Score'].rank(method='dense', ascending=False).astype(int)
いくつかの列の値を使って新しい列の値を作る
これもしばしばやります。列Aと列Bの値を使って列Cの値を作るという感じです。
R
df <- data.frame(A = c(10,20,30), B = c(5,25,35)) df["C"] = ifelse(df["A"] > df["B"], "A is bigger", "B is bigger or equal")
Python
df = pd.DataFrame({'A': [10,20,30], 'B': [5,25,35]}) df['C'] = ["A is bigger" if b else "B is bigger or equal" for b in df['A'] > df['B']]
Pythonで美しく書く方法が分かりませんでした。lambdaを使うと更にごちゃごちゃしてしまいます。
df['C'] = list(map(lambda b: "A is bigger " if b else "B is bigger or equal", df['A'] > df['B']))
RのggplotからPythonのMatplotlib等への移行
普通の棒グラフを書く
とりえあず普通に棒グラフをmatplotlibで書いてみます。pandasにビルトインされているようなので、使える限りはそれを使おうと思います。
import numpy as np import pandas as pd df = pd.DataFrame({'City':["Tokyo", "Beijing", "New York", "Paris"], 'Value1':[250,500,200,125], 'Value2':[40, 30, 20, 55]}) df.plot.bar(x='City', y=['Value1','Value2'], stacked=False)
グラフを画像で保存する
Rのときはggsave()を使っていました。ggsave()を使うとかなり綺麗な画像が出力出来て良かったんですよね。Pythonでpandasのdataframeを使ってやる場合、以下のようにget_figure()した後にsavefig()を使うのが良さそうです。
fig = df.plot.bar(x='City', y=['Value1','Value2'], stacked=False).get_figure() fig.savefig("MyFile.jpg")
データフレームのひとつ前(またはひとつ先)の行の値を取得する
割とよくする作業なのですが、データフレームのひとつ前、またはひとつ先のデータを取得する作業です。
Rだとこのように書けます。
df <- data.frame(A=c(10,20,30,40,50)) df %>% mutate(PrevA = lag(A))
pandasだとlead(), lag()に値する関数のようなものが見つからないので、shift()を使ってずらしたデータフレームを作り、それをmerge/concatするしかなさそうです。
df = pd.DataFrame({'A':[10,20,30,40,50]}, index=range(5)) df = pd.concat([df, df.shift(1).rename(columns={'A':'PrevA'})], axis=1)
RのRStudioからPythonのJupyter Notebookへの移行
コードブロックを実行する
むしろR Studioの方が特殊なんでしょうけれど、R StudioがCTRL+ENTERでコードブロックを実行できるのに対し、Jupyter NotebookはSHIFT+ENTERで実行です。こういう手癖は地味に生産性に差が出てくるのでしっかりと切り替えられるようにしたいです。
dataframeの中身を目視する
R Studioだとdata.frameの中身を目視したいときにView()という便利な関数があって、それっぽいUIを出してくれるんですよね。今のところJupyter Notebookであれば
from IPython.display import HTML HTML(df.to_html(index=False))
というやり方でテーブルをレンダリングするのが良さそう。ただ大きなdataframeをレンダリングしようとしたらブラウザ止まりました。
Rの統計処理からSciPy等への移行
カイ二乗検定をする
検定は統計処理をする上で避けて通れません。ここではカイ二乗検定をしてみたいと思います。Wikipediaに載っていた例を用いたいと思います。
R
m <- matrix(c(90,60,104,95,30,50,51,20,30,40,45,35), nrow = 3, byrow = TRUE) colnames(m) <- c("A", "B", "C", "D") rownames(m) <- c("White collar", "Blue collar", "No collar") chisq.test(m, correct=FALSE)
Python
import numpy as np import pandas as pd from scipy import stats df = pd.DataFrame({'A':[90,30,30], 'B':[60,50,40], 'C':[104,51,45], 'D':[95,20,35]}, index=['White collar','Blue collar', 'No collar']) stats.chi2_contingency(df, correction=False)
*1:移行先は断定的に書いていますが、もちろん代替が存在することも理解しています。