はじめに
『機械学習のエッセンス(http://isbn.sbcr.jp/93965/)』のPythonサンプルをJuliaで書き換えてみる。(第04章02NumPyの基本)の続きです。
いろいろな集計関数
- 合計(sum)
- 最大値(maximum)
- 最小値(minimum)
Juliaでは配列の最大値、最小値はmax
、min
ではなくそれぞれmaximum
、minimum
でした。
julia> a = collect(0:4)
5-element Array{Int64,1}:
0
1
2
3
4
julia> sum(a)
10.0
julia> maximum(a)
4.0
julia> minimum(a)
0.0
- 平均(mean)
mean
はBase
モジュールではなくStatistics
モジュールで定義されています。
julia> using Statistics
julia> mean(a)
2.0
2次元配列の合計
julia> b = reshape(collect(0.:8.), 3, 3)
3×3 Array{Float64,2}:
0.0 3.0 6.0
1.0 4.0 7.0
2.0 5.0 8.0
julia> sum(b)
36.0
- 列ごとの合計
- 行ごとの合計
Juliaのreshape
はPythonと見た目が転置状態なので、それぞれ本とは値が逆になっています。またaxis
ではなくdims
で指定するようです。
julia> sum(b, dims=1)
1×3 Array{Float64,2}:
3.0 12.0 21.0
julia> sum(b, dims=2)
3×1 Array{Float64,2}:
9.0
12.0
15.0
ブロードキャスト
julia> a = collect(3.:7.)
5-element Array{Float64,1}:
3.0
4.0
5.0
6.0
7.0
julia> exp(a)
ERROR: MethodError: no method matching exp(::Array{Float64,1})
Closest candidates are:
exp(::BigFloat) at mpfr.jl:558
exp(::Missing) at math.jl:1070
exp(::Complex{Float16}) at math.jl:1019
Juliaでは直接関数を指定してもブロードキャストしないようです。明示的に指定するbroadcast(Base.Broadcast.broadcast )
という関数がありました。
あるいは、もっと簡単に関数に.
(ドット)を付けるとできます。
julia> broadcast(exp, a)
5-element Array{Float64,1}:
20.085536923187668
54.598150033144236
148.4131591025766
403.4287934927351
1096.6331584284585
julia> exp.(a)
5-element Array{Float64,1}:
20.085536923187668
54.598150033144236
148.4131591025766
403.4287934927351
1096.6331584284585
julia> broadcast(log, a)
5-element Array{Float64,1}:
1.0986122886681098
1.3862943611198906
1.6094379124341003
1.791759469228055
1.9459101490553132
julia> log.(a)
5-element Array{Float64,1}:
1.0986122886681098
1.3862943611198906
1.6094379124341003
1.791759469228055
1.9459101490553132
julia> broadcast(sqrt, a)
5-element Array{Float64,1}:
1.7320508075688772
2.0
2.23606797749979
2.449489742783178
2.6457513110645907
julia> sqrt.(a)
5-element Array{Float64,1}:
1.7320508075688772
2.0
2.23606797749979
2.449489742783178
2.6457513110645907
julia> b = reshape(collect(0.:8.), 3, 3)
3×3 Array{Float64,2}:
0.0 3.0 6.0
1.0 4.0 7.0
2.0 5.0 8.0
julia> broadcast(exp, b)
3×3 Array{Float64,2}:
1.0 20.0855 403.429
2.71828 54.5982 1096.63
7.38906 148.413 2980.96
julia> exp.(b)
3×3 Array{Float64,2}:
1.0 20.0855 403.429
2.71828 54.5982 1096.63
7.38906 148.413 2980.96
配列とスカラの演算
やはり+
でも直接はダメですね。
julia> a = collect(0:4)
5-element Array{Int64,1}:
0
1
2
3
4
julia> a + 3
ERROR: MethodError: no method matching +(::Array{Int64,1}, ::Int64)
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...) at operators.jl:502
+(::Complex{Bool}, ::Real) at complex.jl:292
+(::Missing, ::Number) at missing.jl:93
...
Stacktrace:
[1] top-level scope at none:0
下記のようにbroadcast
に演算子と引数を指定するか、演算子に.
(ドット)を付けるとできます。
julia> broadcast(+, 3, a)
5-element Array{Int64,1}:
3
4
5
6
7
julia> a .+ 3
5-element Array{Int64,1}:
3
4
5
6
7
julia> broadcast(*, 3, a)
5-element Array{Int64,1}:
0
3
6
9
12
julia> a .* 3
5-element Array{Int64,1}:
0
3
6
9
12
julia> broadcast(^, 2, a)
5-element Array{Int64,1}:
1
2
4
8
16
julia> a .^ 2
5-element Array{Int64,1}:
0
1
4
9
16
- 条件式の場合
演算子のように.
(ドット)を付けてできます。
julia> a .>= 2
5-element BitArray{1}:
false
false
true
true
true
julia> a .!= 3
5-element BitArray{1}:
true
true
true
false
true
julia> b = reshape(collect(0:8), 3, 3)
3×3 Array{Int64,2}:
0 3 6
1 4 7
2 5 8
julia> b .> 3
3×3 BitArray{2}:
false false true
false true true
false true true
ブール値を要素として持つ配列の演算
julia> a = [10, 20, 30, 40]
4-element Array{Int64,1}:
10
20
30
40
julia> b = [false, true, true, false]
4-element Array{Bool,1}:
false
true
true
false
julia> a[b]
2-element Array{Int64,1}:
20
30
julia> c = [3 4 5; 6 7 8]
2×3 Array{Int64,2}:
3 4 5
6 7 8
julia> d = [false false true; false true true]
2×3 Array{Bool,2}:
false false true
false true true
julia> c[d]
3-element Array{Int64,1}:
7
5
8
最後の結果がPythonと順番が違います。本ではarray([5, 7, 8])
です。Juliaの場合、行列の順番通りに数えるようです。ここでは7と5と8がそれぞれ2行3列、3行1列、3行2列の順で配列を出力しています。
ちなみに、Pythonと同じように配列の配列で書くと下記のようにエラーになってしまいました。
julia> e = [[3,4,5],[6,7,8]]
2-element Array{Array{Int64,1},1}:
[3, 4, 5]
[6, 7, 8]
julia> f = [[false,false,true],[false,true,true]]
2-element Array{Array{Bool,1},1}:
[false, false, true]
[false, true, true]
julia> e[f]
ERROR: ArgumentError: invalid index: Array{Bool,1}[[false, false, true], [false, true, true]] of type Array{Array{Bool,1},1}
Stacktrace:
[1] to_index(::Array{Array{Bool,1},1}) at ./indices.jl:263
[2] to_index(::Array{Array{Int64,1},1}, ::Array{Array{Bool,1},1}) at ./indices.jl:247
[3] to_indices at ./indices.jl:294 [inlined]
[4] to_indices at ./indices.jl:291 [inlined]
[5] getindex(::Array{Array{Int64,1},1}, ::Array{Array{Bool,1},1}) at ./abstractarray.jl:905
[6] top-level scope at none:0
条件を指定した要素の抽出
julia> a = collect(0:9)
10-element Array{Int64,1}:
0
1
2
3
4
5
6
7
8
9
julia> a[a .> 5]
4-element Array{Int64,1}:
6
7
8
9
- 条件式の
&
と|
配列に.
(ドット)付きの演算子を適用するとBitArray
という配列が返ってきます。配列の演算なので&
をそのまま使うとエラーになります。複数の条件の場合は同じく.
を付けます。
julia> a .>= 3
10-element BitArray{1}:
false
false
false
true
true
true
true
true
true
true
julia> a .< 6
10-element BitArray{1}:
true
true
true
true
true
true
false
false
false
false
julia> (a .>= 3) & (a .< 6)
ERROR: MethodError: no method matching &(::BitArray{1}, ::BitArray{1})
Closest candidates are:
&(::Any, ::Any, ::Any, ::Any...) at operators.jl:502
Stacktrace:
[1] top-level scope at none:0
julia> (a .>= 3) .& (a .< 6)
10-element BitArray{1}:
false
false
false
true
true
true
false
false
false
false
julia> a[(a .>= 3) .& (a .< 6)]
3-element Array{Int64,1}:
3
4
5
julia> a[(a .< 2) .| (a .> 7)]
4-element Array{Int64,1}:
0
1
8
9
julia> a[a .% 3 .!= 0]
6-element Array{Int64,1}:
1
2
4
5
7
8
配列の演算
配列同士の演算
julia> u = collect(0:3)
4-element Array{Int64,1}:
0
1
2
3
julia> v = collect(3:6)
4-element Array{Int64,1}:
3
4
5
6
配列同士ですが、.
がなくても+
と-
は問題なくできました。
.
を付けても同じ結果です。
julia> u + v
4-element Array{Int64,1}:
3
5
7
9
julia> u .+ v
4-element Array{Int64,1}:
3
5
7
9
julia> u - v
4-element Array{Int64,1}:
-3
-3
-3
-3
julia> u .- v
4-element Array{Int64,1}:
-3
-3
-3
-3
*
は.
なしだとエラーとなりました。.
を付けるとできます。
julia> u * v
ERROR: MethodError: no method matching *(::Array{Int64,1}, ::Array{Int64,1})
Closest candidates are:
*(::Any, ::Any, ::Any, ::Any...) at operators.jl:502
(略)
julia> u .* v
4-element Array{Int64,1}:
0
4
10
18
- ベクトルの内積
dot
関数はLinearAlgebra
モジュールにあります。あるいは⋅
(cdot)演算子を使います。⋅
はREPL上で\cdot
と打ってTABキーで変換できます。(⋅
もusing LinearAlgebra
を実行しておかないと使えません。)
julia> sum(u .* v)
32
julia> using LinearAlgebra
julia> dot(u, v)
32
julia> u ⋅ v
32
2次元配列の演算
ここの計算は本の表示と合わせるためにreshape
関数のあとに'
を付けて転置させました。
julia> a = reshape(collect(0.:8.),3, 3)'
3×3 Adjoint{Float64,Array{Float64,2}}:
0.0 1.0 2.0
3.0 4.0 5.0
6.0 7.0 8.0
julia> b = reshape(collect(4.:12.), 3, 3)'
3×3 Adjoint{Float64,Array{Float64,2}}:
4.0 5.0 6.0
7.0 8.0 9.0
10.0 11.0 12.0
-
+
と-
は.
をつけなくても計算できますが、他と合わせて.
を付けました。
julia> a .+ b
3×3 Array{Float64,2}:
4.0 6.0 8.0
10.0 12.0 14.0
16.0 18.0 20.0
julia> a .- b
3×3 Array{Float64,2}:
-4.0 -4.0 -4.0
-4.0 -4.0 -4.0
-4.0 -4.0 -4.0
julia> a .* b
3×3 Array{Float64,2}:
0.0 5.0 12.0
21.0 32.0 45.0
60.0 77.0 96.0
julia> a ./ b
3×3 Array{Float64,2}:
0.0 0.2 0.333333
0.428571 0.5 0.555556
0.6 0.636364 0.666667
-
dot
関数
NumPyのdot
は行列積となっていますが、Juliaでdot
を使ってみたところ結果が違いました。
julia> using LinearAlgebra
julia> dot(a, b)
348.0
これはsum(a .* b)
の結果と同じです。これは内積です。
julia> sum(a .* b)
348.0
行列積を計算したいときは*
を使うようです。.
をつけて.*
とすると行列のそれぞれの要素をかけ算しますが、*
だけだと行列の積になります。
julia> a * b
3×3 Array{Float64,2}:
27.0 30.0 33.0
90.0 102.0 114.0
153.0 174.0 195.0
+
と-
はそれぞれの要素をプラス、マイナスするので.
を付けても付けなくても結果が同じだったということですね。
Juliaの場合、行列のそれぞれの要素で計算したいときは演算子に.
を付ける。行列として計算したいときは演算子に.
を付けないようです。
形状の違う行列の積
ここは行列の形状の話なので、本には合わせずJuliaのそのままの出力で行います。本とはエラーなどの順番が違います。
julia> a = reshape(collect(0.:8.), 3, 3)
3×3 Array{Float64,2}:
0.0 3.0 6.0
1.0 4.0 7.0
2.0 5.0 8.0
julia> v = collect(1.:3.)
3-element Array{Float64,1}:
1.0
2.0
3.0
3 x 3
行列と3 x 1
行列(縦ベクトル)との計算になります。
- 積を求める。
3 x 3
行列*
3 x 1
行列 =3 x 1
行列
julia> a * v
3-element Array{Float64,1}:
24.0
30.0
36.0
- 引数を逆にする。
3 x 1
行列*
3 x 3
行列→エラー
julia> v * a
ERROR: DimensionMismatch("A has dimensions (3,1) but B has dimensions (3,3)")
Stacktrace:
(略)
-
v
を転置させて形状を1 x 3
行列に
julia> u = v'
1×3 LinearAlgebra.Adjoint{Float64,Array{Float64,1}}:
1.0 2.0 3.0
julia> a * u
ERROR: DimensionMismatch("A has dimensions (3,3) but B has dimensions (1,3)")
Stacktrace:
julia> u * a
1×3 LinearAlgebra.Adjoint{Float64,Array{Float64,1}}:
8.0 26.0 44.0
-
v
の形状を1 x 3
行列に。(Juliaだと最初のv
が3 x 1
の縦ベクトルなのでw
はu
と同じです。)
julia> w = reshape(v, 1,:)
1×3 Array{Float64,2}:
1.0 2.0 3.0
julia> w * a
1×3 Array{Float64,2}:
8.0 26.0 44.0
julia> u * a
1×3 Adjoint{Float64,Array{Float64,1}}:
8.0 26.0 44.0
配列同士の演算におけるブロードキャスト
ここではJuliaのそのままの出力とします。a
の表示が本と転置状態なので計算結果は本と違います。
julia> a = reshape(collect(0.:11.), 4, 3)
4×3 Array{Float64,2}:
0.0 4.0 8.0
1.0 5.0 9.0
2.0 6.0 10.0
3.0 7.0 11.0
julia> b = reshape(collect(0.:2.), 1, 3)
1×3 Array{Float64,2}:
0.0 1.0 2.0
julia> c = reshape(collect(0.:3.), 4, 1)
4×1 Array{Float64,2}:
0.0
1.0
2.0
3.0
-
+
そのままではエラー
形状が一致しないときは.
を付けないとエラーになりました。
julia> a + b
ERROR: DimensionMismatch("dimensions must match")
.
を付ける
julia> a .+ b
4×3 Array{Float64,2}:
0.0 5.0 10.0
1.0 6.0 11.0
2.0 7.0 12.0
3.0 8.0 13.0
julia> a .* c
4×3 Array{Float64,2}:
0.0 0.0 0.0
1.0 5.0 9.0
4.0 12.0 20.0
9.0 21.0 33.0
julia> b .- c
4×3 Array{Float64,2}:
0.0 1.0 2.0
-1.0 0.0 1.0
-2.0 -1.0 0.0
-3.0 -2.0 -1.0
2次元配列と1次元配列の演算
julia> a = reshape(collect(0.:11.), 4, 3)
4×3 Array{Float64,2}:
0.0 4.0 8.0
1.0 5.0 9.0
2.0 6.0 10.0
3.0 7.0 11.0
julia> v = reshape(collect(0.:2.), 1, 3)
1×3 Array{Float64,2}:
0.0 1.0 2.0
julia> a .+ v
4×3 Array{Float64,2}:
0.0 5.0 10.0
1.0 6.0 11.0
2.0 7.0 12.0
3.0 8.0 13.0