Пропущенные значения
Julia предоставляет поддержку для представления пропущенных значений в статистическом смысле, то есть для ситуаций, когда для переменной в наблюдении нет доступного значения, но теоретически существует действительное значение. Пропущенные значения представляются через объект missing, который является единственным экземпляром типа Missing. missing эквивалентен NULL в SQL и NA в R, и ведет себя подобно им в большинстве ситуаций.
Распространение пропущенных значений
Поведение пропущенных значений подчиняется одному основному правилу: пропущенные значения распространяются автоматически при передаче их в стандартные операторы и функции, в частности, математические функции. Неопределенность относительно значения одного из операндов вызывает неопределенность относительно результата. На практике это означает, что операция, включающая пропущенное значение, обычно возвращает missing
julia> missing + 1 missing julia> "a" * missing missing julia> abs(missing) missing
Поскольку missing — обычный объект Julia, это правило распространения работает только для функций, которые выбрали его реализацию. Этого можно достичь либо с помощью специального метода для аргументов типа Missing, либо просто путем принятия аргументов этого типа и передачи их функциям, которые их распространяют (например, стандартным операторам). Пакеты должны учитывать, имеет ли смысл распространять пропущенные значения при определении новых функций, и определять соответствующие методы, если это необходимо. Передача пропущенного значения функции, для которой не определен метод, принимающий аргументы типа Missing, вызывает MethodError, как и для любого другого типа.
Операторы равенства и сравнения
Стандартные операторы равенства и сравнения следуют правилу распространения, представленному выше: если любой из операндов — 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
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 — это ещё один специальный случай, так как они распространяют пропущенные значения только тогда, когда это логически требуется. Для этих операторов неопределенность результата зависит от конкретной операции, следуя хорошо установленным правилам трёхзначной логики, которые также реализованы в 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 Array{Union{Missing, Int64},1}:
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 Array{Union{Missing, String},2}:
missing missing missing
missing missing missing
Массив, допускающий missing значения, но не содержащий таких значений, может быть преобразован обратно в массив, не допускающий пропущенных значений, с помощью convert. Если массив содержит missing значения, при преобразовании генерируется MethodError
julia> x = Union{Missing, String}["a", "b"]
2-element Array{Union{Missing, String},1}:
"a"
"b"
julia> convert(Array{String}, x)
2-element Array{String,1}:
"a"
"b"
julia> y = Union{Missing, String}[missing, "b"]
2-element Array{Union{Missing, String},1}:
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])
Base.SkipMissing{Array{Union{Missing, Int64},1}}(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 Array{Int64,1}:
4
julia> findfirst(!iszero, x)
1
julia> argmax(x)
1
Используйте collect для извлечения значений, не являющихся missing , и сохранения их в массив
julia> collect(x)
3-element Array{Int64,1}:
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–2019 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.2.0/manual/missing/