LoginSignup
4
2

More than 5 years have passed since last update.

『機械学習のエッセンス(http://isbn.sbcr.jp/93965/)』のPythonサンプルをJuliaで書き換えてみる。(第04章03配列の基本計算)

Posted at

はじめに

『機械学習のエッセンス(http://isbn.sbcr.jp/93965/)』のPythonサンプルをJuliaで書き換えてみる。(第04章02NumPyの基本)の続きです。

いろいろな集計関数

  • 合計(sum)
  • 最大値(maximum)
  • 最小値(minimum)

Juliaでは配列の最大値、最小値はmaxminではなくそれぞれmaximumminimumでした。

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)

meanBaseモジュールではなく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だと最初のv3 x 1の縦ベクトルなのでwuと同じです。)
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
4
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2