DataFramesMeta, JuliaDB, Queryverseをそれぞれ触ってみた
Juliaでデータ処理
データ処理といえばR (& tidyverse)やPython (& Pandas)が思い浮かびますが、Juliaでも同様のデータ処理のパッケージが存在します。
今回はDataFramesMeta.jl、JuliaDB, Queryverseのそれぞれで典型的な処理をやってみます。
tidyverseやPandasのような完成度はありませんが、簡単な処理だけでいいのであればこなせる印象です。
RとPythonで書いた例
まずはRとPythonで例を示します。
データセットはおなじみのiris
を使います。
library(dplyr)
iris %>%
dplyr::filter(Sepal.Length <= 6) %>%
dplyr::group_by(Species) %>%
dplyr::summarise(
SumMeanSLandPL = mean(Sepal.Length) + mean(Petal.Length),
stdSL = sd(Sepal.Length),
stdPL = sd(Petal.Length)
)
# A tibble: 3 x 4
Species SumMeanSLandPL stdSL stdPL
<fct> <dbl> <dbl> <dbl>
1 setosa 6.47 0.352 0.174
2 versicolor 9.64 0.305 0.462
3 virginica 10.7 0.335 0.201
上のコードは「条件による行の選択(dplyr::filter
)」、「集約と演算(dplyr::group_by
, dplyr::summarise
)」のを行う例となっています。
Pythonで同様な処理を行う例は以下です。(あまり美しくない書き方かも)
import pandas as pd
import seaborn as sns
iris = sns.load_dataset("iris")
df = iris[iris["sepal_length"] <= 6].groupby(
"species",
as_index = False
).agg(
{"sepal_length":[np.mean, np.std],
"petal_length":[np.mean, np.std]}
).assign(
SumMeanSLandPL = lambda df: df[("sepal_length", "mean")] + df[("petal_length", "mean")]
).drop([("sepal_length", "mean"), ("petal_length", "mean")],
axis=1
).rename(
columns = {"sepal_length": "stdSL", "petal_length": "stdPL"}
)
df.columns = df.columns.droplevel(1)
print(df)
species stdSL stdPL SumMeanSLandPL
0 setosa 0.352490 0.173664 6.468000
1 versicolor 0.305053 0.462141 9.636667
2 virginica 0.334581 0.200693 10.677778
Juliaで書いてみる
今回のJuliaの環境と、それぞれのパッケージのバージョンは以下の通りです。
つい先日v1.0.0がリリースされましたが、以下のコードがv1.0.0で動く保証は全くありません笑
julia> versioninfo()
Julia Version 0.6.2
Commit d386e40c17 (2017-12-13 18:08 UTC)
Platform Info:
OS: macOS (x86_64-apple-darwin14.5.0)
CPU: Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
WORD_SIZE: 64
BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell)
LAPACK: libopenblas64_
LIBM: libopenlibm
LLVM: libLLVM-3.9.1 (ORCJIT, skylake)
julia> Pkg.status("DataFramesMeta")
- DataFramesMeta 0.3.0
julia> Pkg.status("JuliaDB")
- JuliaDB 0.8.4
julia> Pkg.status("Queryverse")
- Queryverse 0.0.1
データセットのiris
はDataFrames.jl
のパッケージに入ってるものをそのまま使います。
julia> using DataFrames, CSV
julia> filepath_iris = joinpath(Pkg.dir("DataFrames"), "test/data/iris.csv")
julia> df_iris = CSV.read(filepath_iris)
DataFramesMeta
DataFramesMeta.jlはDataFrames.jlを、マクロを使って拡張するメタプログラミングツールです。
julia> using DataFramesMeta
julia> @linq df_iris |>
where(:SepalLength .<= 6) |>
by(:Species,
MeanSL = mean(:SepalLength), StdSL = std(:SepalLength),
MeanPL = mean(:PetalLength), StdPL = std(:PetalLength)) |>
transform(SumMeanSLandPL = :MeanSL + :MeanPL) |>
select(:Species, :SumMeanSLandPL, :StdSL, :StdPL) |>
orderby(:SumMeanSLandPL)
3×4 DataFrames.DataFrame
│ Row │ Species │ SumMeanSLandPL │ StdSL │ StdPL │
├─────┼────────────┼────────────────┼──────────┼──────────┤
│ 1 │ setosa │ 6.468 │ 0.35249 │ 0.173664 │
│ 2 │ versicolor │ 9.63667 │ 0.305053 │ 0.462141 │
│ 3 │ virginica │ 10.6778 │ 0.334581 │ 0.200693 │
JuliaDB
JuliaDBはJulia Computingによって開発が進められているパッケージで、スパースなデータの取り扱いや分散処理の平易さあたりをウリにしているそうです。
ドキュメントが充実しているのはとてもありがたいです。
julia> using JuliaDB
julia> using Lazy
julia> filepath_iris = joinpath(Pkg.dir("DataFrames"), "test/data/iris.csv")
julia> tb_iris = loadtable(filepath_iris)
julia> @as t tb_iris begin
filter(x -> x<=6, t, select = :SepalLength)
JuliaDB.groupby(
@NT(
meanSL = :SepalLength => mean,
stdSL = :SepalLength => std,
meanPL = :PetalLength => mean,
stdPL = :PetalLength => std),
t, :Species)
setcol(t, :SumMeanSLandPL, map(x -> x.meanSL + x.meanPL, t))
select(t, (:Species, :SumMeanSLandPL, :stdPL, :stdSL))
renamecol(t, :Species => :Class)
sort(t, :SumMeanSLandPL)
end
Table with 3 rows, 4 columns:
Class SumMeanSLandPL stdPL stdSL
────────────────────────────────────────────────
"setosa" 6.468 0.173664 0.35249
"versicolor" 9.63667 0.462141 0.305053
"virginica" 10.6778 0.200693 0.334581
最後の@as
はLazy
パッケージによる遅延評価を用いています。また、JuliaDBとLazyでgroupby
関数がコンフリクトしているので上の例ではJuliaDB.groupby
と書いています。
Queryverse
Queryverseは1つのパッケージではなく、いくつかのパッケージをまとめたメタパッケージを指します。
Queryverseの詳細は作者のチュートリアル動画が詳しいです。
julia> using Queryverse
julia> filepath_iris = joinpath(Pkg.dir("DataFrames"), "test/data/iris.csv")
julia> df_iris = load(filepath_iris)
julia> df_iris |>
@filter(_.SepalLength <= 6) |>
@groupby(_.Species) |>
@map({Species = (_..Species)[1],
meanSL = mean(_..SepalLength),
stdSL = sqrt.(var(_..SepalLength)), # stdSL = std(_..SepalLength)だとなぜかエラー
meanPL = mean(_..PetalLength),
stdPL = sqrt.(var(_..PetalLength)) # stdPL = std(_..PetalLength)だとなぜかエラー
}) |>
@map({Species = _.Species,
SumMeanSLandPL = _.meanSL + _.meanPL,
stdSL = _.stdSL,
stdPL = _.stdPL
})
3x4 query result
Species │ SumMeanSLandPL │ stdSL │ stdPL
───────────┼────────────────┼──────────┼─────────
setosa │ 6.468 │ 0.35249 │ 0.173664
versicolor │ 9.63667 │ 0.305053 │ 0.462141
virginica │ 10.6778 │ 0.334581 │ 0.200693
QueryverseはDataFramesMetaと同様にパイプ|>
を使ってデータを変形していくことができます。ただし、上記コードのコメントにもあるように@map
の中でstd()
では動かずsqrt.(var())
では動くというような謎な挙動が残っていたりします…。(将来的に修正されるといいのですが)
ちなみにここでは紹介していませんが、QueryverseにはDataVoyagerとの連携が組み込まれているので多様なデータ可視化が可能です。
おわりに
どのパッケージも一長一短で、この中のどれかがtidyverseやPandasのようなスタンダードになるにはまだまだ時間がかかるかと思います。
機能も限定的で、業務用途に耐えうるようなデータ分析基盤としてはJuliaはまだ厳しい印象です。
ただし、大規模なデータに対する高速な計算はJuliaの強みが活かされる部分だと思うので、(JITコンパイルの時間が探索的データ分析と相性が悪いとはいえ、)これからも注視していきたい分野です。
Juliaを数値計算に用いている人は少なくないはずなので、そういった数値計算の結果の集計等にうまく使っていくところがまずは第一歩になるのではないかと思います。