Apache Arrow をやってみた

Apache Arrow をやってみた:

なんか「Apache Arrow が良いらしいよ。」と聞いたので、早速かまってみた。


Apache Arrowってなに?

→ 公式 https://arrow.apache.org

データを言語(プロジェクト)間でやり取りするときの効率化と高速化を目指すものっぽいです(ざっくり。本当は違うかも)

https://www.slideshare.net/MapR_Japan/apache-arrow-value-vectors-tokyo-apache-drill-meetup-20160322

そういえば、最近かかわったプロジェクトで、Ruby でデータを揃えて、 Python で機械学習する、っていうのがありまして、その時は Ruby 側で揃えたデータをファイルに書き出して、Python 側でそのファイルを読み込んで処理して、結果をファイルに書き出して、Ruby 側で読み込んで Javascript(ブラウザ)でグラフ化する、ということをしていました。

こういうところで使えそう?

ということで、ちょっとやってみます。


Ruby で Apache Arrow

gemにあるのでインストールします。

Gemfile
source "https://rubygems.org" 
 
gem "rake" 
gem "red-arrow" 
$ bundle install --path vendor/bundler 
インストール完了です。

早速使ってみましょう。

test.rb
# -*- coding: utf-8 -*- 
require "arrow" 
 
class RedArrowTest 
 
  # ------------------------------------------------ 
  def read(streamname) 
    input = Arrow::MemoryMappedInputStream 
    input.open(streamname) do |inp| 
      reader = Arrow::RecordBatchStreamReader.new(inp) 
      fields = reader.schema.fields 
      p "fields: #{fields.collect{|c| c.name }}" 
      reader.each do |r| 
        p [r] 
      end 
    end 
  end 
 
  def readfile(filename) 
    input = Arrow::MemoryMappedInputStream 
    input.open(filename) do |inp| 
      reader = Arrow::RecordBatchFileReader.new(inp) 
      fields = reader.schema.fields 
      p "fileds: #{fields.collect{|c| c.name }}" 
      reader.each do |r| 
        p [r] 
      end 
    end 
  end 
 
  # ------------------------------------------------ 
  def write(streamname, schema, columns) 
    output = Arrow::FileOutputStream 
    output.open(streamname, false) do |oup| 
      writer = Arrow::RecordBatchStreamWriter.open(oup, schema) do |wrt| 
        recordbatch = Arrow::RecordBatch.new(schema, columns[0].count, columns) 
        wrt.write_record_batch(recordbatch) 
      end 
    end 
  end 
 
  def writefile(filename, schema, columns) 
    output = Arrow::FileOutputStream 
    output.open(filename, false) do |oup| 
      writer = Arrow::RecordBatchFileWriter.open(oup, schema) do |wrt| 
        recordbatch = Arrow::RecordBatch.new(schema, columns[0].count, columns) 
        wrt.write_record_batch(recordbatch) 
      end 
    end 
  end 
 
  # ------------------------------------------------ 
  # sample data 
  def createdata 
    fields = [ Arrow::Field.new("name", :string), 
               Arrow::Field.new("age", :int8) ] 
    schema = Arrow::Schema.new(fields) 
 
    names = ["taro", "jiro", "hanako", "tsukiko", "saburo"] 
    ages = [26, 21, 24, 17, 10] 
 
    columns = [ Arrow::StringArray.new(names), 
                Arrow::Int8Array.new(ages) ] 
 
    return schema, columns 
  end     
end 
 
# ---------------------------------------------------------------- 
# run 
 
ra = RedArrowTest.new 
 
# create sample data :p 
schema, columns = ra.createdata 
 
ra.write("hoge", schema, columns) 
ra.read("hoge") 
 
#ra.writefile("hoge", schema, columns) 
#ra.readfile("hoge") 
どうやらファイル経由のものとメモリ経由のものがあるようです。

→ read、write がメモリ経由、readfile、writefile がファイル経由

#がしかし、いずれもファイルを生成するんだなぁこれが。。。

#内容はちょっと違うみたいだけど、、、とりあえずメモリ経由のものを使いましょう。

では実行。

$ bundle exec ruby test.rb 
"fields: [\"name\", \"age\"]" 
[#<Arrow::RecordBatch:0x7feaf2057500 ptr=0x7feaf30f0f90 name:   [ 
    "taro", 
    "jiro", 
    "hanako", 
    "tsukiko", 
    "saburo" 
  ] 
age:   [ 
    26, 
    21, 
    24, 
    17, 
    10 
  ] 
>] 
できました。

実体は、カレントディレクトリに hoge ファイルができててそれみたいです。


Javascript で Apache Arrow

言語間でデータのやりとりができるということだったので、Ruby で作ったデータを Javascript で読み込んでみます。


Node.js って

Javascript でも使えるって書いてあったから、ブラウザサイドでもちゃちゃっと使えるんだと思ってましたが、どうやら全然違ったようです。

とりあえず Node.js をインストールします。

うちは Mac なので Homebrew でインストールします。

Node.js の各種パッケージなどを管理する nodebrew というものがあるらしく、それをインストールして Apache Arrow をインストールします。

$ brew install nodebrew 
$ mkdir -p ~/.nodebrew/src 
$ nodebrew install-binary latest  ← nodebrew install latest でもいいらしい。 
install-binary はバイナリをインストール。install はソースからインストールらしい。。 
$ nodebrew list 
v11.1.0 
 
current: none 
はい、インストールできました。

しかしこのままでは使えないので、設定します。

$ nodebrew use 11.1.0 
use v11.1.0 
~/.bash_profile にパスを追加

# Node.js 
export PATH="$HOME/.nodebrew/current/bin:$PATH" 
とりあえず以上で Node.js がインストールできました


ブラウザサイドでも使用できるようにしてみる

いろいろネットで探してみましたが、やはり直接ブラウザサイドで使用する方法は見つかりませんでした。代わりに、Node.js でサーバを立てて、jQuery でそのサーバにアクセスする、という方法でデータを渡している人が居られましたので、その方法を真似てみることにしました。


express で HTTPサーバ

Node.js のパッケージを使うコマンドは npm と言うようです。これを使って各種パッケージをインストールします。

どうやら、カレントディレクトリに node_modules というフォルダを作って、その中に関係するファイルを保存するっぽいです。

なので、とりあえずプロジェクトのフォルダに移動してからインストールします。

$ cd appfolder 
$ npm install express 
Apache Arrow もインストールしましょう。

$ npm install apache-arrow 


サーバを作る

arrow_service.js
"use strict" 
// apache-arrow service :p 
 
const express = require("express"); 
const port = 8010; 
const app = express(); 
app.use(express.static("./public")); 
 
// allow CrossSiteScript 
app.use(function(req, res, next) { 
    res.header("Access-Control-Allow-Origin", "*"); 
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 
    next(); 
}); 
 
// say hello 
app.get("/", (req, res) => { 
    res.send("Hello, world!"); 
}); 
 
// arrow 
app.get("/input", (req, res) => { 
    const fs = require("fs"); 
    const aa = require("apache-arrow"); 
    const path = "./"; 
    const encoding = "binary"; 
 
    var filename = req.query.n; 
    //console.log("filename: " + filename); 
 
    // result data 
    var result = {}; 
 
    if(filename != undefined){ 
        try { 
            var adata = fs.readFileSync(path + filename); 
            var table = aa.Table.from([adata]); 
            var schema = table.schema;       
            var fldnames = new Array(); 
            for(var i = 0; i < schema.fields.length; i++){ 
                fldnames.push(schema.fields[i].name); 
                result[schema.fields[i].name] = new Array(); 
            } 
            for(var j = 0; j < fldnames.length; j++){ 
                var column = table.getColumn(fldnames[j]); 
                for(var i = 0; i < column.length; i++){ 
                    result[fldnames[j]].push(column.get(i)); 
                    //console.log(column.get(i)); 
                } 
            }    
        } catch(ex){ 
            // nop :p 
        }    
    } 
    res.json(result); 
}); 
 
// run service! 
app.listen(port); 
では走らせます。

$ node arrow_service.js 
とりあえずブラウザでアクセスしてみましょう。

ブラウザで http://localhost:8010/ にアクセスして Hello, world! が表示されれば成功です。


スクリーンショット 2018-11-06 0.04.04.png


動いてますね。

では、ブラウザ側を作りましょう。


Ajaxでアクセス

jQuery を使ってアクセスをします。

とりあえず、application.js に関数を作ります。

application.js
function arrowInput(arrowname) {     
    var result; 
    $.ajax({ type: "GET", 
         url: "http://localhost:8010/input", 
         data: { n: arrowname }, 
         async: false, 
         dataType: "json", 
         success: function(data){ 
         result = data; 
         } 
       }); 
    return result; 
} 
今回 Ajax ですが非同期ではなく同期にしました。

ここら辺はプロジェクトに合わせてごにょごにょしてみてください。

では html 側を作ります。

index.html
<html> 
  <head> 
    <title>Red Arrow Test</title> 
    <script type="text/javascript" src="/javascripts/jquery-3.2.1.min.js"></script> 
    <script type="text/javascript" src="/javascripts/application.js"></script> 
  </head> 
  <body> 
    <h3>Red Arrow Test</h3> 
 
<script type="text/javascript"> 
  var data = arrowInput("hoge"); 
  console.log(data); 
</script> 
 
  </body> 
</html> 
では実行してみましょう。



スクリーンショット 2018-11-06 10.09.05.png


ブラウザ画面だと console.log が見れないので、右ボタン「要素の詳細を表示」でツール窓をだして、リロードすると、コンソール画面に取得した内容が表示されているはずです。
スクリーンショット 2018-11-06 10.09.59.png


お、なんか良さげですね。


まとめ

以上で、Apache Arrow を使って Ruby で作成したデータを Javascript(ブラウザサイド) で取得することができました。

まあ本当にこういう使い方なのかはわかりませんが、確かにデータのやり取りは楽になったような気がします。もう少しかまってみようと思います。

#しかし、これ、要らなくなったデータを消すタイミング(ガベージコレクション)ってどうなんでしょうね? 手動っぽいですけど。。。

コメント

このブログの人気の投稿

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-04-30 23:37:32 RSSフィード2021-04-30 23:00 分まとめ(42件)

投稿時間:2023-02-05 02:09:04 RSSフィード2023-02-05 02:00 分まとめ(9件)