julia で、n×m の Array で平均等の計算の速度を上げる方法

先日、juliaDB は、早いという話しを書きました。しかしながら、Array を、しっかりと使いこなすと、Array はとても早いです。

先日の DataFrame を作ってみましょう。

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

この DataFrame の横方向の合計等を調べようというお話しです。

単純に、横方向の合計を求める場合は、まず、Array に変換して、sum(A::Array,2) とするだけで良いようです。

julia 1.0 の人は、sum(A::Array, dims=2) なので注意です。

julia> A = Array(df);

julia> sum(A,2) # julia 1.0 のひとは、"sum(A, dims=2)" です
100000×1 Array{Float64,2}:
  0.339824
 -0.711329
  5.8185
 -1.94555
  0.535156
 -0.670403
 -1.56872
  0.534236
 -1.98732
 -2.90369
  ⋮
略

これで1撃です。mean(), std()なども、この方法が使えます。

では、先日の関数、

 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

上記のかなり正直な組み方だと、10秒以上かかっていました。個人的には、現在、一番時間を取るのは、vcat() のところだと考えています。

これを組み変えてみます。

function summary_row_A(df::DataFrame)
          array_selected = Array(df[collect(df[:,2].>0) .& collect(df[:,2].>0),:])
          row_sum = sum(array_selected, 2)
          row_mean = mean(array_selected, 2)
          row_std = std(array_selected, 2)
          return DataFrame(sum=row_sum[1:end], mean=row_mean[1:end], std=row_std[1:end])
        end

こうなります。

では、これを動かしてみましょう。

julia> @time summary_row_A(df);
  0.075172 seconds (18.31 k allocations: 6.151 MiB)

julia> @time summary_row_A(df);
  0.007241 seconds (220 allocations: 5.213 MiB)

すばらしく早くなりました。Array の使い方は難しいですね。

では、10^6 個に増やしてやってみます。

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

julia> @time summary_row_A(df);
  0.099327 seconds (223 allocations: 51.933 MiB, 30.86% gc time)

julia> @time summary_row_A(df);
  0.213423 seconds (223 allocations: 51.933 MiB, 68.19% gc time)

julia> @time summary_row_A(df);
  0.071288 seconds (223 allocations: 51.933 MiB, 16.10% gc time)

とても早いです。

ようやく julia 1.0.0 の公式マニュアルから mean() などで、計算の方向が2番目の引数で指定できることが記載されるようになりました。しかし、こうも、Array と DataFrame で速度が異ると、どうしても csv 形式で保存したい時以外は、Array のまま、列名は別の Array か Dictionary でも良いか? なんて気になってしまいます。

B! LINE