Never Too Late

いつだってゼロからやり直そう

データ仕事をRからPythonに乗り換えるときのあれこれメモ

ずっとデータの仕事をRで行っていたのですが、思い立ってPythonに乗り換えることにしました。Pythonという言語自体には以前から定期的に使っていたこともあり全く抵抗はありませんが、以降に際して乗り越えなければならない大きなハードルが大きく分けて4つあります*1

  1. Rのdata.frame、dplyrで行っていた作業をpandasで出来るようにならなくてはならない
  2. ggplotで行っていた視覚化の作業をMatplotlibで出来るようにならなくてはならない
  3. RStudioではなくJupyter Notebookで作業を出来るようにならなくてはならない
  4. 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:移行先は断定的に書いていますが、もちろん代替が存在することも理解しています。