爆速の JuliaDB

2018年11月現在で、JuliaDB は julia 1.0.x では動きません。下記は、julia 0.6.x を使っていますので、注意して下さい。

JuliaDB について

JuliaDB のドキュメントのはじめには、 "JuliaDBは、Juliaのフルパワーで、あらゆる大きさのデータを扱うため多彩なデータ構造を提供します。" と記載されています。

めざした点は4つ

  1. 多次元データセットを迅速に段階的に読み込む
  2. データにインデックスを付けて、フィルター、集約、ソート、および結合操作を行う
  3. 結果を記録し、後に効率的に読み込む
  4. Juliaの組み込み並列処理を簡単に使用して、マシンまたはクラスタを完全に活用する
と述べられています。

前回のブログで、JuliaDB に少しだけ触りました。もう少し、触ってみましょう。

自分の使用方法だと、速度が最大の関心事です。

DataFrame で、row 毎の平均を求めてみよう

10000×3 の大きさの DataFrame を作ります。1列目と2列目が正の行を選び、行の平均を求めます。@time は、関数にしないと測れないので、関数を定義して測定します。2018年5月末に julia 0.63 が出たので、0.63 で測定してみます。

  julia> df = DataFrame(
           col1 = randn(10^5),
           col2 = randn(10^5),
           col3 = randn(10^5)
         );

 julia> function summary_row(df::DataFrame)
          array_selected = Array(df[collect(df[:,2].>0) .& collect(df[:,2].>0),:])
          row_sum=Array{Float64,1}[]
          row_mean=Array{Float64,1}[]
          for i in 1:size(array_selected)[1]
            if i==1
              row_sum = sum(array_selected[i,:])
              row_mean = mean(array_selected[i,:])
            else
              row_sum = vcat(row_sum, sum(array_selected[i,:]) )
              row_mean = vcat(row_mean, mean(array_selected[i,:]) )
            end
          end
          return DataFrame(sum=row_sum, mean=row_mean)
        end

  summary_row (generic function with 1 method)

  julia> @time summary_row(df);

  17.466397 seconds (7.64 M allocations: 18.567 GiB, 11.95% gc time)
  49569×2 DataFrames.DataFrame
  49569×2 DataFrames.DataFrame
  │ Row   │ sum       │ mean      │
  ├───────┼───────────┼───────────┤
  │ 1     │ 3.48641   │ 1.16214   │
  │ 2     │ -0.868312 │ -0.289437 │
  │ 3     │ 0.383235  │ 0.127745  │
  │ 4     │ -1.03704  │ -0.345681 │
  │ 5     │ -1.59083  │ -0.530275 │
  ...以下略

  julia> for i in 1:5
           @time summary_row(df)
         end
 14.441185 seconds (5.75 M allocations: 18.846 GiB, 15.22% gc time)
 14.330333 seconds (5.75 M allocations: 18.846 GiB, 14.46% gc time)
 14.372350 seconds (5.75 M allocations: 18.846 GiB, 14.41% gc time)
 14.360536 seconds (5.75 M allocations: 18.846 GiB, 14.48% gc time)
 14.288630 seconds (5.75 M allocations: 18.846 GiB, 14.55% gc time)

sum() が Array() をかぶせないと動かないところが悲しい感じです。なお、せっかく Array にするなら、Statistics.mean などを上手に使うと、実は、うんと早くなります。julia で、n×m の Array で平均等の計算の速度を上げる方法を、見て下さい。

Array(df[collect(df[:,2].>0) .& collect(df[:,2].>0),:]) は、 50045×3 Array{Float64,2} の大きさですした。julia の速度なら、お手軽な大きさです。

ちなみに、Query.jl では、sum() をかぶせるような計算は上手く動きませんでした。

JuliaDB を使ってみよう

では、JuliaDB を使ってみましょう。106行で挑戦します。 @time を使いたいので、やはり関数化をしています。

  julia> using JuliaDB

  julia> t = table(randn(10^6), randn(10^6), randn(10^6),  names=[:a, :b, :c])

  Table with 1000000 rows, 3 columns:
  a           b          c
  ─────────────────────────────────
  0.498796    -2.27555   -0.241127
  -0.84204    1.3747     1.13924
  -0.3349     0.297729   -0.0748025
  -0.498936   0.904679   2.05926
  -1.45501    1.36358    2.97176
  ...略

  julia> function DB_summary(t)
           t2 = filter(p -> p.a>0 && p.b>0 , t);
           table(sum.(rows(t2)), mean.(rows(t2)), std.(rows(t2)), names=[:sum, :mean, :sd])
         end
  DB_summary (generic function with 1 method)

  julia> @time DB_summary(t)
    4.354405 seconds (3.64 M allocations: 158.669 MiB, 3.96% gc time)

  Table with 250428 rows, 3 columns:
  sum        mean       sd
  ──────────────────────────────
  2.71225    0.904085   0.964576
  0.37264    0.124213   0.925363
  0.0399666  0.0133222  0.715164
  5.58788    1.86263    2.56691
  3.90184    1.30061    0.538544
  ...略

  julia> for i in 1:5
           @time DB_summary(t)
         end

  0.292617 seconds (2.25 M allocations: 85.075 MiB, 10.44% gc time)
  0.273872 seconds (2.25 M allocations: 85.032 MiB, 4.34% gc time)
  0.387218 seconds (2.25 M allocations: 85.032 MiB, 34.39% gc time)
  0.249349 seconds (2.25 M allocations: 85.032 MiB, 3.37% gc time)
  0.255571 seconds (2.25 M allocations: 85.032 MiB, 4.18% gc time)

なんと、DataFrame と Array を組み合わせて計算する方法よりも 100 倍くらい早いです。 しかも、sd まで計算していただいております。 rows() が良い仕事をしています。

先程の DataFrame の場合と、条件をそろえてみましょう。

  julia> t = table(randn(10^5), randn(10^5), randn(10^5),  names=[:a, :b, :c]);

  julia> for i in 1:5
         @time DB_summary(t)
       end

  0.029004 seconds (226.03 k allocations: 8.529 MiB)
  0.034718 seconds (226.03 k allocations: 8.529 MiB, 19.24% gc time)
  0.026672 seconds (226.03 k allocations: 8.529 MiB)
  0.026905 seconds (226.03 k allocations: 8.529 MiB)
  0.031421 seconds (226.03 k allocations: 8.529 MiB, 20.22% gc time)

やはり100倍早いですね。

まとめ

JuliaDB は上手に使ったら、超高速の表計算ができる可能性があります。あきらめていた何かが、新しいことが、できるかもしれません。 自分は、R から julia に乗り換えた時に、今まではあきらめていた鬼のような計算の繰り返しができるようになりました。 万人にとって juliaDB の可読性が良いかは、今後の普及次第なのだろうと考えます。

B! LINE