Red Arrow 覚え書き

## はじめに

　Rubyでデータフレームの操作をしようとすると、Daru::DataFrameがあります。しかしDaruはバックエンドにRubyの配列を使っているため、実行速度はあまり速くありません。このページでは、Daruの代替としてApacheArrowのRubyバインディングであるRed ArrowのTableつかい方を簡単にまとめておきます。

## 基礎知識

### 実装

　C++実装 → C実装(GLib) → (GObject Introspection) → Ruby実装

RubyのメソッドはGObject Introspectionで自動生成されている。

### Arrow

* Arrow
    * メモリ上にデータを配置するフォーマット。効率的に処理できるようにする。ただし、Arrow　IPC形式のファイルというものも存在し、プロセス間通信に使用する。Feather形式はArrow　IPC形式の旧称。

* Parquet
    * ストレージにデータを保存する圧縮フォーマット。ディスク使用量を最小限にする。永続的に保存。

## インストール
* Apache Arrow のインストール https://arrow.apache.org/install/
* `gem install red-arrow`

## Arrow プロジェクトの面白いところ

> メモリー上のデータ配置を仕様化するのはApache Arrow以外には聞いたことないですねぇ。

> 同じソフトウェア内でならプロセスを超えて共有するのはよくやりますが（たとえばPostgreSQLとか）、ソフトウェアを超えて共有するのはレアだと思います。なのでApache Arrowが重要なんだと思っています。

## ドキュメント

GObject introspection でバインディングを自動生成しているライブラリなので yarddoc は当てにならない。テストを見るのが良さそう。

* https://github.com/apache/arrow/tree/master/ruby/red-arrow/test

## Arrow を利用したデータフレーム

https://github.com/heronshoes/red_amber

## Arrow::Table の簡単なサンプル

ここでは有名なirisのデータを読み込むことにします。

CSVファイルをダウンロードします。

```sh
wget https://raw.githubusercontent.com/pandas-dev/pandas/master/pandas/tests/data/iris.csv
```

```ruby
require 'arrow'

t = Arrow::Table.load("iris.csv")
```

### 列数・行数

```ruby
t.n_columns
# 5
t.n_rows # size # length
# 150
```

### 列名

```ruby
t.columns.map(&:name)
# => ["SepalLength", "SepalWidth", "PetalLength", "PetalWidth", "Name"]
```

### 列選択

```ruby
t[5] # t.find_column(5)
t["Name"] # t.find_column("Name")
```

### 複数列選択

```ruby
t.select_columns(1,2,5)
t.select_columns(1..2)
t.select_columns("SepalLength", "Name")
```

### 行選択

```ruby
t.slice(3)
t.slice(3).to_a
t.slice(3).to_h
t.slice(3,5) # index 3から5行選択の意
```

### 列イテレータ

```ruby
t.each_column {|col| }
```

### 行イテレータ

```ruby
t.each_record {|r| }
t.each_record_batch {|r| }
```

### Table → Rubyの配列に変換

```ruby
t.raw_records
```

# CSV形式で保存

```ruby
t.save("iris_save.csv")
```

　ここにあげたのはTableのつかい方（将来的にはDataFrameクラスが追加される予定とのこと）ですが、とにかくApacheArrowは高速ですね。

### delimiterの指定

```ruby
require "arrow"

pp Arrow::Table.load("/tmp/a.tsv",
                     format: "csv",
                     delimiter: "\t".ord)
```

###文字列からのCSV読み込み

```ruby
Arrow::CSVLoader.load("1a2a3\n4a5a6", delimiter: "a")
```

## リンク
* [Apache Arrow](https://arrow.apache.org/)
* [Apache Arrow(PyArrow)を使って簡単かつ高速にParquetファイルに変換する](https://dev.classmethod.jp/articles/20190614-apache-arrow-parquet/)

## ポエム
<details><summary>●</summary><div>

　あと、これからDataFrame機能が追加されていく予定ですが、Ruby向けのAPIの実装を考えるのは、実際のところ、かなりけわしい仕事になるんじゃなかろうかと思うのです。たぶんC言語で実装したり、C++言語で実装したり、Rubyのバインディングを作成したり、Pandasなどの既存のデータフレームをツールとして使いこなすのとは、違う質の思考が必要になる気がします。

　そこではRuby言語の関数を避けてメソッドを使いたがる性質が関係してくると思います。Rubyでは関数を表に立たせることを避ける傾向があるように（個人的に）感じています。メソッドはどれもオブジェクトに紐づけされており、メソッド自体はあまりオブジェクトとして扱えず、関数的なものは、ブロックや、proc、lambda、アロー演算子など、関数っぽい何かとしてゴテゴテと周辺的に存在している気がします。私は、操作というものを実体として考えるのはすごく苦手なので、どんな操作が可能なのかはオブジェクト自身が知っていて、操作を知りたい時はオブジェクトに聞けばいいというRubyの考え方はすごくフィットします。でも、データ処理のツールは、やはり数学方面から発展してきたこともあり、関数を中心に据えてツールを作る方が伝統的にうまくいくみたいで、そのあたりの課題とうまく折り合いをつけながらDataFrameの操作を考えなければならないんじゃないかなと思います。

　そういう事ができるかどうかは、その場で急に努力してもたぶんダメで、強烈な才能が必要だと思うので（才能とは、それまでの人生でどのぐらい深くDataFrameについて考えてきたかという意味かもしれませんが）そんな才能あふれる人々が登場するのを私は待ち望んでいます。Rubyのハドリー・ウィッカムさんみたいな方が求められているかもしれません。

　身の回りに、そういう強烈な才能を持っている人がいたら、ぜひRuby用のデータフレームを開発してみないか勧誘してください。よろしくおねがいします。
</div></details>

　この記事は以上です。