Пропущенные значения
Julia поддерживает представление пропущенных значений в статистическом смысле, то есть для ситуаций, когда для переменной в наблюдении нет доступного значения, но теоретически существует действительное значение. Пропущенные значения представляются через объект missing, который является одиночным экземпляром типа Missing. missing эквивалентен NULL в SQL и NA в R, и ведет себя подобно им в большинстве ситуаций.
Распространение пропущенных значений
Поведение значений missing подчиняется одному основному правилу: значения missing автоматически распространяются при передаче в стандартные операторы и функции, в частности математические функции. Неопределенность относительно значения одного из операндов вызывает неопределенность относительно результата. На практике это означает, что операция, включающая значение missing , обычно возвращает missing
julia> missing + 1 missing julia> "a" * missing missing julia> abs(missing) missing
Поскольку missing является обычным объектом Julia, это правило распространения работает только для функций, которые выбрали реализацию этого поведения. Это может быть достигнуто либо с помощью специального метода, определенного для аргументов типа Missing, либо просто путем принятия аргументов этого типа и передачи их в функции, которые их распространяют (например, стандартные операторы). Пакеты должны учитывать, имеет ли смысл распространять пропущенные значения при определении новых функций, и определять методы соответствующим образом, если это необходимо. Передача значения 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 и отличное от любого другого значения. Таким образом, их можно использовать для проверки того, является ли значение 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 только когда это логически необходимо. Для этих операторов неопределенность результата зависит от конкретной операции, следуя хорошо известным правилам логики с тремя значениями, которые также реализуются в SQL с помощью NULL и в R с помощью NA. Это абстрактное определение на самом деле соответствует относительно естественному поведению, которое лучше всего объясняется на конкретных примерах.
Давайте проиллюстрируем этот принцип с помощью логического оператора «или» |. Согласно правилам булевой логики, если один из операндов является 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–2020 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.3.1/manual/missing/