Rubyでデータ解析 - Daru入門

データ解析といえばPythonやRを使うことが多いが、Rubyでも簡単なデータ解析は行うことができる。

Rubyでデータフレーム（Excelのスプレッドシートのようなデータ構造）を扱うには、[Daru](https://github.com/v0dro/daru) というライブラリが利用できる。作者の@v0droさんはRubykaigi 2016でも登壇してDaruについて発表していて、公演の様子は[こちら](http://rubykaigi.org/2016/presentations/v0dro.html)で見ることができる。

この記事では、Daruの基本的な使い方を紹介する。
必要な機能を全部書くと量が多くなるので、最小限の利用例のみ書いていく。

# 環境

- Ruby2.3.1
- daru 0.1.4.1

Gemfileに `gem "daru"` と書いてbundleすればOK。

以後、`require "daru"` を行ってから実行する

# データ構造

dataframeは２次元配列のような構造で、`Daru::DataFrame` という名前のクラスになる。
dataframeに各行や列を表す１次元配列を表すクラスが `Daru::Vector` で、基本的にはこのVectorとDataFrameを使う。

# Daruの利用方法

## DataFrame作成

- 列のArrayから作る

```rb
Daru::DataFrame.new(
  "col0" => [1,2,3,4,5],
  "col1" => [0.1,0.2,0.3,0.4,0.5],
  "col2" => [11,22,33,44,55]
  )

=> #<Daru::DataFrame(5x3)>
      col0 col1 col2
    0    1  0.1   11
    1    2  0.2   22
    2    3  0.3   33
    3    4  0.4   44
    4    5  0.5   55
```

- 各行のArrayから作る
    - 列の名前は `:order`オプションで指定

```rb
df = Daru::DataFrame.rows(
  [
    [1, 0.1, 11],
    [2, 0.2, 22],
    [3, 0.3, 33],
    [4, 0.4, 44],
    [5, 0.5, 55]
  ],
  order: ["col0", "col1", "col2"]
)
=> #<Daru::DataFrame(5x3)>
      col0 col1 col2
    0    1  0.1   11
    1    2  0.2   22
    2    3  0.3   33
    3    4  0.4   44
    4    5  0.5   55
```

## 要素の参照

- 列を取得

```rb
df["col0"]
=> #<Daru::Vector(5)>
      col0
    0    1
    1    2
    2    3
    3    4
    4    5
```

- 複数列の取得

```rb
df["col0", "col2"]      # 複数の列名を指定
df["col1".."col2"]      # rangeで指定
```

- 行を取得したいとき

```rb
df.row[2]
=> #<Daru::Vector(3)>
         2
 col0    3
 col1  0.3
 col2   33
```

## 要素の設定

- 列の設定

```rb
df["col3"] = [5,4,3,2,1]
df
=> #<Daru::DataFrame(5x4)>
      col0 col1 col2 col3
    0    1  0.1   11    5
    1    2  0.2   22    4
    2    3  0.3   33    3
    3    4  0.4   44    2
    4    5  0.5   55    1
```

- 行の設定

```rb
df.row[5] = [6, 0.6, 66, 0]
df
=> #<Daru::DataFrame(6x4)>
      col0 col1 col2 col3
    0    1  0.1   11    5
    1    2  0.2   22    4
    2    3  0.3   33    3
    3    4  0.4   44    2
    4    5  0.5   55    1
    5    6  0.6   66    0
```

## 統計量の表示

- とりあえず`describe`すれば代表的な統計量が得られる

```rb
df.describe
=> #<Daru::DataFrame(5x4)>
                  col0       col1       col2       col3
      count          6          6          6          6
       mean        3.5 0.35000000       38.5        2.5
        std 1.87082869 0.18708286 20.5791156 1.87082869
        min          1        0.1         11          0
        max          6        0.6         66          5
```

- 平均

```rb
df.mean
=> #<Daru::Vector(4)>
                                    mean
                col0                 3.5
                col1 0.35000000000000003
                col2                38.5
                col3                 2.5
```

- 相関係数

```rb
df.corr
=> #<Daru::DataFrame(4x4)>
                  col0       col1       col2       col3
       col0        1.0        1.0        1.0       -1.0
       col1        1.0        1.0 1.00000000       -1.0
       col2        1.0 1.00000000 1.00000000       -1.0
       col3       -1.0       -1.0       -1.0        1.0
```

- 共分散

```rb
df.cov
=> #<Daru::DataFrame(4x4)>
                  col0       col1       col2       col3
       col0        3.5       0.35       38.5       -3.5
       col1       0.35 0.03499999       3.85      -0.35
       col2       38.5       3.85      423.5      -38.5
       col3       -3.5      -0.35      -38.5        3.5
```

## イテレーション

- 列に対してiteration

```rb
df.each {|col| puts col[0] + col[3] }
# 5
# 0.5
# 55
# 7
```

- 行に対してiteration

```rb
df.each(:row) {|row| p row["col1"] }
# 0.1
# 0.2
# 0.3
# 0.4
# 0.5
# 0.6
```

- 同様に `map`, `map(:row)` も定義されている。返り値はarray。

## フィルタリング

- 列に対してfiltering

```rb
df.filter {|col| col.mean > 10 }
=> #<Daru::DataFrame(6x1)>
      col2
    0   11
    1   22
    2   33
    3   44
    4   55
    5   66
```

- 行に対してfiltering

```rb
df.filter(:row) {|row| row["col0"]*row["col3"] < 8 }
=> #<Daru::DataFrame(3x4)>
      col0 col1 col2 col3
    0    1  0.1   11    5
    4    5  0.5   55    1
    5    6  0.6   66    0
```

## ソート

- 行をソート。各列に対してascending, descendingを指定する。引数は列名の配列

```rb
df.sort(["col3", "col1"], ascending: [false, true])
=> #<Daru::DataFrame(6x4)>
      col0 col1 col2 col3
    0    1  0.1   11    5
    1    2  0.2   22    4
    2    3  0.3   33    3
    3    4  0.4   44    2
    4    5  0.5   55    1
    5    6  0.6   66    0
```

## 他の機能

ここでは紹介しないが以下の様な機能がある。詳細はREADMEを参照のこと

- pivot table
- csvやexcelからのimport
- 時系列データ用のメソッド
- 他の統計用のgem statsample,stasample-glm と連携してより高度な統計処理
- グラフプロットライブラリ Nyaplot を利用したプロット
- categorical dataの扱い

# reference

- github repository
    - [v0dro/daru](https://github.com/v0dro/daru)
    - メソッドの詳細はこちらのREADMEからリンクされているサンプル集を見ると良い
- Rubykaigi2016での講演
    - http://rubykaigi.org/2016/presentations/v0dro.html
- 上記のコードをjupyterで実行した結果
    - https://gist.github.com/yohm/4f521aedc9b0f185fd5a38572fb5b4e3