裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

Julia のベクトル(その 1)

2021年02月08日 | ブログラミング

ベクトル

Julia では,配列(特に1次元のときはベクトル,2次元のときは行列とも呼ぶ)は [ ] で囲んで使用する。

v = [1, 2, 3, 4, 5];
v # 1, 2, 3, 4, 5

対話モードで実行する場合(RERL; Run Evaluate Print Loop と呼ばれる),行の最後に ';' が付いているとその行の評価結果を表示しない。C 言語などのように,行末に必ず必要な ';' ではない。

typeof(v) # Array{Int64,1}

typeof
関数は,オブジェクトの型を表示する。上のように小数点なしの数を ',' で区切って定義すると整数ベクトルになる。Array は配列,Int64 は要素が整数,1 は次元数が 1 すなわちベクトルであることを意味する。

ちなみに,配列の次元サイズは size() で求めることができる。

size(v) # (5,)

次元が 1 のときは,上の例のように示される。
最後の ',' は (5) だと数値の 5 がカッコでくくられたもの(数式)と区別がつかないのでこのように示される。
つまり,この場合の '( )' は数式の中での '( )' ではなく,これ全体がタプル(複数の要素のまとまり)という型であることを意味している。

要素に一つでも小数点がついていると実数ベクトルになる。

v2 = [1.0, 2, 3, 4, 5]
; # 1.0, 2.0, 3.0, 4.0, 5.0

整数ベクトルを実数ベクトルにするときは,以下のように型を指定して変換することができる。

v3 = Array{Float64,1}(v); # 1.0, 2.0, 3.0, 4.0, 5.0

Float64 で,実数型であることを示している。

typeof(v3) # Array{Float64,1}

または,

v4 = convert.(Float64, v); # 1.0, 2.0, 3.0, 4.0, 5.0

convert は最初の引数で示した型に変換する関数。'(' の前の '.' は,第 2 引数が複数の要素を持つ場合,それぞれの要素に作用することを指示する。もし,この '.' がないとエラーになる。

空白で区切ると横ベクトルになる。

v5 = [10 20 30 40 50];
# 1×5 Array{Int64,2}:
#  10  20  30  40  50

しかしこれは 1 行 n 列の配列(行列)である。typeof() で型を見ると次元数が 2 になっていることで確認できる。

typeof(v5) # Array{Int64,2}
size(v5)   # (1, 5)

v の定義のときのように ',' で区切って定義した縦ベクトルを n 行 1 列の配列(行列)にするには,配列の形を変える関数 reshape を使って次のようにする。

reshape(v, 5, 1) # 1, 2, 3, 4, 5

第 2,第 3 引数の 5, 1 は 結果として返す配列の第 1,第 2 次元の大きさを示すので,5 行 1 列の配列になる。
一般的には以下のようにするとよい。length(v)v の長さ,':' は 2 次元配列の場合は,1 次元目の大きさが決まれば自動的に決まるので,特に示す必要がないときに指定するとよい。

reshape(v, length(v), :)

逆に ' ' で区切った 1 行 n 列の配列をベクトルにしたいときには vec 関数を使う。

vec([10 20 30 40 50]) # 10, 20, 30, 40, 50

要素が文字列である配列(ベクトル,行列)も同じように定義できる。typeof 関数で示される String は要素が文字型であることを示している。

v8 = ["A", "B", "C", "D", "E"]; # "A", "B", "C", "D", "E"
typeof(v8) # Array{String,1}

Julia では,文字列は長さが 1 であっても,二重引用符で囲まなければならない。
以下のように,1 文字を一重引用符で囲むと文字列型ではなく文字型 Char になる。逆に言えば,長さが2以上の文字列を一重引用符で囲むとエラーになる。

v7 = ['1', 'A', 'b'];
# 3-element Array{Char,1}:
#  '1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)
#  'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
#  'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
typeof(v7) # Array{Char,1}

規則性のあるベクトル

等差数列

collect(1:1:10) # 1 〜 10,初項 1,公差 1; 第2引数が公差
collect(1:10) # 1 〜 10,初項 1,公差 1; 第2引数が1の場合は省略可
collect(10:-1:1) # 10 〜 1,初項 10,公差 -1; 降順の場合も公差は明示する
collect(1:5:20) # 1, 6, 11, 16,初項 1,公差 5; 第2引数が公差
collect(0:0.2:3) # 初項 0, 公差 0.2の数列; 最終項は 3

collect は要素を実際にメモリー上に確保する。
for i = 1:10 のような使い方(イテレータ)のときには,メモリー上に確保しない。

range(1, 10, step=2) # 1:2:9; イテレータを作成する
range(1, 10, length=20) # 1.0:0.47368421052631576:10.0
collect(range(1, 10, length=3)); # 1.0, 5.5, 10.0

繰り返し

repeat([0], 5) # 0 が 5個; repeat(0, 5) はエラーになる
repeat([0, 1, 2], 5) # 0, 1, 2 が 5 回繰り返されるベクトル
repeat(["foo", "bar", "baz"], 5) # "foo", "bar", "baz" が 5 回繰り返されるベクトル

A = repeat([10 20 30], inner=(1,2,1)) # 10  10  20  20  30  30
B = repeat([10 20 30], inner=(1,4,1)) # 10  10  10  10  20  20  20  20  30  30  30  30

inner を指定する repeat 関数,repeat(v, inner=(1, m, 1))1 × (length(v)×m) × 1 の3次元配列である。

size(A) # (1, 6, 1)
size(B) # (1, 12, 1)

vec 関数で,ベクトルに変換できる。

vec(A) # 10  10  20  20  30  30
vec(B) # 10  10  10  10  20  20  20  20  30  30  30  30

ゼロベクトル

zeros(5) # 実数 0.0 が 5 個のベクトル
zeros(Int64, 5) # 整数 0 が 5 個; 64 ビット環境では zeros(Int, 5) と同じ

1 ベクトル

ones(5) # 実数 1.0 が 5 個のベクトル; fill(1.0, 5) と同じ
ones(Int64, 5) # 整数 1 が 5 個; 64 ビット環境では zeros(Int, 5) と同じ

任意の要素の繰り返しを持つベクトル

fill(0, 5) # zeros(Int64, 5) と同じ
fill(1, 5) # ones(Int64, 5) と同じ
fill(0.0, 5) # zeros(5) と同じ
fill(1.0, 5) # ones(1) と同じ
fill("a", 5) # "a" が 5 個のベクトル

乱数ベクトル

using Random;
Random.seed!(12345); # 乱数の種の設定。本当に乱数が必要なときは使わない。

以下では,途中で乱数を使わないときには,示されたのと同じ結果になる。

無作為抽出

ベクトルから要素をランダムに取り出す。

rand(1:5, 10) # 3, 4, 1, 2, 2, 5, 5, 4, 5, 2; 要素 1〜5 から 10 個を無作為抽出したベクトル
rand(["foo", "bar", "baz"], 5) # "baz", "bar", "bar", "foo", "bar"

乱数

一様乱数を要素とする,長さ 3 のベクトル

rand(3) # 0.36007770681119133, 0.41511087872028374, 0.8325976767993883

正規乱数を要素とする,長さ  3 のベクトル

randn(3) # -0.55971756622102, -1.4990404969589635, 1.6739451586233716

要素の取得

Julia では,R と同様先頭要素のインデックスは 1 である。

v11 = [10, 20, 30, 40, 100]; # 10, 20, 30, 40, 100
v11[1] # 10
v11[begin] # 10; 'begin' はインデックス 1 を表す
v11[end] # 100; 'end' は最後のインデックスを表す
v11[end-2] # 30; 'begin', 'end' を算術演算してインデックスにできる
v11[[2,3,end]] # 20, 30, 100 # ベクトルで指定して要素を取り出すことができる
v11[[1,2,1,3,5,5]] # 10, 20, 10, 30, 100, 100; 何回でもどこからでも取り出せる
v11[1:2:end] # 10, 30, 100; イテレータで要素を取り出すことができる
v11[v11 .> 15] # 20, 30, 40, 100; 条件式(比較演算)を満たす要素を取り出せる

'.>''.' は複数の要素を持つオブジェクトの各要素に適用するために必要である。
なければエラーになる。

BitArray 型のベクトル

v11 .> 15 # BitArray 型のベクトル [0, 1, 1, 1, 1]

配列のインデックスに指定すると,1 に対応するオブジェクトの要素が取り出される。

v11[15 .< v11 .< 35] # 20, 30; 条件式(論理演算))を満たす要素を取り出せる

'15 .< v11 .< 35' '15 .< v11 .& v11 .< 35' と同じではない。

以下の 2 つは 同じことを表していそうに思うが,結果が違う。

v11[15 .< v11 .& v11 .< 35] # 20, 30; 両方の条件を満たす要素を取り出す。 & は and
v11[v11 .> 15 .& v11 .< 35] # 20, 30, 40, 100; 両方の条件を満たす要素を取り出す。 & は and

'15 .< v11 .< 35''15 .< v11 .& v11 .< 35' と同じになる。'.&' とすることに注意。
'15 .< v11''v11 .> 15' は同じ意味なので,
'v11[15 .< v11 .& v11 .< 35]''v11[v11 .> 15 .& v11 .< 35]' の結果が違うのは一見納得ができない。

実は,ここでの '&' はビット演算子であり,比較演算子 '.<''.>' より優先順位が高い。
'v11 .& v11''[10, 20 ,30, 40, 100]',で
'v11[15 .< [10, 20 ,30, 40, 100] .< 35]''[20, 30]' になる。

一方,'15 .& v11''[10, 4, 14, 8, 4]' で,
'v11[v11 .> [10, 4, 14, 8, 4] .< 35]''20, 30, 40, 100' になる。当然,予期せぬ間違った答えである。

以下のように,比較演算を先に行うようにカッコを補えば,正しい答えになる。

v11[(v11 .> 15) .& (v11 .< 35)] # 20, 30

また,'v11[15 .< v11 .& v11 .< 35]' もプログラムを書いた意図からは 'v11[(15 .< v11) .& (v11 .< 35)]' とすべきである。

以下のような単純な論理式の例だとわかりやすいかもしれない。

1 < 3 & 4 < 5 # false
(1 < 3) & (4 < 5) # true

1 > 3 | 5 < 6 # false
(1 > 3) | (5 < 6) # true

Python の場合も R の場合も,論理演算子のが出現する可能性のあるところにビット演算子も出現する可能性はないので,Julia のときのような誤解は生じないのである。

R にはビット演算はないので,'&' は論理演算子として解釈されるので,比較演算をカッコでくくらなくても正しい答えになる。

using RCall
R"""
v11 = c(10, 20, 30, 40, 100)
v11[v11 >= 15 & v11 <= 35] # 20, 30
"""

要素への代入

インデックスを指定してスカラー,ベクトルを代入することができる。

v = collect(1.:10);
v[1] = 10;
v[2:5] = [20, 30, 40 ,50];
v[9] = NaN;
v # 10.0  20.0  30.0  40.0  50.0  6.0  7.0  8.0  NaN  10.0

脇道

欠損値は 'missing' で表すが,上のようなベクトル v の要素として代入はできない。

'v[8] = missing' はエラーになる。

'missing' も許す型(たとえば 'Union{Missing, Int64}')のオブジェクトならば,可能である。

x = Array{Union{Missing, Int64},1}([3,2,1,4,3,5,missing,6]);
x[4] = missing;
x #  3, 2, 1, missing, 3, 5, missing, 6

たとえば,以下のようにすると 'missing' を初期値として持つ長さ 10 のベクトルを定義できる。

y = Array{Union{Missing, Float64},1}(undef, 10)

1, 4, 7, 10 番目の要素を二乗する。

for i =1:3:length(y)
  y[i] = i^2
end
y; # 1, 2, 1, 16, 5

'missing' を取り除いたベクトルを求める。

z = collect(skipmissing(y)); # 1, 4, 1, 16, 5
using Statistics # 単に mean を使うだけなのにこれが必要
mean(z) # 41.5; missing のないベクトルの平均値
mean(y) # missing
mean(skipmissing(y)) # 41.5; missing を除いたベクトルの平均値

Julia に NA はない。

NA; # UndefVarError: NA not defined

Julia の NaN は?

NaN # NaN
NaN == missing # missing
NaN * 10.3 # NaN
NaN * Inf # NaN
NaN & missing # MethodError: no method matching &(::Float64, ::Missing)

ベクトルの属性

a = [1, 2, 3, 4, 5];

長さ

length(a) # 5

検索

要素はあるか?

2 in a # true
9 in a # false

何個あるか?

b = [0, 1, 2, 1, 2, 3, 4, 3, 5, 2, 2, 1, 1, 5, 0];

count の第一引数は関数である。
比較演算式,論理演算式も関数なので,count の第 1 引数になれる。

5 < 10 # true
<(5, 10) # true

count(isequal(1), b) # 4; 1 と等しいか?
count(b .== 1) # 4; b は 1 と等しいか?
count(isequal.(b, 1)) # 4; b と 1 が等しいか? 等しければ 1 等しくなければ 0
count(iseven, b) # 7; 偶数か?
count(mod.(b, 2) .== 0) # 7; 偶数か?
count(isless.(b, 2)) # 6; b は 2 より小さいか? isgreater() はない
count(b .< 2) # 6; b は 2 より小さいか?

最大値,最小値は何か?どこにあるか?

argmax(b) # 9; 最大値が最初に現れるインデックス
b[argmax(b)] # 5; 最大値
maximum(b) # 5; 最大値
argmin(b) # 1; 最小値が最初に現れるインデックス
b[argmin(b)] # 0; 最小値
minimum(b) # 0; 最小値

ある値がどこにあるか?

findall 関数の第 1 引数は,関数である。論理演算子も関数である。λ関数(無名関数)でもよい。

findall(==(1), b); # 2, 4, 12, 13; 1 と等しい要素のインデックス
findall(==(b[argmax(b)]), b) # 9, 14; 最大値が現れる全てのインデックス
findall(==(minimum(b)), b) # 9, 14; 最大値が現れる全てのインデックス
findall(==(b[argmin(b)]), b) # 1, 15; 最小値が現れる全てのインデックス
findall(==(minimum(b)), b) # 1, 15; 最小値が現れる全てのインデックス

findall(isodd, b) # 2, 4, 6, 8, 9, 12, 13, 14; 要素が奇数のインデックス
b[findall(isodd, b)] # 1, 1, 3, 3, 5, 1, 1, 5; その要素の列挙
findall(x -> 2 < x < 4, b) # 6, 8; 要素を x として 2 < x < 4 を満たす要素のインデックス(λ関数)
b[findall(x -> 2 < x < 4, b)] # 3, 3
c = findall(x -> sqrt(x) == floor(Int, sqrt(x)), b) # 1, 2, 4, 7, 12, 13, 15; 要素が平方数であるインデックス
b[c] # 0, 1, 1, 4, 1, 1, 0; その要素

コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« Julia で注意しておかないと... | トップ | Julia のベクトル(その 2) »
最新の画像もっと見る

コメントを投稿

ブログ作成者から承認されるまでコメントは反映されません。

ブログラミング」カテゴリの最新記事