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)
missing

Da 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 Missing definiert 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
missing

Insbesondere 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)
true

The 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)
false

Logical 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
true

Basierend 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
true

Im 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
missing

Das 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
false

Andererseits propagiert die Fehlendheit, wenn einer der Operanden true ist, zum Beispiel der erste:

julia> true & true
true

julia> true & false
false

julia> true & missing
missing

Schließ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 context

For 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 context

Im 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
false

Arrays 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
  missing

Wie 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  missing
Note

Die 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 String

Skipping 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])
missing

In dieser Situation verwenden Sie die skipmissing-Funktion, um fehlende Werte zu überspringen:

julia> sum(skipmissing([1, missing]))
1

Diese 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.146264369941973

Objekte, 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)
1

Verwenden Sie collect, um nicht fehlende Werte zu extrahieren und sie in einem Array zu speichern:

julia> collect(x)
3-element Vector{Int64}:
 3
 2
 1

Logical 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]
missing

Was 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])
false

Funktionen 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