Edited at
RubyDay 17

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

More than 3 years have passed since last update.


背景

先日、RubyKaigi 2015 に参加してきました

終了後、↓のような感想エントリを読んで、そうだよなーと思いました

RubyKaigi 2015(3日目) ただのにっき


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


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


Daru

先々日、SQL 脳から見た Ruby という発表をしたところ「タプルの集合っぽいのを弄るなら Daru という Gem あるから触ってみると良い」とアドバイスを頂きました

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次元のベクトルと、2次元のスプレッドシートげな構造を扱えますとありますね


1次元のベクトル


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

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


2次元のベクトル


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

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


おまけ

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 っぽい操作


絞り込み

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


グルーピング

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 で書きたい...


プロットとか色々...

こっからが本番... だけど、ひとまずここまで。続きはまたこんどということで

この辺のデモを試したかったのですが、plot を動かすための条件がまだ分かってません


感想


  • いくらなんでも生の2次元配列をゴリゴリ弄っていくのは辛いよなと思っていたので、こういうライブラリがあるのはありがたい

  • SQL でやったほうが柔軟性あるし楽なんじゃないの。と思うけど、あえてこういうのがある理由というのがあるはずなので、それを探りたい

  • Python の Pandas との違い(主に、なにがどれくらい足りないかなど)も分からないので Pandas と併せて今後も触ってみたいところ


参考資料