Daruの基本操作


この記事ではRubyのデータフレームであるDaruを紹介します。
(編集リクエストは大歓迎します）

## Daruとは
Pythonにおけるpandas。Daruは[インドのお酒](https://en.wikipedia.org/wiki/Desi_daru)の意味だそうです。

　　![image.png](https://qiita-image-store.s3.amazonaws.com/0/144608/dacb834a-66f4-23a6-d337-90f474520758.png)　　　![image.png](https://qiita-image-store.s3.amazonaws.com/0/144608/24ac00b4-bc2e-8062-277a-08b887db1d3b.png)

### 2023年6月時点の開発状況について

幸いにしてRubyのデータフレームには新しい作品がいくつもありますが、Daru自体の開発は事実上休止しています。Daruはあくまで歴史的な文化財として、実際にデータを処理する場合は、下記のいずれかのデータフレームの利用をおすすめします。

https://github.com/ankane/rover

↑Pure Ruby製。この中では最も安定していると思われる。

https://github.com/ankane/polars-ruby

↑今後Pythonなどでデファクトになる可能性が高いPolarsのRuby版。

https://github.com/red-data-tools/red_amber

↑RedArrowを用いたubyとの親和性の高いデータフレーム。

### 特徴
column(Series)をvectorと呼ぶところ。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/144608/f43a9f3e-ba70-d881-b388-5b9bea83e387.png)
青い四角で強調されている部分がVectorです。


### 関連プロジェクト

* [Daru::View](https://github.com/SciRuby/daru-view)
    * Google Charts(GoogleVisualr), Highcharts(lazy_high_charts) による可視化ライブラリ。
* [Daru-IO](https://github.com/SciRuby/daru-io)
    * 標準でサポートされていないデータ形式等の読み込み。完成度はそんなに高くないかも。
* [rubyplot](https://github.com/SciRuby/rubyplot)
    * Daruに搭載されるかもしれないプロッティングライブラリ

## クラス
Daru::DataFrame と Daru::Vector を覚えておけばだいたい事足ります。

| クラス名               | 説明                 |
|------------------------|----------------------|
| Daru::DataFrame        | データフレーム       |
| Daru::Vector           | シリーズ（列）       |
| Daru::Index            | インデックス（軸）   |
| Daru::DateTimeIndex    | 時系列のインデックス |
| Daru::MultiIndex       | 階層型インデックス   |
|                        |                      |
| Daru::Core::GroupBy    |                      |
| Daru::Core::MergeFrame |                      |

## インストール
RubyGemに登録されているDaruはやや古いのでGithubのmasterからインストールする方がいいでしょう。

```bash
gem install specific_install
gem specific_install https://github.com/SciRuby/daru
```

```ruby
require 'daru'
```

Jupyter Notebook + IRuby環境が推奨される。

## データを読み込む

#### CSVファイル

```ruby
Daru::DataFrame.from_csv "filepath"
```

インターネット上のCSVファイル

```ruby
require 'open-uri'
url = 'https://raw.githubusercontent.com/pandas-dev/pandas/v1.0.5/pandas/tests/io/data/csv/iris.csv'
df = Daru::DataFrame.from_csv(url)
```

一般的なもの

```ruby
Daru::DataFrame.new a: [1,2,3,4,5], b: [2,3,4,5,6]
Daru::DataFrame.rows 配列
# 数値がStringになる場合あり、何らかの改善の余地
```

RのデータセットでDaruをためすとき。

```ruby
require 'rdatasets'
df = RDatasets.load :datasets, :iris
```
そのほか
`from_activerecord` `from_excel` `from_html` `from_plaintext` `from_sql` など

Daru-ioを見ると色々ある。（完成度は高くない印象）

* Importers
    * `ActiveRecord`, `Avro`, `CSV`, `Excel`, `Excelx`, `HTML`, `JSON`, `Mongo`, `Plaintext`, `RData`, `RDS`, `Redis`, `SQL`
* Exporters
    * `Avro`, `CSV`, `Excel`, `JSON`, `RData`, `RDS`, `SQL`

## データの状態を確認する

先頭の5行を表示する

```ruby
df.head 5
```

   | SepalLength | SepalWidth | PetalLength | PetalWidth | Name       
---| ----------- | ---------- | ----------- | ---------- | -----------
0  | 5.1         | 3.5        | 1.4         | 0.2        | Iris-setosa
1  | 4.9         | 3.0        | 1.4         | 0.2        | Iris-setosa
2  | 4.7         | 3.2        | 1.3         | 0.2        | Iris-setosa
3  | 4.6         | 3.1        | 1.5         | 0.2        | Iris-setosa
4  | 5.0         | 3.6        | 1.4         | 0.2        | Iris-setosa


最後尾の5行を表示する

```ruby
df.tail 5
```

    | SepalLength | SepalWidth | PetalLength | PetalWidth | Name          
--- | ----------- | ---------- | ----------- | ---------- | --------------
145 | 6.7         | 3.0        | 5.2         | 2.3        | Iris-virginica[150, 5]
146 | 6.3         | 2.5        | 5.0         | 1.9        | Iris-virginica
147 | 6.5         | 3.0        | 5.2         | 2.0        | Iris-virginica
148 | 6.2         | 3.4        | 5.4         | 2.3        | Iris-virginica
149 | 5.9         | 3.0        | 5.1         | 1.8        | Iris-virginica

行数・列数を確認する

```ruby
df.shape # 形状 [150, 5]
df.ncols # 列数 5
df.nrows # 行数 150
df.size  # 行数 150
```

列名の取得

```ruby
df.vectors
#<Daru::Index(5): {SepalLength, SepalWidth, PetalLength, PetalWidth, Name}>
```

返却されるのはIndexクラスのオブジェクト。Vectorや配列ではない。


## データを選択する
以下、特に断りがない場合は `.head(5)` している場合があります。

### Ⅲ 列 Ⅲ

`[]` でVectorの名前を指定する

`df['vector1', 'vector2']`
`df.vector1`

```ruby
df['SepalLength']
df.SepalLength
```

   | SepalLength
---| -----------
0  | 5.1        
1  | 4.9        
2  | 4.7        
3  | 4.6        
4  | 5.0        

`at` 列数で指定

```ruby
df.at 0, 1
```

    | SepalLength | SepalWidth
--- | ----------- | ----------
0   | 5.1         | 3.5       
1   | 4.9         | 3.0       
2   | 4.7         | 3.2       
3   | 4.6         | 3.1       
4   | 5.0         | 3.6       


`[列数]` は返却されたデータフレームのベクトルの名前も数になる

```ruby
df[0, 1]
```

   | 0   | 1  
---| --- | ---
0  | 5.1 | 3.5
1  | 4.9 | 3.0
2  | 4.7 | 3.2
3  | 4.6 | 3.1
4  | 5.0 | 3.6

### ≡ 行 ≡

`row[]` でindexを指定（行数ではないので注意）

```ruby
df.row[100] # df.row 100 はエラー df.row [100] もエラーになる
```

            | 100           
----------- | --------------
SepalLength | 6.3           
SepalWidth  | 3.3           
PetalLength | 6.0           
PetalWidth  | 2.5           
Name        | Iris-virginica

範囲

```ruby
df.row[100..105]
```

    | SepalLength | SepalWidth | PetalLength | PetalWidth | Name          
--- | ----------- | ---------- | ----------- | ---------- | --------------
100 | 6.3         | 3.3        | 6.0         | 2.5        | Iris-virginica
101 | 5.8         | 2.7        | 5.1         | 1.9        | Iris-virginica
102 | 7.1         | 3.0        | 5.9         | 2.1        | Iris-virginica
103 | 6.3         | 2.9        | 5.6         | 1.8        | Iris-virginica
104 | 6.5         | 3.0        | 5.8         | 2.2        | Iris-virginica
105 | 7.6         | 3.0        | 6.6         | 2.1        | Iris-virginica

複数

```ruby
df.row[40,90,140]
# indexは数値とは限らない
# df.row[:a]
```

    | SepalLength | SepalWidth | PetalLength | PetalWidth | Name           
--- | ----------- | ---------- | ----------- | ---------- | ---------------
40  | 5.0         | 3.5        | 1.3         | 0.3        | Iris-setosa    
90  | 5.5         | 2.6        | 4.4         | 1.2        | Iris-versicolor
140 | 6.7         | 3.1        | 5.6         | 2.4        | Iris-virginica 

`row_at` で行数を指定

```ruby
df.row_at 100          # Vector が返される
df.row_at 100..105     # DataFrame
df.row_at 100, 102     # DataFrame
```

    | SepalLength | SepalWidth | PetalLength | PetalWidth | Name          
--- | ----------- | ---------- | ----------- | ---------- | --------------
100 | 6.3         | 3.3        | 6.0         | 2.5        | Iris-virginica
102 | 7.1         | 3.0        | 5.9         | 2.1        | Iris-virginica

`df.row[100]` 等として、取り出すことは可能だが、取り出したデータフレームのベクトルの名前も100になる。また、1行だけ取り出す場合は、データフレームではなくベクトルが返却される。

```ruby
df = Daru::DataFrame.new({a:[1,2,3], b:[4,5,6]}, index: [:a,:b,:c])
```

  | a | b
---|---|---
a | 1 | 4
b | 2 | 5
c | 3 | 6

```ruby
df.row[:b]
```

Daru::Vector(2)

  | b
---|---
a | 2
b | 5

```ruby
df.row[1]
```

Daru::Vector(2)

  | 1
---|---
a | 2
b | 5

```ruby
df.row_at 1
```

Daru::Vector(2)

| | |
---|---
a | 2
b | 5

### 列と行を同時に選択する
ベクトルを先に指定する

```ruby
df['SepalLength'][3]       # 4.6
```

### where で抽出
真偽値インデックス

```ruby
df.where(df.col1.eq "hoge")
df.where(df.col1 > 10 )
df.where(df.col1 > 10 | df.col2 < 10)
df.where((df.col1 > 10 | df.col2 < 10 ) & df.col3.eq 10)

# and or
df.where(((df.col1 > 10).or df.col2 < 10).and df.col3.eq 10)
```
`eq` `not_eq`
`mt` `lt` `mteq` `lteq`
`gt` 

`&` `|` `&&` `||` `and` `or` など

この領域はDaruは改善の余地がありそうです
Numo::NArray など他のライブラリとの統一感があまりない
データフレーム全体を真偽値でマスクする方法はないかもしれない

```ruby
df.where(df.SepalLength > 7.5)
```

    | SepalLength | SepalWidth | PetalLength | PetalWidth | Name          
--- | ----------- | ---------- | ----------- | ---------- | --------------
105 | 7.6         | 3.0        | 6.6         | 2.1        | Iris-virginica
117 | 7.7         | 3.8        | 6.7         | 2.2        | Iris-virginica
118 | 7.7         | 2.6        | 6.9         | 2.3        | Iris-virginica
122 | 7.7         | 2.8        | 6.7         | 2.0        | Iris-virginica
131 | 7.9         | 3.8        | 6.4         | 2.0        | Iris-virginica
135 | 7.7         | 3.0        | 6.1         | 2.3        | Iris-virginica


### filter で抽出

whereよりも記述が長くなるのですが、filterの方が書いていてRubyっぽくて楽だなあと感じることがあります。

```ruby
df2 = df.filter do |vector|
  vector.type == :numeric and vector.median < 50
end

df2 = df.filter(:row) do |row|
  row[:a] + row[:d] < 100
end
```

```ruby
mdt df.filter(:row){|row| row["SepalLength"] < 4.6}
```

   | SepalLength | SepalWidth | PetalLength | PetalWidth | Name       
---| ----------- | ---------- | ----------- | ---------- | -----------
8  | 4.4         | 2.9        | 1.4         | 0.2        | Iris-setosa
13 | 4.3         | 3.0        | 1.1         | 0.1        | Iris-setosa
38 | 4.4         | 3.0        | 1.3         | 0.2        | Iris-setosa
41 | 4.5         | 2.3        | 1.3         | 0.3        | Iris-setosa
42 | 4.4         | 3.2        | 1.3         | 0.2        | Iris-setosa

### keep_row_if　　keep_vector_if

破壊的メソッド

```ruby
df.keep_row_if do |row|
  row[:a] > 5
end
# return [index, index, ...]
```

## 要素をカウントする

`vector.value_counts`

```ruby
df.Name.value_counts
```

|||
|--------------- | ---|
|Iris-setosa     | 50 |
|Iris-versicolor | 50 |
|Iris-virginica  | 50 |


Uniq
`vector.uniq`

```ruby
df.Name.uniq
```

    | Name           
--- | ---------------
0   | Iris-setosa    
50  | Iris-versicolor
100 | Iris-virginica 

## 欠損値を扱う

```ruby
df.has_missing_data?
# nil, Float::NAN に反応する. 文字列 "NA" などには反応しない。
# df.reject_values "NA"
# 下記を使うためには df.replace_values "NA", nil などとする

df.include_values? nil
vector.count_values nil
df.filter_rows{|r| r.include? nil, Float::NAN}
df.dup_only_valid
df.clone_only_valid # 'shallow' copy
df.collect{|v| v.count_values nil}

df.replace_values nil, Float::NAN
```

例えば次のようにして、欠損値があるベクトルと、その個数の一覧を表示できる。

```ruby
require 'rdatasets'
df = RDatasets.Stat2Data.Hawks
df.replace_values "NA", nil
df.collect{|v| v.count_values nil}
```

|||
------------ | ---
Month        | 0  
Day          | 0  
Year         | 0  
CaptureTime  | 0  
ReleaseTime  | 0  
BandNumber   | 0  
Species      | 0  
Age          | 0  
Sex          | 0  
Wing         | 1  
Weight       | 10 
Culmen       | 7  
Hallux       | 6  
Tail         | 0  
StandardTail | 337
Tarsus       | 833
WingPitFat   | 831
KeelFat      | 341
Crop         | 343

ベクトルには `replace_nils!` メソッドを使うことができる。

```ruby
vector.replace_nils!
df["vname"].replace_nils!(0)
```

### 補間
現状ではほとんどできないと思われる。

```ruby
df.rolling_fillna
df.rolling_rillna!
```

## Rubyのイテレーターを使う
鬼門です。ものすごくわかりにくいです。
もしかすると、DaruのAPI自体を改善した方がよいかもしれません。
Daruの評判の悪さの原因の大半はここにあると思います。ひょっとすると、このわかりにくさはRubyそのものに遠因があるかもしれません。

####　map　collect　reduce　inject　all?　any? 

##### DataFrame

* `each` `map` は`axis:`(`:row` `:vector` `:column`)を指定する。`all?` `any?` も同様
* `each_vector`, `each_row`, `map_vectors`, `map_rows`
* `each_vector_with_index`, `each_row_with_index`, `map_vectors_with_index`, `map_rows_with_index`
* collect の挙動は `map` と違い、vectorになる。
* map は配列を返却する。
* map! は破壊的でdataframeになる
* recode は破壊的ではないが、dfを返却する
* `inject` はない。
* `collect_matrix`

* clone / dup
* transpose

名前の違うメソッドは、それぞれ微妙に挙動が違う傾向がある。`map`は配列を返し、`collect`はVectorを返し、`map!`はdfを返し破壊的、`recode`もdataframeを返すが破壊的ではないという関係性はわかりにくい。ここに書いてあることも間違っているかもしれない。現状まだ把握しきれていない。

##### Vector


### apply_method
引数はsymbol。procも引数に取る。破壊的ではない

```ruby
df.apply_method(:some_nice_method)
df.apply_method(->(x){x*10})
# df * 10
```

### aggregate
Rubyではブロックがあるため lambda のダッシュロケット記法はそんなに見かけないが、Daruでは使うシーンがあるだろうか?

```ruby
df.aggregate(num_100_times: ->(df) { (df.num*100).first })
df.aggregate(num: :mean)
```


### 列名の変更
`rename_vectors`

```ruby
df.rename_vectors :a => :alpha, :b => :beta
```

単なる`rename`はデータフレームの名称変更を意味するので、'name' という名前のベクトルがある時に注意

```ruby
df.name # データフレームの名前
```


### 列の並び替え

```ruby
df.order = ["col2", "col1"]
df.order = Daru::Index.new["col2", "col1"]
```

### 行の並び替え

```ruby
df.reindex Daru::Index.new([1,3,2,4])
```

### 特定の列をindexにする

```ruby
df.set_index "vector"
```

## ソートする

```ruby
df.sort ["col1"]
df.sort ["col1", "col2"]
df.sort ["col1"], assending: false
df.sort! ["col1"]
```

```ruby
df.sort [:b], by: {b: lambda { |a| a.length } }, handle_nils: true
```

## マージする
Daruでは `join` というメソッドを使う。
`how` で `:inner` `:outer` `:right` `:left` などを指定。

```ruby
new_df = df.join(df2, how: :inner, on: [:name])
```

一方で `merge` という名前のメソッドもある。

## 結合する

```ruby
new_df = df.concat(df2)
```

## 時系列インデックス

### DateTimeIndex
from_csv => datetime => set_index

```ruby
vecotor['2014']
```

`month` などを利用して df["month"] = df.index.month 
と行を追加して、`group_by("month") などとすれば月別の集計もできるかも。（現状は既知のバグあり）

# 統計量

```ruby
df.max
df.min
df.mean
df.median
df.std
df.variance
df.rolling_XXX #XXXにはmin, max, meanなどが入る
df.standardize
# etc
```

表示が見にくいときは`round`が使える。

```ruby
df.group_by("Species").mean.round(3)
```

# describe

```ruby
df.describe.round(2)
```

      | SepalLength | SepalWidth | PetalLength | PetalWidth
----- | ----------- | ---------- | ----------- | ----------
count | 150         | 150        | 150         | 150       
mean  | 5.84        | 3.05       | 3.76        | 1.2       
std   | 0.83        | 0.43       | 1.76        | 0.76      
min   | 4.3         | 2.0        | 1.0         | 0.1       
max   | 7.9         | 4.4        | 6.9         | 2.5       

`summary` はterminal出力用

# group_by

```ruby
gb = df.group_by [:col1, :col2]
gb.mean
gb.get_group :col1
```
gbは `Daru::Core::GroupBy` クラスのオブジェクト。

```ruby
df.group_by("Name").mean.round(3)
```

                | SepalLength | SepalWidth | PetalLength | PetalWidth
--------------- | ----------- | ---------- | ----------- | ----------
Iris-setosa     | 5.006       | 3.418      | 1.464       | 0.244     
Iris-versicolor | 5.936       | 2.77       | 4.26        | 1.326     
Iris-virginica  | 6.588       | 2.974      | 5.552       | 2.026     


# pivot

```ruby
df = Daru::DataFrame.new({
  a: ['foo'  ,  'foo',  'foo',  'foo',  'foo',  'bar',  'bar',  'bar',  'bar'],
  b: ['one'  ,  'one',  'one',  'two',  'two',  'one',  'one',  'two',  'two'],
  c: ['small','large','large','small','small','large','small','large','small'],
  d: [1,2,2,3,3,4,5,6,7],
  e: [2,4,4,6,6,8,10,12,14]
})
```

  | a   | b   | c     | d | e 
---| --- | --- | ----- |---|---
0 | foo | one | small | 1 | 2 
1 | foo | one | large | 2 | 4 
2 | foo | one | large | 2 | 4 
3 | foo | two | small | 3 | 6 
4 | foo | two | small | 3 | 6 
5 | bar | one | large | 4 | 8 
6 | bar | one | small | 5 | 10
7 | bar | two | large | 6 | 12
8 | bar | two | small | 7 | 14

```ruby
hoge = df.pivot_table(index: [:a, :c], vectors: [:b], agg: :mean, values: :e)
```

      |       |one   | two  
----- | ----- | ---- | ----
bar   | large | 8.0  | 12.0
      |small | 10.0  | 14.0
foo   | large | 4.0  |     
      |small | 2.0   | 6.0 

## 行や列の追加

最後の列に追加するとき

```ruby
df["new_name"] = Vector.new
```
`add_vector` `add_row`　などで列数・行数を指定できる。

## 行や列の削除
`delete_vector` `delete_vectors` `delete_row`

## only_numerics
type には `object` と `numeric` の2種類がある。
`only_numerics` は便利で使用頻度が高い

```ruby
df.only_numerics # 数値だけのデータフレーム
```

# カテゴリー

```ruby
df.to_category "Name"
df.Name.type           # :category
```

```ruby
dfs = df.split_by_category "Name"
dfs[0].head(3)
dfs[1].head(3)
dfs[2].head(3)
```

   | SepalLength | SepalWidth | PetalLength | PetalWidth
---| ----------- | ---------- | ----------- | ----------
0 | 5.1         | 3.5        | 1.4         | 0.2       
1 | 4.9         | 3.0        | 1.4         | 0.2       
2 | 4.7         | 3.2        | 1.3         | 0.2       

   | SepalLength | SepalWidth | PetalLength | PetalWidth
---| ----------- | ---------- | ----------- | ----------
50 | 7.0         | 3.2        | 4.7         | 1.4       
51 | 6.4         | 3.2        | 4.5         | 1.5       
52 | 6.9         | 3.1        | 4.9         | 1.5       

    | SepalLength | SepalWidth | PetalLength | PetalWidth
--- | ----------- | ---------- | ----------- | ----------
100 | 6.3         | 3.3        | 6.0         | 2.5       
101 | 5.8         | 2.7        | 5.1         | 1.9       
102 | 7.1         | 3.0        | 5.9         | 2.1       

## 可視化ライブラリ

### Nyaplot
標準は[Nyaplot](https://github.com/SciRuby/nyaplot)である。もし使いたい場合は erector もインストールする。
Nyaplotはかっこいい。しかしメンテナンスが事実上停止しており、今後の開発は期待しにくい。
Jupyter Labでは動作しないかもしれない。オフラインでも動作しない。
散布図、棒グラフ、箱ひげ図、折れ線、ヒストグラムなどがある。
現在RubyPlotの開発が進められているが、おそらくNyaplotの置き換えを視野に入れていると思われる。

### Daru::View
現状では実用性で Daru::View が頭一つ抜ける。Google Chartsは使いやすい。

```ruby
require 'daru/view'
require 'rdatasets'

df = RDatasets.load :datasets, :USArrests
df.head 5
```

  |            | Murder | Assault | UrbanPop | Rape
---| ---------- | ------ | ------- | -------- | ----
0 | Alabama    | 13.2   | 236     | 58       | 21.2
1 | Alaska     | 10     | 263     | 48       | 44.5
2 | Arizona    | 8.1    | 294     | 80       | 31  
3 | Arkansas   | 8.8    | 190     | 50       | 19.5
4 | California | 9      | 276     | 91       | 40.6

```ruby
Daru::View::Plot.new(df,
    title: "USArrests dataset",
    width: 900,
    height: 560,
    isStacked: true,
    hAxis: {slantedTextAngle: 90, textStyle: {fontSize: 9}},
    type: :column,
    adapter: :googlecharts
    ).show_in_iruby
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/144608/07ca38d2-8661-daf3-aef1-2746240d697c.png)

```ruby
Daru::View::Plot.new(df,
    title: "USArrests Dataset", 
    titleTextStyle: {fontSize: 16},
    width: 840,
    height: 560,
    hAxis: {title: "Murder"},
    vAxis: {title: "Assault"},
    colors: ["yellow", "red"],
    type: :bubble,
    adapter: :googlecharts
    ).show_in_iruby
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/144608/3d35236a-9ad3-5ab5-c0b3-76b651cce77a.png)

#### 散布図行列
下記のような関数を作成すれば、散布図行列のようなものを描くことも可能である。

```ruby
def pair_plot(df)
  vector_names = df.only_numerics.vectors.to_a

  opts = {
    chartArea: { left: 5, right: 5, top: 5, bottom: 5 },
    height: 100,
    width: 100,
    hAxis: { viewWindowMode: 'maximized' },
    vAxis: { viewWindowMode: 'maximized' },
    backgroundColor: '#f0f0f0',
    gridlineColor: 'white',
    colors: ['#4784BF']
  }

  vector_names.each do |row|
    plots = vector_names.map do |column|
      if row != column

        Daru::View::Plot.new(
          df[column, row], opts.merge(type: :scatter,
                                      pointSize: 1)
        )
      else
        Daru::View::Plot.new(
          df[row], opts.merge(title: row,
                              titlePosition: 'in',
                              type: :histogram)
        )
      end
    end
    IRuby.display Daru::View::PlotList.new(plots).show_in_iruby
  end
end
```

```ruby
pair_plot(df)
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/144608/7bf90e68-1c30-5654-6645-20d948cca2c5.png)

### numo-gnuplot
gnuplotの文法に慣れていれば使いやすいかもしれない。
[【Ruby】Daru + gnuplot で散布図を描く](https://qiita.com/kojix2/items/c7f4eda21d3a3307bea8)

### PyCall + Python のライブラリ

ややトリッキーだが、[PyCall](https://github.com/mrkn/pycall.rb)を利用してPythonのライブラリを使用する方法がある。仕事で他人に見せるなど、本格的なグラフが必要なときの手段として有力。

```ruby
require 'daru'
require 'rdatasets'

iris = RDatasets.load :datasets, :iris
iris.write_csv "iris.csv"
```

```ruby
require 'pycall'
require 'pycall/import'
include PyCall::Import
require 'matplotlib/iruby'
Matplotlib::IRuby.activate
Matplotlib.interactive(false) # これがないと描画がとても遅くなる

pyimport :pandas, as: :pd
pyimport :seaborn, as: :sns

df = pd.read_csv("iris.csv")
df.head(3)
```

```ruby
sns.pairplot(df, hue: "Species")
```

![sns.png](https://qiita-image-store.s3.amazonaws.com/0/144608/6ed928c0-3aa5-f5cd-5bb9-14338bbb0dfb.png)


## 書き出す
`write_csv`

# おわりに

### Rails関連

[Rails と jupyter の連携をとても簡単にする railtie の gem を書いた](https://qiita.com/Yuki-Inoue/items/1614f5df5bf187fa191a)

### Daruの歩き方

困ったら `yard server -g` でドキュメントを読む。
IRuby上で `ri` でドキュメントを見たり、 `show-source` や でソースコードに当たってみる。Pryを活用する。

```ruby
show-source Daru::DataFrame#from_csv
```
とすれば、該当部分のソースがJupyterに表示される。
ソースコードは平易で、詳しくない人でもまあまあ読める。怪しいと思ったら、ソースコード全体をセルにコピペして、オープンクラスの仕組みを使ってデバッグ。バグを踏んだらぜひissueに投稿してほしい。

```ruby
ri Daru::DataFrame.some_nice_method
```

### Daruからの声

下記のポエムを頭の片隅におくと、Daruのメソッドの名付け方が理解しやすいかもしれない。

> …きこえますか…きこえますか…ルビイストよ…Daruです。いいですか、データは大事です。データとは、単なる数字の列ではありません。それは誰かが膨大な時間をかけて収集した貴重な資料です。たとえば…パン10個、ゾウ10頭、10光年、10年、Version10…。いずれも同じ数字の 10 ですが意味はだいぶ違うでしょ？　実はデータには、数字以上の情報が含まれているのですよ！君は、その数字の列が本当は何を意味しているのか理解しなければなりません…。

> …ベクトルをいくつか集めたのがデータフレームです。ベクトルの「名前」には、データを記述した人間が観測した目的や意図が、名前という形で刻印されています。データフレームとは、単なる行列計算の道具ではないのです。それは、数字に付与された人間の残存思念を、ベクトルの名称という形でサポートするツールなのです。だから、ベクトルは番号ではなく、なるべく「名前」で呼んであげなければなりません。Daruの世界ではベクトルはRow(行)などよりずっと偉いのです！

（※Daruの公式見解ではなく筆者がDaruをみて感じた感想です）

# QiitaにおけるDaruの記事

* [Daru のちょっと詳しい説明: Index, Vector, DataFrame のデータ構造について](https://qiita.com/Yuki-Inoue/items/2f826d67fb4f34a8ab1a)
* [Rubyで数値計算（R, Pythonと比較）](https://qiita.com/t2h5/items/6fc4e8c2f314684fa120)
* [Jupyter Notebook + Ruby で CSV を加工して出力する](https://qiita.com/QUANON/items/cbbca57eebc7d1b835b3)
* [daru チートシート](https://qiita.com/kozo2/items/d3e9e22a88e33feed779)
* [Ruby で数値を弄るやーつ -- Daru について --](https://qiita.com/yancya/items/32cc434541efead3fa34)
* [Rubyでデータ解析 - Daru入門](https://qiita.com/yohm/items/b63e20634f86b4f66f9d)


# 参考資料（pandas)
* [データ分析で頻出のPandas基本操作](https://qiita.com/ysdyt/items/9ccca82fc5b504e7913a)
* [ゆるふわPandasチートシート](https://qiita.com/tanemaki/items/2ed05e258ef4c9e6caac)