Missing Values
Julia bietet Unterstützung für die Darstellung fehlender Werte im statistischen Sinne. Dies gilt für Situationen, in denen kein Wert für eine Variable in einer Beobachtung verfügbar ist, aber theoretisch ein gültiger Wert existiert. Fehlende Werte werden über das missing Objekt dargestellt, das die Singleton-Instanz des Typs Missing ist. missing ist gleichwertig mit NULL in SQL und NA in R und verhält sich in den meisten Situationen wie sie.
Propagation of Missing Values
fehlende Werte propagieren sich automatisch, wenn sie an standardmäßige mathematische Operatoren und Funktionen übergeben werden. Für diese Funktionen führt die Unsicherheit über den Wert eines der Operanden zu Unsicherheit über das Ergebnis. In der Praxis bedeutet dies, dass eine mathematische Operation, die einen fehlenden Wert beinhaltet, im Allgemeinen fehlend zurückgibt:
julia> missing + 1
missing
julia> "a" * missing
missing
julia> abs(missing)
missingDa missing ein normales Julia-Objekt ist, funktioniert diese Propagierungsregel nur für Funktionen, die sich entschieden haben, dieses Verhalten zu implementieren. Dies kann erreicht werden durch:
- eine spezifische Methode hinzufügen, die für Argumente vom Typ
Missingdefiniert ist, - Argumente dieses Typs akzeptieren und sie an Funktionen weitergeben, die sie propagieren (wie standardmäßige mathematische Operatoren).
Pakete sollten überlegen, ob es sinnvoll ist, fehlende Werte zu propagieren, wenn neue Funktionen definiert werden, und die Methoden entsprechend definieren, falls dies der Fall ist. Das Übergeben eines missing Wertes an eine Funktion, die keine Methode hat, die Argumente vom Typ Missing akzeptiert, wirft eine MethodError, genau wie bei jedem anderen Typ.
Funktionen, die missing-Werte nicht propagieren, können dazu gebracht werden, dies zu tun, indem man sie in die passmissing-Funktion einwickelt, die im Missings.jl-Paket bereitgestellt wird. Zum Beispiel wird f(x) zu passmissing(f)(x).
Equality and Comparison Operators
Standardgleichheits- und Vergleichsoperatoren folgen der oben dargestellten Propagationsregel: Wenn einer der Operanden fehlend ist, ist das Ergebnis fehlend. Hier sind einige Beispiele:
julia> missing == 1
missing
julia> missing == missing
missing
julia> missing < 1
missing
julia> 2 >= missing
missingInsbesondere ist zu beachten, dass missing == missing missing zurückgibt, sodass == nicht verwendet werden kann, um zu testen, ob ein Wert fehlt. Um zu testen, ob x missing ist, verwenden Sie ismissing(x).
Besondere Vergleichsoperatoren isequal und === sind Ausnahmen von der Propagierungsregel. Sie geben immer einen Bool-Wert zurück, selbst in Anwesenheit von missing-Werten, wobei missing als gleich zu missing und als unterschiedlich von jedem anderen Wert betrachtet wird. Sie können daher verwendet werden, um zu testen, ob ein Wert missing ist:
julia> missing === 1
false
julia> isequal(missing, 1)
false
julia> missing === missing
true
julia> isequal(missing, missing)
trueThe isless operator is another exception: missing is considered as greater than any other value. This operator is used by sort!, which therefore places missing values after all other values:
julia> isless(1, missing)
true
julia> isless(missing, Inf)
false
julia> isless(missing, missing)
falseLogical operators
Logische (oder boolesche) Operatoren |, & und xor sind ein weiterer Sonderfall, da sie nur fehlende Werte propagieren, wenn dies logisch erforderlich ist. Bei diesen Operatoren hängt es von der jeweiligen Operation ab, ob das Ergebnis ungewiss ist oder nicht. Dies folgt den gut etablierten Regeln von three-valued logic, die z.B. durch NULL in SQL und NA in R implementiert sind. Diese abstrakte Definition entspricht einem relativ natürlichen Verhalten, das am besten durch konkrete Beispiele erklärt werden kann.
Lassen Sie uns dieses Prinzip mit dem logischen "oder"-Operator | veranschaulichen. Nach den Regeln der booleschen Logik hat, wenn einer der Operanden true ist, der Wert des anderen Operanden keinen Einfluss auf das Ergebnis, das immer true sein wird:
julia> true | true
true
julia> true | false
true
julia> false | true
trueBasierend auf dieser Beobachtung können wir schließen, dass, wenn einer der Operanden true und der andere missing ist, wir wissen, dass das Ergebnis true ist, trotz der Unsicherheit über den tatsächlichen Wert eines der Operanden. Hätten wir den tatsächlichen Wert des zweiten Operanden beobachten können, könnte dieser nur true oder false sein, und in beiden Fällen wäre das Ergebnis true. Daher propagiert in diesem speziellen Fall die Fehlendeheit nicht:
julia> true | missing
true
julia> missing | true
trueIm Gegenteil, wenn einer der Operanden false ist, kann das Ergebnis entweder true oder false sein, abhängig vom Wert des anderen Operanden. Daher muss, wenn dieser Operand fehlend ist, das Ergebnis ebenfalls fehlend sein:
julia> false | true
true
julia> true | false
true
julia> false | false
false
julia> false | missing
missing
julia> missing | false
missingDas Verhalten des logischen "und" Operators & ist ähnlich dem des | Operators, mit dem Unterschied, dass Fehlende Werte nicht propagiert werden, wenn einer der Operanden false ist. Zum Beispiel, wenn dies der Fall beim ersten Operanden ist:
julia> false & false
false
julia> false & true
false
julia> false & missing
falseAndererseits propagiert die Fehlendheit, wenn einer der Operanden true ist, zum Beispiel der erste:
julia> true & true
true
julia> true & false
false
julia> true & missing
missingSchließlich propagiert der logische Operator "exklusiv oder" xor immer fehlende Werte, da beide Operanden immer einen Einfluss auf das Ergebnis haben. Beachten Sie auch, dass der Negationsoperator ! fehlende zurückgibt, wenn der Operand fehlend ist, genau wie andere unäre Operatoren.
Control Flow and Short-Circuiting Operators
Steuerflussoperatoren, einschließlich if, while und der ternary operator x ? y : z, erlauben keine fehlenden Werte. Dies liegt an der Ungewissheit darüber, ob der tatsächliche Wert true oder false wäre, wenn wir ihn beobachten könnten. Das bedeutet, dass wir nicht wissen, wie sich das Programm verhalten sollte. In diesem Fall wird ein TypeError ausgelöst, sobald ein missing Wert in diesem Kontext auftritt:
julia> if missing
println("here")
end
ERROR: TypeError: non-boolean (Missing) used in boolean contextFor the same reason, contrary to logical operators presented above, the short-circuiting boolean operators && and || do not allow for missing values in situations where the value of the operand determines whether the next operand is evaluated or not. For example:
julia> missing || false
ERROR: TypeError: non-boolean (Missing) used in boolean context
julia> missing && false
ERROR: TypeError: non-boolean (Missing) used in boolean context
julia> true && missing && false
ERROR: TypeError: non-boolean (Missing) used in boolean contextIm Gegensatz dazu wird kein Fehler ausgelöst, wenn das Ergebnis ohne die missing-Werte bestimmt werden kann. Dies ist der Fall, wenn der Code vor der Auswertung des missing-Operands kurzgeschlossen wird und wenn der missing-Operand der letzte ist:
julia> true && missing
missing
julia> false && missing
falseArrays With Missing Values
Arrays, die fehlende Werte enthalten, können wie andere Arrays erstellt werden:
julia> [1, missing]
2-element Vector{Union{Missing, Int64}}:
1
missingWie dieses Beispiel zeigt, ist der Elementtyp solcher Arrays Union{Missing, T}, wobei T der Typ der nicht fehlenden Werte ist. Dies spiegelt wider, dass die Array-Einträge entweder vom Typ T (hier Int64) oder vom Typ Missing sein können. Diese Art von Array verwendet eine effiziente Speicherverwaltung, die einem Array{T} entspricht, das die tatsächlichen Werte hält, kombiniert mit einem Array{UInt8}, das den Typ des Eintrags angibt (d.h. ob er Missing oder T ist).
Arrays, die fehlende Werte zulassen, können mit der Standard-Syntax erstellt werden. Verwenden Sie Array{Union{Missing, T}}(missing, dims), um Arrays zu erstellen, die mit fehlenden Werten gefüllt sind:
julia> Array{Union{Missing, String}}(missing, 2, 3)
2×3 Matrix{Union{Missing, String}}:
missing missing missing
missing missing missingDie Verwendung von undef oder ähnlichem kann derzeit ein Array mit missing füllen, aber dies ist nicht der richtige Weg, um ein solches Array zu erhalten. Verwenden Sie stattdessen einen missing-Konstruktor, wie oben gezeigt.
Ein Array mit einem Elementtyp, der missing-Einträge erlaubt (z. B. Vector{Union{Missing, T}}), das keine missing-Einträge enthält, kann in einen Arraytyp umgewandelt werden, der keine missing-Einträge erlaubt (z. B. Vector{T}), indem convert verwendet wird. Wenn das Array missing-Werte enthält, wird während der Umwandlung ein MethodError ausgelöst:
julia> x = Union{Missing, String}["a", "b"]
2-element Vector{Union{Missing, String}}:
"a"
"b"
julia> convert(Array{String}, x)
2-element Vector{String}:
"a"
"b"
julia> y = Union{Missing, String}[missing, "b"]
2-element Vector{Union{Missing, String}}:
missing
"b"
julia> convert(Array{String}, y)
ERROR: MethodError: Cannot `convert` an object of type Missing to an object of type StringSkipping Missing Values
Da missing-Werte mit standardmäßigen mathematischen Operatoren propagieren, geben Reduktionsfunktionen missing zurück, wenn sie auf Arrays angewendet werden, die fehlende Werte enthalten:
julia> sum([1, missing])
missingIn dieser Situation verwenden Sie die skipmissing-Funktion, um fehlende Werte zu überspringen:
julia> sum(skipmissing([1, missing]))
1Diese Hilfsfunktion gibt einen Iterator zurück, der fehlende Werte effizient herausfiltert. Sie kann daher mit jeder Funktion verwendet werden, die Iteratoren unterstützt:
julia> x = skipmissing([3, missing, 2, 1])
skipmissing(Union{Missing, Int64}[3, missing, 2, 1])
julia> maximum(x)
3
julia> sum(x)
6
julia> mapreduce(sqrt, +, x)
4.146264369941973Objekte, die durch den Aufruf von skipmissing auf einem Array erstellt wurden, können mit Indizes aus dem übergeordneten Array indiziert werden. Indizes, die fehlenden Werten entsprechen, sind für diese Objekte nicht gültig, und es wird ein Fehler ausgegeben, wenn versucht wird, sie zu verwenden (sie werden auch von keys und eachindex übersprungen):
julia> x[1]
3
julia> x[2]
ERROR: MissingException: the value at index (2,) is missing
[...]Dies ermöglicht es Funktionen, die auf Indizes arbeiten, in Kombination mit skipmissing zu funktionieren. Dies gilt insbesondere für Such- und Findefunktionen. Diese Funktionen geben Indizes zurück, die für das Objekt, das von skipmissing zurückgegeben wird, gültig sind, und sind auch die Indizes der übereinstimmenden Einträge im übergeordneten Array:
julia> findall(==(1), x)
1-element Vector{Int64}:
4
julia> findfirst(!iszero, x)
1
julia> argmax(x)
1Verwenden Sie collect, um nicht fehlende Werte zu extrahieren und sie in einem Array zu speichern:
julia> collect(x)
3-element Vector{Int64}:
3
2
1Logical Operations on Arrays
Die oben beschriebene dreiwertige Logik für logische Operatoren wird auch von logischen Funktionen angewendet, die auf Arrays angewendet werden. Daher geben Array-Gleichheitstests mit dem == Operator missing zurück, wann immer das Ergebnis nicht bestimmt werden kann, ohne den tatsächlichen Wert des missing Eintrags zu kennen. In der Praxis bedeutet dies, dass missing zurückgegeben wird, wenn alle nicht fehlenden Werte der verglichenen Arrays gleich sind, aber eines oder beide Arrays fehlende Werte enthalten (möglicherweise an unterschiedlichen Positionen):
julia> [1, missing] == [2, missing]
false
julia> [1, missing] == [1, missing]
missing
julia> [1, 2, missing] == [1, missing, 2]
missingWas einzelne Werte betrifft, verwenden Sie isequal, um fehlende Werte als gleichwertig zu anderen fehlenden Werten zu behandeln, jedoch unterschiedlich von nicht fehlenden Werten:
julia> isequal([1, missing], [1, missing])
true
julia> isequal([1, 2, missing], [1, missing, 2])
falseFunktionen any und all folgen ebenfalls den Regeln der dreiwertigen Logik. Daher geben sie missing zurück, wenn das Ergebnis nicht bestimmt werden kann:
julia> all([true, missing])
missing
julia> all([false, missing])
false
julia> any([true, missing])
true
julia> any([false, missing])
missing