Пропущенные значения
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 , если мы сможем его наблюдать. Это означает, что нам неизвестно, как должно вести себя программа. В этом случае, как только встречается значение missing в этом контексте, выбрасывается TypeError:
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> mean(x)
2.0
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–2022 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.8/manual/missing/