※タイトルで煽るのは良くないと思ったのでタイトルを変えました。
まだExcelで消耗してるの?Pythonによる自動集計ガイド 基礎編 - Qiitaを読んでいて(Rのが絶対便利…!)というお気持ちが強まってきたので勢い余って書きました。
はじめに
PythonはColaboratoryで手軽に試せて非常に良いです。実は、RもColaboratoryから使うことができます。ColaboratoryにはRのカーネルが既に入っているのですが、表から見えないようになっているだけなのです。
そこで、見えるようにしたものを用意しました。
このノートブックを使えば、Rだってすぐ試せます(もうちょっと詳しい説明はColaboratoryでRやSwiftを使う - Qiitaをどうぞ)。
試して下さい。今すぐ。
使用するパッケージ
主にdplyr
を使います。
dplyr
はpandasのように、いや、(たぶん)pandasよりずっと柔軟に、そして簡単にデータ処理が行えるRのパッケージです
dplyr
はColaboratoryなら既に入っています。Colaboratory以外からRを使っている場合は、次の関数を実行してパッケージをインストールして下さい。
install.packages("dplyr")
その後、パッケージを使うためにロードしておきましょう。
library(dplyr)
データの読み込み
Web上にある.xlsxファイルは、rio
パッケージのimport()
関数を使えば直接読み込むことができます(ローカルにあるファイルならreadxl
パッケージを使うと良いでしょう)。
rio
はColaboratoryには入っていないのでインストールが必要です。
install.packages("rio")
この関数は1回しか使わないので、今回はパッケージをロードせずに関数を使用してみます。
df <- rio::import("https://pbpython.com/extras/excel-comp-data.xlsx")
head()
でデータの先頭を確認してみます。
head(df)
## (一部略...)
## postal-code Jan Feb Mar
## 1 28752 10000 62000 35000
## 2 38365 95000 45000 35000
## 3 76517 91000 120000 35000
## 4 46021 45000 120000 10000
## 5 49681 162000 120000 35000
## 6 62785 150000 120000 35000
手元にデータを落とさずにデータの読み込みができました。
さて、pandasにこんなことはできるでしょうか?
import pandas as pd
df = pd.read_excel("https://pbpython.com/extras/excel-comp-data.xlsx")
print(df.head())
## account name ... Feb Mar
## 0 211829 Kerluke, Koepp and Hilpert ... 62000 35000
## 1 320563 Walter-Trantow ... 45000 35000
## 2 648336 Bashirian, Kunde and Price ... 120000 35000
## 3 109996 D'Amore, Gleichner and Bode ... 120000 10000
## 4 121213 Bauch-Goldner ... 120000 35000
##
## [5 rows x 9 columns]
できましたね!!!余分なパッケージも不要だしpandas便利ですね!!!!!
集計列の追加
気を取り直してdplyr
パッケージを使い、JanからMarまでの合計列を追加してみます。
新しい列はmutate()
で追加します。
head(mutate(df, total = Jan + Feb + Mar))
## (一部略...)
## postal-code Jan Feb Mar total
## 1 28752 10000 62000 35000 107000
## 2 38365 95000 45000 35000 175000
## 3 76517 91000 120000 35000 246000
## 4 46021 45000 120000 10000 175000
## 5 49681 162000 120000 35000 317000
## 6 62785 150000 120000 35000 305000
よりdplyr
らしい書き方をするなら、パイプ演算子%>%
を使いましょう。この演算子は、パイプの前の処理結果をパイプの後の関数の第一引数として渡すという仕事をします。つまり、上記の例は次のように記述できます。
df %>%
mutate(total = Jan + Feb + Mar) %>%
head()
このように行単位で途中の処理を記述することができるので、処理を追加したり、削除したりといった試行錯誤が非常にやりやすいという利点があります。
結果を再度使うため、変数に代入しておきます。
df2 <- df %>%
mutate(total = Jan + Feb + Mar)
ところで、Rの代入演算子は右辺への代入もできるので、次のような書き方もできます。右辺への代入は好みが分かれるところですが、パイプ演算子を使った試行錯誤の後に変数へ代入するような場合には便利です。処理の頭にカーソルを戻さなくていいので、書いていて気持ちいいという効果もあります。
df %>%
mutate(total = Jan + Feb + Mar) %>%
head() -> df2
また、もとの変数を更新するのであれば、magrittr
パッケージの%<>%
演算子を使ってより洗練された書き方をすることもできます。
library(magrittr)
df %<>%
mutate(total = Jan + Feb + Mar) %>%
head()
集計行を加える
列の集約値は、summarise()
関数で得られます。
df2 %>%
summarise(
sum = sum(Jan),
mean = mean(Jan),
min = min(Jan),
max = max(Jan),
)
## sum mean min max
## 1 1462000 97466.67 10000 162000
summarise_at
を使えばより洗練された書き方をすることもできます。この例ではJan
を繰返し書かなくて良くなるので、見た目がスッキリします。
df2 %>%
summarise_at(
vars(Jan),
list(~sum, ~mean, ~min, ~max)
)
## sum mean min max
## 1 1462000 97466.67 10000 162000
複数列に複数の集計を適用するなんてことも簡単です。vars()
は複数列を簡単に選択するための色々な方法を備えていますが、例えばJan:Mar
のようにするとJan
からMar
までの3列をまとめて選択できます。
df2 %>%
summarise_at(
vars(Jan:Mar),
list(~sum, ~mean)
)
## Jan_sum Feb_sum Mar_sum Jan_mean Feb_mean Mar_mean
## 1 1462000 1507000 717000 97466.67 100466.7 47800
ちなみにJan:Mar
みたいな選択はpandasでもできます。pandas便利ですね!(複数の関数は.agg
で適用できますが、組み込み関数にないmean
は文字列として指定しなければならない点に注意が必要です。)
print(df.loc[:, "Jan":"Mar"].agg([sum, 'mean']))
## Jan Feb Mar
## sum 1.462000e+06 1.507000e+06 717000.0
## mean 9.746667e+04 1.004667e+05 47800.0
再度気を取り直してRに戻ります。もし名前付きベクトルとして結果がほしければ、unlist()
します。
df2 %>%
summarise_at(
vars(Jan:Mar),
~sum(.)
) %>% unlist()
## Jan Feb Mar
## 1462000 1507000 717000
Jan
からtotal
までの合計を計算して、もとのデータフレームにつなげてみましょう。それにはbind_rows()
を使います。
df2 %>%
summarise_at(
vars(Jan:total),
~sum(.)
) -> df_total
df2 %>%
bind_rows(df_total) %>%
tail
## (一部略...)
## city state postal-code Jan Feb Mar total
## 11 Rosaberg Tenessee 47743 45000 120000 55000 220000
## 12 Norbertomouth NorthDakota 31415 150000 10000 162000 322000
## 13 East Davian Iowa 72686 162000 120000 35000 317000
## 14 Goodwinmouth RhodeIsland 31919 55000 120000 35000 210000
## 15 Kathryneborough Delaware 27933 150000 120000 70000 340000
## 16 <NA> <NA> NA 1462000 1507000 717000 3686000
いかがでしたか?
ここで終わろうかと思いましたが、もうちょっとだけ続きます。
データをイケてる感じにする
ところで今回例として扱ったデータの形式はイマイチです。
よく見ると「月」が列名になってしまっています。月が増えたら横に伸びるのでしょうか?このような何らかの属性が横に展開されてしまっているデータ形式は横持ちと呼ばれます。
データを横持ちにしておくと、表がコンパクトにまとまるのでなんとなく「整理してやったぞ」感が出ます。また、紙にデータを記入する場合は大抵横持ち形式で記入すると思いますので、そのままExcelに入力したら横持ちデータの完成です。まあ何やかんやで世の中には横持ちのデータが沢山あるわけです。
しかし、横持ちのデータは一般的に機械的な処理に向きません。で、どうするかというとデータを縦持ちにします。縦持ちのデータは整然データ、あるいはtidy dataなどとも呼ばれます。
縦持ちのデータはどういうものかというと、ざっくりいうと「同じ属性のデータは同じ列に入れる」というルールに則って整理されたデータです。
例えば先程の例であれば、列名であったJan
, Feb
, Mar
を変数としてmonth
という列に入れて、数値はvalue
という列に入れると縦持ちになります。その名の通り縦に長くなるわけです。
Rでは、tidyの名を関するtidyr
パッケージを使うとこの種のデータ変換を容易に行えます。横持ちデータはgather()
で縦持ちに変換できます。
library(tidyr)
df3 <- df %>%
gather(key = month, value = value, Jan:Mar)
df3 %>% head(10)
## (一部略...)
## postal-code month value
## 1 28752 Jan 10000
## 2 38365 Jan 95000
## 3 76517 Jan 91000
## 4 46021 Jan 45000
## 5 49681 Jan 162000
## 6 62785 Jan 150000
## 7 18008 Jan 62000
## 8 53461 Jan 145000
## 9 64415 Jan 70000
## 10 46308 Jan 70000
一旦データを縦持ちにしてしまえば、dplyr
を使ったデータ変換や集計といった操作は、非常にシンプルかつ直感的、可読性に優れた状態で記述可能となります。
df3 %>%
group_by(month) %>%
summarise(sum_value = sum(value))
## # A tibble: 3 x 2
## month sum_value
## <chr> <dbl>
## 1 Feb 1507000
## 2 Jan 1462000
## 3 Mar 717000
で、pandasにこんなことができますか?ということが気になるわけです。
print(df.melt(value_vars=['Feb', 'Mar', 'Jan']).head(10))
## variable value
## 0 Feb 62000
## 1 Feb 45000
## 2 Feb 120000
## 3 Feb 120000
## 4 Feb 120000
## 5 Feb 120000
## 6 Feb 120000
## 7 Feb 95000
## 8 Feb 95000
## 9 Feb 120000
できますね!!!!!!!!!!
ただ、value_vars
を指定するときに'Feb':'Jan'
みたいな書き方はできないようでした。この点はRに軍配が上がりそうです。
まとめ
今回紹介したような基本的な集計なら、Rを使ってもPythonを使っても大きな差はないので、どちらでも好きな方でやれば良いと思います。
しかしRは寛容ですから、RからPythonを使うこともできます(cf. reticulateパッケージでRからPythonを使う - Qiita)。どちらを使うべきかはもうお分かりですね?