Spec-Zone.ru › Julia 1.11

Пропущенные значения

Julia предоставляет поддержку для представления пропущенных значений в статистическом смысле. Это для ситуаций, когда для переменной в наблюдении нет значения, но теоретически существует действительное значение. Пропущенные значения представляются через объект missing, который является одиночным экземпляром типа Missing. missing эквивалентно NULL в SQL и NA в R, и ведет себя подобно им в большинстве ситуаций.

Распространение пропущенных значений

missing значения распространяются автоматически при передаче в стандартные математические операторы и функции. Для этих функций неопределенность значения одного из операндов вызывает неопределенность результата. На практике это означает, что математическая операция, включающая в себя значение missing , обычно возвращает missing:

julia> missing + 1
missing

julia> "a" * missing
missing

julia> abs(missing)
missing

Поскольку missing — это обычный объект Julia, это правило распространения работает только для функций, которые выбрали реализовать такое поведение. Это можно сделать, добавив:

  • специальный метод, определенный для аргументов типа Missing,
  • прием аргументов этого типа и передача их функциям, которые их распространяют (например, стандартные математические операторы).

Пакеты должны учитывать, имеет ли смысл распространять пропущенные значения при определении новых функций, и определять соответствующие методы, если это необходимо. Передача значения missing функции, у которой нет метода, принимающего аргументы типа Missing , вызовет MethodError, как и для любого другого типа.

Функции, которые не распространяют missing значения, можно заставить это делать, обернув их в функцию passmissing, предоставляемую пакетом Missings.jl. Например, f(x) становится passmissing(f)(x).

Операторы равенства и сравнения

Стандартные операторы равенства и сравнения следуют правилу распространения, представленному выше: если любой из операндов — missing, результат — missing. Вот несколько примеров:

julia> missing == 1
missing

julia> missing == missing
missing

julia> missing < 1
missing

julia> 2 >= missing
missing

В частности, обратите внимание, что missing == missing возвращает missing, поэтому == нельзя использовать для проверки наличия пропущенного значения. Чтобы проверить, является ли x missing, используйте ismissing(x).

Специальные операторы сравнения isequal и === являются исключениями из правила распространения. Они всегда возвращают значение Bool, даже при наличии значений missing, считая missing равным missing и отличным от любого другого значения. Таким образом, их можно использовать для проверки, является ли значение missing:

julia> missing === 1
false

julia> isequal(missing, 1)
false

julia> missing === missing
true

julia> isequal(missing, missing)
true

Оператор isless — ещё одно исключение: missing считается больше любого другого значения. Этот оператор используется функцией sort!, которая поэтому помещает missing значения после всех других значений:

julia> isless(1, missing)
true

julia> isless(missing, Inf)
false

julia> isless(missing, missing)
false

Логические операторы

Логические (или булевы) операторы |, & и xor — ещё один особый случай, поскольку они распространяют значения missing только тогда, когда это логически необходимо. Для этих операторов неопределенность результата зависит от конкретной операции. Это соответствует хорошо известным правилам трёхзначной логики, которые реализованы, например, в NULL в SQL и NA в R. Это абстрактное определение соответствует относительно естественному поведению, которое лучше всего объясняется конкретными примерами.

Давайте проиллюстрируем этот принцип на примере логического оператора "или" |. В соответствии с правилами булевой логики, если один из операндов — true, значение другого операнда не влияет на результат, который всегда будет true:

julia> true | true
true

julia> true | false
true

julia> false | true
true

Исходя из этого наблюдения, мы можем заключить, что если один из операндов — true, а другой — missing, то результат будет true независимо от неопределённости фактического значения одного из операндов. Если бы мы могли наблюдать фактическое значение второго операнда, оно могло бы быть либо true, либо false, и в обоих случаях результат был бы true. Таким образом, в этом конкретном случае пропущенность не распространяется:

julia> true | missing
true

julia> missing | true
true

Напротив, если один из операндов — false, результат может быть либо true, либо false, в зависимости от значения другого операнда. Поэтому, если этот операнд — missing, результат также должен быть missing:

julia> false | true
true

julia> true | false
true

julia> false | false
false

julia> false | missing
missing

julia> missing | false
missing

Поведение логического оператора "и" & аналогично поведению оператора |, за исключением того, что пропущенность не распространяется, когда один из операндов — false. Например, когда это относится к первому операнду:

julia> false & false
false

julia> false & true
false

julia> false & missing
false

С другой стороны, пропущенность распространяется, когда один из операндов — true, например, первый:

julia> true & true
true

julia> true & false
false

julia> true & missing
missing

Наконец, логический оператор "исключающее или" xor всегда распространяет значения missing, поскольку оба операнда всегда влияют на результат. Также обратите внимание, что оператор отрицания ! возвращает missing, когда операнд — missing, как и другие унарные операторы.

Операторы управления потоком и короткого замыкания

Операторы управления потоком, включая if, while и условный оператор x ? y : z не допускают пропущенных значений. Это из-за неопределённости того, было бы ли фактическое значение true или false если бы мы могли его наблюдать. Это означает, что мы не знаем, как программа должна себя вести. В этом случае TypeError генерируется при обнаружении значения missing в этом контексте:

julia> if missing
           println("here")
       end
ERROR: TypeError: non-boolean (Missing) used in boolean context

По той же причине, в отличие от логических операторов, представленных выше, операторы короткого замыкания && и || не допускают значений missing в ситуациях, когда значение операнда определяет, будет ли оцениваться следующий операнд или нет. Например:

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

В отличие от этого, ошибки не возникает, когда результат может быть определён без значений missing. Это происходит, когда код выполняется по короткому замыканию до оценки операнда missing, и когда операнд missing является последним:

julia> true && missing
missing

julia> false && missing
false

Массивы с пропущенными значениями

Массивы, содержащие пропущенные значения, могут быть созданы как и другие массивы:

julia> [1, missing]
2-element Vector{Union{Missing, Int64}}:
 1
  missing

Как показывает этот пример, тип элементов таких массивов — Union{Missing, T}, где T — тип не пропущенных значений. Это отражает тот факт, что элементы массива могут быть либо типа T (здесь Int64), либо типа Missing. Этот тип массива использует эффективное хранение памяти, эквивалентное Array{T} , хранящему фактические значения, и Array{UInt8} , указывающему тип элемента (т.е. является ли он Missing или T).

Массивы, допускающие пропущенные значения, могут быть созданы с помощью стандартного синтаксиса. Используйте Array{Union{Missing, T}}(missing, dims) для создания массивов, заполненных пропущенными значениями:

julia> Array{Union{Missing, String}}(missing, 2, 3)
2×3 Matrix{Union{Missing, String}}:
 missing  missing  missing
 missing  missing  missing

Использование undef или similar в настоящее время может создать массив, заполненный missing, но это не правильный способ получить такой массив. Используйте конструктор missing как показано выше.

Массив с типом элементов, допускающим missing элементы (например, Vector{Union{Missing, T}}), который не содержит missing элементов, может быть преобразован в массив типа, не допускающего missing элементы (например, Vector{T}) с помощью convert. Если массив содержит missing значения, при преобразовании будет выброшено MethodError:

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

Пропуск пропущенных значений

Поскольку значения missing распространяются стандартными математическими операторами, функции сокращения возвращают missing при вызове на массивах, содержащих пропущенные значения:

julia> sum([1, missing])
missing

В этой ситуации используйте функцию skipmissing для пропуска пропущенных значений:

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

Эта вспомогательная функция возвращает итератор, эффективно фильтрующий missing значения. Поэтому она может быть использована с любой функцией, которая поддерживает итераторы:

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

Объекты, созданные вызовом skipmissing для массива, могут быть индексированы с помощью индексов из родительского массива. Индексы, соответствующие пропущенным значениям, не являются допустимыми для этих объектов, и ошибка будет выброшена при попытке их использования (они также пропускаются keys и eachindex):

julia> x[1]
3

julia> x[2]
ERROR: MissingException: the value at index (2,) is missing
[...]

Это позволяет функциям, работающим с индексами, работать в сочетании с skipmissing. Это особенно актуально для функций поиска и нахождения. Эти функции возвращают индексы, действительные для объекта, возвращаемого skipmissing, и также являются индексами соответствующих элементов в родительском массиве:

julia> findall(==(1), x)
1-element Vector{Int64}:
 4

julia> findfirst(!iszero, x)
1

julia> argmax(x)
1

Используйте collect, чтобы извлечь значения, отличные от missing , и сохранить их в массив:

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

Логические операции над массивами

Трехзначная логика, описанная выше для логических операторов, также используется логическими функциями, применяемыми к массивам. Таким образом, проверки на равенство массивов с использованием оператора == возвращают missing всякий раз, когда результат не может быть определен без знания фактического значения элемента missing. На практике это означает, что возвращается missing, если все не пропущенные значения сравниваемых массивов равны, но один или оба массива содержат пропущенные значения (возможно, в разных позициях):

julia> [1, missing] == [2, missing]
false

julia> [1, missing] == [1, missing]
missing

julia> [1, 2, missing] == [1, missing, 2]
missing

Что касается отдельных значений, используйте isequal, чтобы рассматривать значения missing как равные другим значениям missing, но отличные от значений, не являющихся пропущенными:

julia> isequal([1, missing], [1, missing])
true

julia> isequal([1, 2, missing], [1, missing, 2])
false

Функции any и all также следуют правилам трехзначной логики. Таким образом, возвращают missing, когда результат не может быть определен:

julia> all([true, missing])
missing

julia> all([false, missing])
false

julia> any([true, missing])
true

julia> any([false, missing])
missing

© 2009–2024 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.11/manual/missing/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API