Пропущенные значения
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 значения, но не содержащий таких значений, может быть преобразован обратно в массив, который не допускает пропущенных значений, используя 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–2021 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.7.0/manual/missing/