データフレーム(2次元の表形式のデータ)を用意されていて、それに何かしらの処理しようと思った時パッと思い浮かぶ利用ツールはPythonのPandasでしょうか?それともExcelなどでしょうか?
今回はそのどちらでもなく、RubyKaigiでも紹介されたRubyのgemの一つであるRedAmberを触ってみます。
RedAmberとは?
データフレームを扱うためのRubyのライブラリです。(ソースコードは下記)
ApacheArrowのRubybindingであるRedArrowが中では動いているみたいです。
詳細は開発者の方がRubyKaigiで発表したスライドがあります。
検証環境
今回は下記の環境で使ってみます。
- Ruby 3.2.2
- M1 Pro
セットアップ
Readmeを参考に下記コマンドを実行します。
brew install apache-arrow
brew install apache-arrow-glib
RedAmberを動かすためにはRedArrowが必要で、RedArrowを動かすためにはApacheArrowが必要、というわけでApacheArrowをインストールしているわけですね。
次に、RedAmberをインストールします。
今回はGemfileを使ってbundle install
します。(普通にgem install red_amber
でもいいと思います)
データセットのサンプルとして、red-datasets-arrowも一緒にインストールしています。
# -*- mode: ruby -*-
# frozen_string_literal: true
source 'https://rubygems.org'
ruby '3.2.2'
gem 'red_amber'
gem 'red-datasets-arrow'
https://github.com/red-data-tools/red-datasets からデータセットの情報を見ることができます。
今回はIrisを使ってみましょう。
まずはデータを覗いてみます。まずは読み込んで出力してみましょう。
require 'datasets-arrow'
require 'red_amber'
include RedAmber
dataset = Datasets::Iris.new
iris = DataFrame.new(dataset) # before v0.2.3, should be `dataset.to_arrow`
p iris
#<RedAmber::DataFrame : 150 x 5 Vectors, 0x000000000000337c>
sepal_length sepal_width petal_length petal_width label
<double> <double> <double> <double> <string>
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
・・・(中略)
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
ヘタと花弁の縦横(?)の長さ、それとIrisの種類の5項目が表示されました。
次にlabelごとに平均を出してみましょう。
まずは、label毎にデータセットを分割する必要がありますが、それはgroupを使うことで解決します。
先ほどのプログラムを少し変えて、irisをgroup関数を使って分けてみましょう。
require 'datasets-arrow'
require 'red_amber'
include RedAmber
dataset = Datasets::Iris.new
iris = DataFrame.new(dataset)
df = iris
.group(:label)
p df
実行するとこんな感じ。
ruby ave_group_label.rb
#<RedAmber::Group : 0x000000000000337c>
label count
<string> <int64>
0 Iris-setosa 50
1 Iris-versicolor 50
2 Iris-virginica 50
ラベルとラベルを振られた数が表示されています。3つのラベルが50ずつあるのがわかります。
さらにmeanを使ってlabel毎のそれぞれの平均を出してみましょう。
df = iris
.group(:label)
+ .mean(:sepal_length, :sepal_width, :petal_length, :petal_width)
実行すると、それぞれの値の平均を出してくれます。
❯ ruby ave_group_label.rb
#<RedAmber::DataFrame : 3 x 5 Vectors, 0x000000000000337c>
label mean(sepal_length) mean(sepal_width) mean(petal_length) mean(petal_width)
<string> <double> <double> <double> <double>
0 Iris-setosa 5.01 3.42 1.46 0.24
1 Iris-versicolor 5.94 2.77 4.26 1.33
2 Iris-virginica 6.59 2.97 5.55 2.03
さらに今回はsepal_widthを基準にソートしてみましょう。下記を追記するとソートしてくれます。
df = iris
.group(:label)
.mean(:sepal_length, :sepal_width, :petal_length, :petal_width)
+ .sort('-mean(sepal_length)')
実行してみると、sepal_widthが高いlabel順にソートされています。
❯ ruby ave_group_label.rb
#<RedAmber::DataFrame : 3 x 5 Vectors, 0x000000000000337c>
label mean(sepal_length) mean(sepal_width) mean(petal_length) mean(petal_width)
<string> <double> <double> <double> <double>
0 Iris-setosa 5.01 3.42 1.46 0.24
1 Iris-virginica 6.59 2.97 5.55 2.03
2 Iris-versicolor 5.94 2.77 4.26 1.33
低い方から出したい時は、sortの部分を一部変えると良いです。
df = iris
.group(:label)
.mean(:sepal_length, :sepal_width, :petal_length, :petal_width)
- .sort('-mean(sepal_length)')
+ .sort('+mean(sepal_length)')
❯ ruby ave_group_label.rb
#<RedAmber::DataFrame : 3 x 5 Vectors, 0x000000000000337c>
label mean(sepal_length) mean(sepal_width) mean(petal_length) mean(petal_width)
<string> <double> <double> <double> <double>
0 Iris-versicolor 5.94 2.77 4.26 1.33
1 Iris-virginica 6.59 2.97 5.55 2.03
2 Iris-setosa 5.01 3.42 1.46 0.24
触ってみて
ActiveRecordを触ったことがあってSQLもそこそこ描ける人なら、ActiveRecord触っている感覚でデータをサクサクと集計などを進めることができそうです。
今後ちょっとした集計などではRedAmberを使ってみて、フィードバックとしてPRやIssueも出していきたいです。
今回触れた機能はごく一部でまだまだいろんなことができるので、気になった人は是非リポジトリを覗いてみてください。
おまけ
今回作ったコードはここにアップしてあります。
https://github.com/Umekawa/red_amber_demo