Why are you using Python?  Rによる自動集計ガイド

※タイトルで煽るのは良くないと思ったのでタイトルを変えました。

[まだExcelで消耗してるの？Pythonによる自動集計ガイド 基礎編 - Qiita](https://qiita.com/stakemura/items/08838d93d43ed4e47fc4)を読んでいて（Rのが絶対便利…！）というお気持ちが強まってきたので勢い余って書きました。


## はじめに

Pythonは[Colaboratory](https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja)で手軽に試せて非常に良いです。実は、RもColaboratoryから使うことができます。ColaboratoryにはRのカーネルが既に入っているのですが、表から見えないようになっているだけなのです。

そこで、見えるようにしたものを用意しました。

- [R Example - Colaboratory](https://colab.research.google.com/github/nozma/colab_kernel_examples/blob/master/R_Example.ipynb)

このノートブックを使えば、Rだってすぐ試せます（もうちょっと詳しい説明は[ColaboratoryでRやSwiftを使う - Qiita](https://qiita.com/nozma/items/cbfffb223ef8155d04bf)をどうぞ）。

試して下さい。今すぐ。

## 使用するパッケージ

主に`dplyr`を使います。

- [A Grammar of Data Manipulation • dplyr](https://dplyr.tidyverse.org/)

`dplyr`はpandasのように、いや、（たぶん）pandasよりずっと柔軟に、そして簡単にデータ処理が行えるRのパッケージです

`dplyr`はColaboratoryなら既に入っています。Colaboratory以外からRを使っている場合は、次の関数を実行してパッケージをインストールして下さい。

```r
install.packages("dplyr")
```

その後、パッケージを使うためにロードしておきましょう。

```r
library(dplyr)
```

## データの読み込み

Web上にある.xlsxファイルは、`rio`パッケージの`import()`関数を使えば直接読み込むことができます（ローカルにあるファイルなら`readxl`パッケージを使うと良いでしょう）。

`rio`はColaboratoryには入っていないのでインストールが必要です。

```r
install.packages("rio")
```

この関数は1回しか使わないので、今回はパッケージをロードせずに関数を使用してみます。

```{r}
df <- rio::import("https://pbpython.com/extras/excel-comp-data.xlsx")
```

`head()`でデータの先頭を確認してみます。

```{r}
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にこんなことはできるでしょうか？

``` python
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()`で追加します。

``` r
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`らしい書き方をするなら、パイプ演算子`%>%`を使いましょう。この演算子は、パイプの前の処理結果をパイプの後の関数の第一引数として渡すという仕事をします。つまり、上記の例は次のように記述できます。

``` r
df %>% 
  mutate(total = Jan + Feb + Mar) %>% 
  head()
```

このように行単位で途中の処理を記述することができるので、処理を追加したり、削除したりといった試行錯誤が非常にやりやすいという利点があります。

結果を再度使うため、変数に代入しておきます。

``` r
df2 <- df %>% 
  mutate(total = Jan + Feb + Mar)
```

ところで、Rの代入演算子は右辺への代入もできるので、次のような書き方もできます。右辺への代入は好みが分かれるところですが、パイプ演算子を使った試行錯誤の後に変数へ代入するような場合には便利です。処理の頭にカーソルを戻さなくていいので、書いていて気持ちいいという効果もあります。

``` r
df %>% 
  mutate(total = Jan + Feb + Mar) %>% 
  head() -> df2
```

また、もとの変数を更新するのであれば、`magrittr`パッケージの`%<>%`演算子を使ってより洗練された書き方をすることもできます。

``` r
library(magrittr)
df %<>% 
  mutate(total = Jan + Feb + Mar) %>% 
  head()
```

## 集計行を加える

列の集約値は、`summarise()`関数で得られます。

``` r
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`を繰返し書かなくて良くなるので、見た目がスッキリします。

``` r
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列をまとめて選択できます。

``` r
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`は文字列として指定しなければならない点に注意が必要です。）

``` python
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()`します。

``` r
df2 %>% 
  summarise_at(
    vars(Jan:Mar),
    ~sum(.)
  ) %>% unlist()
```

    ##     Jan     Feb     Mar 
    ## 1462000 1507000  717000

`Jan`から`total`までの合計を計算して、もとのデータフレームにつなげてみましょう。それには`bind_rows()`を使います。

``` r
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()`で縦持ちに変換できます。

``` r
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`を使ったデータ変換や集計といった操作は、非常にシンプルかつ直感的、可読性に優れた状態で記述可能となります。

``` r
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にこんなことができますか？ということが気になるわけです。

``` python
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](https://qiita.com/yamano357/items/9319d4b073d82a261ed8)）。どちらを使うべきかはもうお分かりですね？