多くのプログラミング言語には、存在しない値を表すためにnullやnoneのような仕組みがあります。
Juliaは大量の(しばしば雑然とした)データを扱えるように設計されているため、「何もないこと」を表す形が複数用意されています。
全体のねらいは、見つかった欠損値や疑わしい値をその場で示し、例外を発生させずに処理を続けることです。
nothing値が本当に存在しない場合、それはnothingで表されます。これはおそらくC(NULL)やPython(None)の動作に最も近いものです。
julia> n = nothing
julia> isnothing(n)
true
julia> typeof(n)
Nothing
つまりnothingは型Nothingの単一の値で、それかどうかをテストできます。
nothingのよくある使い方の1つは、副作用(出力、ネットワーク設定など)のためだけに使われる関数の戻り値(実際には何も返さない値)としてです。
missing理論上は値が存在するものの、それが何かわからない場合にはmissingを使います。
たとえば、道路を走る車を数えるとき、人間の観測者が休憩を取る必要があったり、自動センサーが故障したりしても、交通の流れは続いています。
このようにmissingはプレースホルダーであり、データの欠けをどう扱うかを人間が決める必要があることを警告します。
julia> mv = [1, 2, missing]
3-element Vector{Union{Missing, Int64}}:
1
2
missing
julia> typeof(mv)
Vector{Union{Missing, Int64}} (alias for Array{Union{Missing, Int64}, 1})
julia> ismissing.(mv) # broadcast function, displays as 1 for true, 0 for false
3-element BitVector:
0
0
1
この機能を標準で備えている言語はほとんどありませんが、近いものとしてRのNAやSQLのNULLがあります。
missing値が含まれている場合、式は通常デフォルトでmissingを返します。
これらの値を無視したい場合は、skipmissing()関数を使ってその意図を明示します。
julia> mv = [1, 2, missing]
3-element Vector{Union{Missing, Int64}}:
1
2
missing
julia> sum(mv) # missing in, missing out
missing
julia> skipmissing(mv)
skipmissing(Union{Missing, Int64}[1, 2, missing])
julia> collect(skipmissing(mv))
2-element Vector{Int64}:
1
2
julia> sum(skipmissing(mv)) # functions like sum() can work with iterators
3
skipmissingはイテレーターを作るので、ベクトルが必要な場合はcollect()で包みます。
missing値を何らかのデフォルト値で置き換えると便利なこともあります。
@coalesce()マクロがこれに便利で、最初のmissing以外の値(他になければmissing)を返します。
julia> str = ["I", "exist", missing]
3-element Vector{Union{Missing, String}}:
"I"
"exist"
missing
julia> [@coalesce(s, "-") for s in str]
3-element Vector{String}:
"I"
"exist"
"-"
NaN「Not a Number」の略であるNaNは、数値が期待される場面で計算上の問題が起きたことを示します。
julia> v = [0, 1, -1]
3-element Vector{Int64}:
0
1
-1
julia> v / 0
3-element Vector{Float64}:
NaN
Inf
-Inf
julia> sum(v / 0)
NaN
NaNを含むデータに対するあらゆる計算は、NaNという結果になります。
現在、NaN値を取り除く専用の関数はありませんが、標準のfilter()関数を使えば簡単にできます。
与えられた条件がtrueになる値だけが結果の配列にコピーされます。
julia> filter(!isnan, [1, 2, NaN])
2-element Vector{Float64}:
1.0
2.0
この演習では、工場の従業員が身につける名札を作成するコードを書きます。従業員にはID、名前、部署名があります。従業員の名札のラベルは、次の形式で書きます。"[id] - name - DEPARTMENT"
print_name_badge関数を実装してください。ID、名前、部署を入力として受け取り、部署名を大文字にした名札のラベルを返します。
julia> print_name_badge(67, "Katherine Williams", "Strategic Communication")
"[67] - Katherine Williams - STRATEGIC COMMUNICATION"
コンピューターシステムの不具合により、新しい従業員は、工場で働き始めた時点でまだIDを持っていないことがあります。名札は必須なので、その場合はIDの接頭辞がない一時的な名札を受け取ります。
print_name_badge関数を拡張してください。IDがないときは、IDを含まない名札を作成します。
julia> print_name_badge(missing, "Robert Johnson", "Procurement")
"Robert Johnson - PROCUREMENT"
工場のオーナーも、常に名札を身につけなければなりません。しかし、オーナーには部署がなく、この先もできません。オーナーはすべての部署の上に立つ存在だからです。この場合、ラベルは部署名の代わりに"OWNER"を返します。
print_name_badge関数を拡張してください。部署がnothingのときは、その名札は会社のオーナーのものとみなします。
julia> print_name_badge(204, "Rachel Miller", nothing)
"[204] - Rachel Miller - OWNER"
オーナーが新しい従業員でもある場合があることに注意してください。
julia> print_name_badge(missing, "Rachel Miller", nothing)
"Rachel Miller - OWNER"
IDがどれだけ順調に発行されているかの大まかな指標として、IDがない従業員の給与の合計を知りたいとします。値が大きいほど、大勢が待たされているか、問題が役職の高い人にまで及んでいることを意味し、どちらもよくありません。
IDのベクトルと、それに対応する給与のベクトルを入力として受け取り、まだIDがない人の給与の合計を返すsalaries_no_id関数を実装してください。どちらのベクトルも長さは同じです。
julia> ids = [204, missing, 210, 352, missing, 263]
julia> salaries = [23, 21, 47, 35, 17, 101] * 1000
julia> salaries_no_id(ids, salaries)
38,000