2018.10.31

データサイエンスにJuliaを利用する


こんにちは、次世代システム研究室のA.Zです。今回はデータ解析や分析の分野における、Julia言語について、紹介したいと思います。

Julia言語について

Julia言語は高速な汎用プログラミング言語及び動的プログラミング言語及び科学計算に最適化された言語です。
JuliaはLLVM(Low Level Virtual Machine)を利用し、ネイティブコードを変換するため、処理の速度が早いです。
また、汎用的と動的という特徴を持ち、使いやうさも良いです。

Julia言語は2009年から開発され、2012年に最初にリリースされました。
今年の8月に、バージョン1.0がリリースされました。Julia言語の特徴は以下です。
  • Multiple Dispatch(同じ関数、違いパラメータがあるときに、パラメータによって、自動的に適切関数を実行される機能です)
  • 動的型と静的型対応
  • metaprogramming機能
  • 小さいオーバーヘッドで、C関数の呼び出し
  • 並列処理と分散処理機能

なぜJulia言語

データ解析や分析の業務は現在、pythonメインで使っています。特にデータ解析やアルゴリズム検証にはpythonは便利です。
しかし、解析した結果やアルゴリズムを実際に本番のサービスを利用するときに、そのままpythonを利用すると、処理スピードの問題やリソースの問題が出る可能性があり、別の高速言語(Java,C)に書き直さないといけないケースも出てきます。

解析時とアルゴリズム検証時にJuliaを使うと、そのまま同じコードが本番で使えるではないかと思い、今回Juliaをちょっと触ってみました。
juliaと他の言語のbenchmarkの結果は以下のリンクで確認できます

https://julialang.org/benchmarks/

また、PyCall.jlのライブラるを利用し、Juliaで、pythonのコードが利用できるので、最初はpython+juliaの組み合わせで、スタートしやすいではないかと思います。

Juliaの事例

Julia言語は様々な分野の事例は以下のリンクで確認できます。

https://juliacomputing.com/case-studies/

注目ケースはいくつかをピックします。
  • 宇宙データ解析 (https://juliacomputing.com/case-studies/celeste.html)
    注目ことはこちらで、Juliaは1.54 petaFLOPSのパーフォマンスを達成できました。これで、JuliaはC、C++、Fortranと一緒に、peta scaleのハイレベル言語に入りました。
  • BlackRockの次世代解析基盤 (https://juliacomputing.com/case-studies/blackrock.html)
    世界最大資産運用会社は2014年から、Juliaを使って、時系列データ解析とビッグデータ解析のシステムを作成しました。金融分野で、こちらのきっかけで、Julia言語が注目し始めたではないかと思います。
Julia言語はまだ新しいですが、既にプロミシングな事例が出てきました。ポテンシャルが大きいと思います。

データサイエンスに利用するには

今までの私の経験から、データ解析や分析には必要なライブラリやツールキットはだいたい以下です。
  • データフレーム的な処理
  • 統計関数
  • 可視化
  • ビッグデータや分散処理
今回は上記の視点や機能からJulia言語を調査し、触ってみました。その結果や感想は以下で簡単にまとめました。

1. データフレーム的な処理

Pythonで、こちら機能はpandasというライブラリが提供しています。データ解析や分析にはこの機能が不可欠になっています。
Juliaでは、pandasと似たような機能はDataFrames.jlが提供しています。次はこちらのパッケージについて簡単に紹介します。

Documentation:
http://juliadata.github.io/DataFrames.jl/stable/index.html

以下はDataFrameで普段行なっている処理の例をJulia言語でのサンプルコードです。

データフレームの作成:
julia>using DataFrames

julia>df = DataFrame(A = 1:2:1000, B = repeat(1:10, inner=50), C = 1:500)
julia>df
500×3 DataFrame
│ Row │ A     │ B     │ C     │
│     │ Int64 │ Int64 │ Int64 │
├─────┼───────┼───────┼───────┤
│ 1   │ 1     │ 1     │ 1     │
│ 2   │ 3     │ 1     │ 2     │
│ 3   │ 5     │ 1     │ 3     │
│ 4   │ 7     │ 1     │ 4     │
│ 5   │ 9     │ 1     │ 5     │
│ 6   │ 11    │ 1     │ 6     │
│ 7   │ 13    │ 1     │ 7     │
⋮
│ 493 │ 985   │ 10    │ 493   │
│ 494 │ 987   │ 10    │ 494   │
│ 495 │ 989   │ 10    │ 495   │
│ 496 │ 991   │ 10    │ 496   │
│ 497 │ 993   │ 10    │ 497   │
│ 498 │ 995   │ 10    │ 498   │
│ 499 │ 997   │ 10    │ 499   │
│ 500 │ 999   │ 10    │ 500   │
 

データフレームのアクセスの例:
# column access
julia> df[:A][1:3]
3-element Array{Int64,1}:
 1
 3
 5

#range access
julia> df[1:3,[:A,:B]]
3×2 DataFrame
│ Row │ A     │ B     │
│     │ Int64 │ Int64 │
├─────┼───────┼───────┤
│ 1   │ 1     │ 1     │
│ 2   │ 3     │ 1     │
│ 3   │ 5     │ 1     │

#conditional access
julia> df[df.A .<=5,[:A,:B]] 3×2 DataFrame │ Row │ A │ B │ │ │ Int64 │ Int64 │ ├─────┼───────┼───────┤ │ 1 │ 1 │ 1 │ │ 2 │ 3 │ 1 │ │ 3 │ 5 │ 1 │ #access with variable julia> col=:A
:A

julia> typeof(col)
Symbol

julia> df[1:3,col]
3-element Array{Int64,1}:
 1
 3
 5
 

joinとGroup Byの例:
julia> df1=DataFrame(ID = [20, 40,50], name = ["Hoge", "Bar", "Foo"])
3×2 DataFrame
│ Row │ ID    │ name   │
│     │ Int64 │ String │
├─────┼───────┼────────┤
│ 1   │ 20    │ Hoge   │
│ 2   │ 40    │ Bar    │
│ 3   │ 50    │ Foo    │

julia> df2=DataFrame(ID = [20, 40,60], address = ["Tokyo", "Osaka","Nagoya"])
3×2 DataFrame
│ Row │ ID    │ address │
│     │ Int64 │ String  │
├─────┼───────┼─────────┤
│ 1   │ 20    │ Tokyo   │
│ 2   │ 40    │ Osaka   │
│ 3   │ 60    │ Nagoya  │

#inner join
julia> join(df1,df2,on=:ID)
2×3 DataFrame
│ Row │ ID    │ name   │ address │
│     │ Int64 │ String │ String  │
├─────┼───────┼────────┼─────────┤
│ 1   │ 20    │ Hoge   │ Tokyo   │
│ 2   │ 40    │ Bar    │ Osaka   │
#left
julia> join(df1,df2,on=:ID,kind=:left)
3×3 DataFrame
│ Row │ ID    │ name   │ address │
│     │ Int64 │ String │ String⍰ │
├─────┼───────┼────────┼─────────┤
│ 1   │ 20    │ Hoge   │ Tokyo   │
│ 2   │ 40    │ Bar    │ Osaka   │
│ 3   │ 50    │ Foo    │ missing │

#groupby
julia> df3=DataFrame(ID = [20, 40,50,20,20,40], num = [1,1,1,1,1,1])
6×2 DataFrame
│ Row │ ID    │ num   │
│     │ Int64 │ Int64 │
├─────┼───────┼───────┤
│ 1   │ 20    │ 1     │
│ 2   │ 40    │ 1     │
│ 3   │ 50    │ 1     │
│ 4   │ 20    │ 1     │
│ 5   │ 20    │ 1     │
│ 6   │ 40    │ 1     │

julia> aggregate(df3,:ID,sum)
3×2 DataFrame
│ Row │ ID    │ num_sum │
│     │ Int64 │ Int64   │
├─────┼───────┼─────────┤
│ 1   │ 20    │ 3       │
│ 2   │ 40    │ 2       │
│ 3   │ 50    │ 1       │
 

触ってみた感じは基本的に、DataFrameの処理方法はpandasと似ています。もちろん、少しい概念や方法の違いもありますが、pandasに慣れているだったら、JuliaのDataframeでもすぐに利用できると思います。また、pandasでしかない関数ももちろんあります。例えば:window関数

Juliaはpythonの違って、処理スピードが早いです。pythonだとpandasのwindow関数を使わずに、pure pythonの関数利用すると処理スピードが遅くなります(pandasは一部の処理の高速化するために、C言語で実装されているため)。Juliaで、足りない関数があれば、そのままJulia言語で実装しても、処理スピードの問題はないと思います。

2. 統計関数

pythonでは統計関数は基本的にscipy.statsが提供しています。Juliaで、こちらの機能は以下のパッケージが提供しています。
  • StatsBase.jl
  • StatsModel.jl
  • Distributions.jl
  • MultivariateStats.jl
  • OnlineStat.jl
  • HypothesisTests.jl
具体的には以下のリンクです。
http://juliastats.github.io/

上記のライブラリのドキュメンテーションを見ると、様々な統計関数が提供されているので、個人的に、データ解析や分析には十分ではないかと思います。

3. 可視化

Juliaで、メインな可視化ライブラリはPlot.jlです。こちらのライブラリは統一のinterfaceを提供し、様々なバックエンド(画像生成エンジン)と連携できます。
その結果、一つコードで、様々なバックエンドを利用することができます。現在、Plot.jlが対応しているバックエンドは以下です。
  • pyplot.jl
    こちらはpythonのpyplotのライブラリを利用するバックエンドです。グラフの生成処理はpythonのpylot api経由で生成します。
    今まで、pythonで利用している可視化種類は対応できます。
  • GR.jl
    こちらのバックエンドはGRフレームワークhttp://gr-framework.org/ という画像生成ライブラリを利用します。パーフォマンス的にはこちらバックエンドが良いですが、
    サポートされている可視化種類がまだ
  • plotly.jl
    こちらのバックエンドはJavaScript(JS)を利用して、画像生成を行います。基本的に、Web用のインターラクティブな可視化だったら、こちらを利用します。
次はJuliaでの可視化するためのサンプルコードです。
julia> gr() #use gr backend
Plots.GRBackend()

julia> x=1:5
1:5

julia> y=rand(5,2)
5×2 Array{Float64,2}:
 0.617008  0.548398 
 0.717441  0.0558186
 0.66377   0.707928 
 0.498989  0.0199771
 0.529762  0.810507 

julia> plot(x,y,seriestype=:scatter)
 


 

以上、のサンプルはPlots.jlで、GRバックエンドを使って、グラフ生成することです。pythonのpyplotに慣れている人はpyplot.jlも使えるし、pythonからの切り替えもしやすくなるではないかと思います。

4. ビッグデータや分散処理

今のビッグデータの時代で、データ解析する時に、基本的にビッグデータフレームワーク(Hadoop,Spark)を利用するのは一般的です。
pythonで、pysparkというSparkが正式にサポートしているライブラリがあります。pythonでビッグデータ解析行うとき、pysparkは不可欠ライブラリになります。

Juliaでは、一般的なのビッグデータプラトフォームのサポートはまだ足りないと思います。特にSparkとの連携です。現在、JuliaとSparkの連携のためのライブラリはSpark.jlというライブラリがありますが、サポートされる機能はまだ少ないです。特にDataframeとmllibはまだサポートされていません。

参照:http://dfdx.github.io/Spark.jl/api.html

Julia自体は並列処理の機能がありますがすでにHadoop上に格納されているデータを解析するにはちょっと不便だと思います。今後、JuliaとHadoopとSparkの統合できる機能やライブラリがあれば、もっと便利で、普及するではないと思います。

今後の展開は?

Juliaはまだまだ新しい言語ですが、言語の特徴やスピードから見ると、発展する可能性があると思います。特にデータ解析・分析分野、機械学習、AIなど、
今まで、POC(Proof of Concept)から、実際の本番環境にデプロイするまで、複数言語や複雑なアーキテクチャを使われている、コストが高いです。
Juliaで、1回だけをコード書き、そのまま本番環境にデプロイできるのはコストもかなり削減でできると思います。これからはサードパーティ大企業やコミュニティはJuliaに貢献し、もっと豊かかつ安定的な様々な機能を実現できれば、Juliaがもっと普及するではないかと思います。

最後に

次世代システム研究室では、アプリケーション開発や設計を行うアーキテクトを募集しています。アプリケーション開発者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ募集職種一覧からご応募をお願いします。