Сведения о реализации
CategoricalArray
состоит из двух полей.
-
refs
: целочисленный массив, хранящий позицию уровня категории в полеlevels
вCategoricalPool
для каждого элементаCategoricalArray
;0
обозначает отсутствующее значение (только дляCategoricalArray{Union{T, Missing}}
). -
pool
: объектCategoricalPool
, поддерживающий уровни массива.
Тип CategoricalPool{V,R,C}
отслеживает уровни типа V
и связывает их с целочисленным справочным кодом типа R
(для внутреннего использования). Он предлагает методы для добавления новых уровней, а также для получения целочисленного индекса, соответствующего уровню, и наоборот. Упорядочены ли значения CategoricalArray
или нет, определяется полем ordered
пула.
Обратите внимание, что уровни CategoricalPool
являются полуизменяемыми: разрешается только добавлять новые уровни, но никогда — удалять или изменять порядок существующих. Благодаря этому существующие объекты CategoricalValue
остаются действительными и всегда указывают на тот же уровень, на котором они были созданы. Поэтому CategoricalArray
создают новый пул каждый раз, когда некоторые из их уровней удаляются или упорядочиваются. Это происходит при вызове функции levels!
, а также при присвоении CategoricalValue
с помощью функций setindex!
, push!
, append!
, copy!
или copyto!
(поскольку новые уровни могут быть добавлены впереди, чтобы сохранить относительный порядок исходного и конечного уровней). В этом случае требуется обновить все справочные коды, чтобы они указывали на новый пул. Однако будет невозможно сравнивать существующие упорядоченные объекты CategoricalValue
со значениями из массива с помощью <
и >
.
Параметры типа CategoricalArray{T, N, R <: Integer, V, C, U}
несколько сложны.
-
T
является типом элементов массива без оболочекCategoricalValue
. ЕслиT >: Missing
, массив поддерживает пропущенные значения. -
N
представляет количество измерений массива. -
R
является ссылочным типом, типом элемента поляrefs
. Он позволяет оптимизировать использование памяти в зависимости от количества уровней (т. е.CategoricalArray
с менее чем 256 уровнями можно использоватьR = UInt8
). -
V
является типом уровней, он равенT
для массивов, не поддерживающих пропущенные значения. Для массивов, поддерживающих пропущенные значения,T = Union{V, Missing}
-
C
— это тип категориальных значений, то есть объектов, возвращаемых при индексации неотсутствующих элементовCategoricalArray
. Он всегда равенCategoricalValue{V, R}
и присутствует только по техническим причинам (чтобы нарушить рекурсивную зависимость междуCategoricalArray
иCategoricalValue
). -
U
может быть либоUnion{}
для массивов, которые не поддерживают отсутствующие значения, либоMissing
для тех, которые их поддерживают.
При построении можно указать только T
, N
и R
. Последние три параметра выбираются автоматически, но они необходимы для определения типа. В частности, U
позволяет выразить, что CategoricalArray{T, N}
наследуется от AbstractArray{Union{C, U}, N}
(что эквивалентно AbstractArray{C, N}
для массивов, не поддерживающих отсутствующие значения, и AbstractArray{Union{C, Missing}, N}
для тех, которые их поддерживают).
Тип CategoricalPool
предназначен для ограничения необходимости прохода по всем элементам вектора, как для чтения, так и для записи. Именно поэтому неиспользуемые уровни не отбрасываются автоматически (это заставило бы проверять все элементы при каждом изменении или вести таблицу подсчетов), а только при вызове droplevels!
. levels
— это (очень быстрая) операция O(1), поскольку она просто возвращает (упорядоченный) вектор уровней, не обращаясь к данным вообще.
Скалярные операции между объектами CategoricalValue
или между объектами CategoricalValue
и CategoricalArray
обычно требуют проверки того, равны ли пулы или является ли один из них супермножеством другого. Чтобы эти операции были эффективными, CategoricalPool
хранит указатель на последний встретившийся равный пул в поле equalto
и указатель на последний встретившийся пул строгих супермножеств в поле subsetof
. Хэш уровней вычисляется при первой необходимости и хранится в поле hash
. Эти оптимизации означают, что при циклическом проходе по значениям в массиве за сравнение пулов придется платить только один раз.