Ruby で数値を弄るやーつ -- Daru について --

# 背景

先日、RubyKaigi 2015 に参加してきました
終了後、↓のような感想エントリを読んで、そうだよなーと思いました

[RubyKaigi 2015(3日目) ただのにっき](http://sho.tdiary.net/20151213.html)

> ところで今回のRubyKaigiで「あ、これはまずいな」と思ったことに「機械学習系の発表がひとつもなかった」点がある。昨日のパーティでも話題にあげてみたところ、危機感を抱いている人は少なからずいた印象だけど、根っこをたどると数値演算ライブラリの整備をずーっと放置してきたことがあるだろう。気がつくと数値演算方面ではPythonに大きく水をあけられていて、いまやその応用である機械学習では(LLの中では)Pythonの独壇場だ。Webアプリケーションの分野で一世を風靡した気になってる間に、いま一番ホットな領域がまったく話題にならない言語になってしまった。

というわけで、Ruby でデータセットを弄る Gem ってのが無いわけではないので、ひとまず触ってみて、アドベントカレンダーでシェアしたら良いかもなーと思ってこのエントリを書いています

# Daru

先々日、[SQL 脳から見た Ruby](http://www.slideshare.net/yancyajp/rubyseenfromsqlishbrain-54870167) という発表をしたところ「タプルの集合っぽいのを弄るなら Daru という Gem あるから触ってみると良い」と[アドバイス](https://twitter.com/chezou/status/663515032781611008)を頂きました

https://github.com/v0dro/daru

> daru (Data Analysis in RUby) is a library for storage, analysis, manipulation and visualization of data.
> daru is inspired by pandas, a very mature solution in Python.

Daru はデータを解析したり、弄ったり、可視化したりする、Python の Pandas インスパイアの Gem だそうです

## インストール

`gem i daru` ってするだけでインストールは出来るんですが、

```
*************************************************************************
Thank you for installing daru!

  oOOOOOo 
 ,|    oO
//|     |
\|     |
 `|     |
  `-----`


Hope you love daru! For enhanced interactivity and better visualizations, 
consider using gnuplotrb and nyaplot with iruby. For statistics use the 
statsample family.

Read the README for interesting use cases and examples.

Cheers!
*************************************************************************
```

とか出てきて、ほっこりしますね

# つかいかた

## 扱えるデータ

> Data structures:
> Vector - A basic 1-D vector.
> DataFrame - A 2-D spreadsheet-like structure for manipulating and storing data sets. This is daru's primary data structure. 

１次元のベクトルと、２次元のスプレッドシートげな構造を扱えますとありますね

### １次元のベクトル

- 1 から 10 まで、みたいなデータ

```rb
require 'daru'

vec = Daru::Vector.new([*1..10].map { |x| x * 99 }, index: [*1..10], name: 'ninetynine')

#=> #<Daru::Vector:70313282595960 @name = one to ten @size = 10 >
#=>            ninetynine
#=>          1         99
#=>          2        198
#=>          3        297
#=>          4        396
#=>          5        495
#=>          6        594
#=>          7        693
#=>          8        792
#=>          9        891
#=>         10        990

vec[1]
#=> 99

vec[3]
#=> 297

vec[1..3] # range を渡すと Vector の index じゃなくて添字扱いなの...
#=> #<Daru::Vector:70313320646980 @name = ninetynine @size = 3 >
#=>            ninetynine
#=>          2        198
#=>          3        297
#=>          4        396
```

### ２次元のベクトル

- A から Z までの文字と、番号、みたいな

```rb
df = Daru::DataFrame.new(
  {
    char: [*'A'..'Z'],
    num: [*1..26]
  }, 
  index: [*'a'..'z'],
  order: [:num, :char]
)
#=> #<Daru::DataFrame:70313283713820 @name = afe2e697-c235-469a-9b8d-b3189dd438b8 @size = 26>
#=>                  char        num 
#=>          0          A          1 
#=>          1          B          2 
#=>          2          C          3 
#=>          3          D          4 
#=>          4          E          5 
#=>          5          F          6 
#=>          6          G          7 
#=>          7          H          8 
#=>          8          I          9 
#=>          9          J         10 
#=>         10          K         11 
#=>         11          L         12 
#=>         12          M         13 
#=>         13          N         14 
#=>         14          O         15 
#=>        ...        ...        ...

df[:char][3..6]
#=> #<Daru::Vector:70313282764340 @name = char @size = 4 >
#=>      char
#=>    d    D
#=>    e    E
#=>    f    F
#=>    g    G

df.row['a']
#=> #<Daru::Vector:70313321157180 @name = a @size = 2 >
#=>         a
#=>  num    1
#=> char    A

df.row['a'..'c']
#=> #<Daru::DataFrame:70313321055320 @name = 81e720bf-41a7-4077-8ddc-ec2f7b38fb12 @size = 3>
#=>                   num       char 
#=>          a          1          A 
#=>          b          2          B 
#=>          c          3          C 
```

### おまけ

```rb
df.to_s
#=> "<table><tr><th colspan=\"3\">Daru::DataFrame:70313279396300  rows: 26  cols: 2<tr><th></th><th>num</th><th>char</th></tr><tr><td>a</td><td>1</td><td>A</td></tr><tr><td>b</td><td>2</td><td>B</td></tr><tr><td>c</td><td>3</td><td>C</td></tr><tr><td>d</td><td>4</td><td>D</td></tr><tr><td>e</td><td>5</td><td>E</td></tr><tr><td>f</td><td>6</td><td>F</td></tr><tr><td>g</td><td>7</td><td>G</td></tr><tr><td>h</td><td>8</td><td>H</td></tr><tr><td>i</td><td>9</td><td>I</td></tr><tr><td>j</td><td>10</td><td>J</td></tr><tr><td>k</td><td>11</td><td>K</td></tr><tr><td>l</td><td>12</td><td>L</td></tr><tr><td>m</td><td>13</td><td>M</td></tr><tr><td>n</td><td>14</td><td>N</td></tr><tr><td>o</td><td>15</td><td>O</td></tr><tr><td>p</td><td>16</td><td>P</td></tr><tr><td>q</td><td>17</td><td>Q</td></tr><tr><td>r</td><td>18</td><td>R</td></tr><tr><td>s</td><td>19</td><td>S</td></tr><tr><td>t</td><td>20</td><td>T</td></tr><tr><td>u</td><td>21</td><td>U</td></tr><tr><td>v</td><td>22</td><td>V</td></tr><tr><td>w</td><td>23</td><td>W</td></tr><tr><td>x</td><td>24</td><td>X</td></tr><tr><td>y</td><td>25</td><td>Y</td></tr><tr><td>z</td><td>26</td><td>Z</td></tr></table>"
```

なぜ to_s すると HTML 出てくるの... 何それ怖い

## SQL っぽい操作

### 絞り込み

```rb
df.where((df[:num].eq(5) | df[:num].eq(6) | df[:num].eq(7)) & df[:char].eq('G'))
#=> #<Daru::DataFrame:70324863177360 @name = 49f9f681-c475-4e37-aab3-613e0d8050f9 @size = 1>
#=>                  num       char 
#=>         g          7          G 
```

### グルーピング

```rb
df = Daru::DataFrame.new(
  age:  [19, 18, 22, 45, 36, 60],
  from: %w[東京都 埼玉県 東京都 東京都 神奈川県 千葉県],
  name: %w[太郎 花子 二郎 フランソワーズ としあき たもつ]
)

#=> #<Daru::DataFrame:70324885066340 @name = 6b7e37b3-ba86-4bf2-8b0e-d4052addd62b @size = 6>
#=>                   age       from       name 
#=>          0         19        東京都         太郎 
#=>          1         18        埼玉県         花子 
#=>          2         22        東京都         二郎 
#=>          3         45        東京都    フランソワーズ 
#=>          4         36       神奈川県       としあき 
#=>          5         60        千葉県        たもつ 

df.group_by(:from)
#=> #<Daru::Core::GroupBy:0x007feb92952dd0
#=>  @context=
#=>   
#=> #<Daru::DataFrame:70324885066340 @name = 6b7e37b3-ba86-4bf2-8b0e-d4052addd62b @size = 6>
#=>                   age       from       name 
#=>          0         19        東京都         太郎 
#=>          1         18        埼玉県         花子 
#=>          2         22        東京都         二郎 
#=>          3         45        東京都    フランソワーズ 
#=>          4         36       神奈川県       としあき 
#=>          5         60        千葉県        たもつ 
#=> ,
#=>  @groups={["千葉県"]=>[5], ["埼玉県"]=>[1], ["東京都"]=>[0, 2, 3], ["神奈川県"]=>[4]},
#=>  @non_group_vectors=[:age, :name]>
```

SQL で書きたい...



## プロットとか色々...

こっからが本番... だけど、ひとまずここまで。続きはまたこんどということで
[この辺のデモ](http://nbviewer.ipython.org/github/SciRuby/sciruby-notebooks/blob/master/Visualization/Visualizing%20data%20with%20daru%20DataFrame.ipynb)を試したかったのですが、plot を動かすための条件がまだ分かってません

# 感想

- いくらなんでも生の２次元配列をゴリゴリ弄っていくのは辛いよなと思っていたので、こういうライブラリがあるのはありがたい
- SQL でやったほうが柔軟性あるし楽なんじゃないの。と思うけど、あえてこういうのがある理由というのがあるはずなので、それを探りたい
- Python の Pandas との違い（主に、なにがどれくらい足りないかなど）も分からないので Pandas と併せて今後も触ってみたいところ

# 参考資料

- [Railsエンジニアに役立つJupyter NotebookとiRuby](http://techlife.cookpad.com/entry/201510-jupyter-and-rails) 
- [現代のエンジニアのための強力なメモ帳 Jupyter notebookのすゝめ](http://techlife.cookpad.com/entry/write-once-share-anywhare)