Сведения о реализации
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. Эти оптимизации означают, что при циклическом проходе по значениям в массиве за сравнение пулов придется платить только один раз.