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 でも良いか? なんて気になってしまいます。