Категориальные данные
Зачастую столбцы во фрейме данных имеют небольшое количество уровней:
julia> v = ["Group A", "Group A", "Group A", "Group B", "Group B", "Group B"]
6-element Vector{String}:
"Group A"
"Group A"
"Group A"
"Group B"
"Group B"
"Group B"
Простейшая форма кодирования, используемая в Vector
, представляет каждый элемент этого вектора в виде полной строки. Для более эффективного представления данных можно заменить строки индексами небольшого набора уровней. Такой подход имеет два преимущества. Первое заключается в том, что такие векторы, как правило, занимают меньше памяти. Второе же в том, что их можно эффективно группировать с помощью функции groupby
.
Есть два стандартных типа, обеспечивающих сжатие уровней:
-
PooledVector
из PooledArrays.jl; -
CategoricalVector
из CategoricalArrays.jl.
Различие между PooledVector
и CategoricalVector
в следующем.
-
Тип
PooledVector
предназначен для случаев, когда единственной целью является сжатие данных. -
Тип
CategoricalVector
дополнительно обеспечивает полную поддержку работы с категориальными переменными, причем как с упорядоченными (номинальные переменные), так и с неупорядоченными (порядковые переменные) категориями, но ценой того, что допускаются элементы только типовAbstractString
,AbstractChar
иNumber
(возможно, в объединении сMissing
).
Тип CategoricalVector
полезен, в частности, когда уникальные значения в массиве (уровнях) должны иметь значимый порядок, например, при выводе таблиц, построении графиков или подборе регрессионных моделей. CategoricalArrays.jl предоставляет функции для задания и получения этого порядка и сравнения значений в соответствии с ним. В свою очередь, тип PooledVector
— это по сути простая замена для Vector
, практически не имеющая заметных пользователю отличий, кроме меньшего потребления памяти и более высокой производительности.
Ниже представлен ряд примеров работы с CategoricalArrays.jl. Дополнительные сведения о категориальных массивах см. в документации по пакету CategoricalArrays.jl. Кроме того, обратите внимание, что в этом разделе речь идет только о векторах, так как нас интересуют фреймы данных. Однако в общем случае оба пакета позволяют работать с массивами любой размерности.
Для разбора приведенных ниже примеров необходимо сначала установить пакет CategoricalArrays.jl.
julia> using CategoricalArrays
julia> cv = categorical(v)
6-element CategoricalArray{String,1,UInt32}:
"Group A"
"Group A"
"Group A"
"Group B"
"Group B"
"Group B"
Векторы CategoricalVectors
поддерживают отсутствующие значения.
julia> cv = categorical(["Group A", missing, "Group A",
"Group B", "Group B", missing])
6-element CategoricalArray{Union{Missing, String},1,UInt32}:
"Group A"
missing
"Group A"
"Group B"
"Group B"
missing
Помимо эффективного представления повторяющихся данных, тип CategoricalArray
позволяет эффективно в любой момент определять допустимые уровни переменной с помощью функции levels
(имейте в виду, что уровни могут как использоваться, так и не использоваться на самом деле в данных):
julia> levels(cv)
2-element Vector{String}:
"Group A"
"Group B"
Функция levels!
также позволяет изменять порядок отображения уровней, что может быть полезно при выводе данных или при работе с упорядоченными переменными.
julia> levels!(cv, ["Group B", "Group A"])
6-element CategoricalArray{Union{Missing, String},1,UInt32}:
"Group A"
missing
"Group A"
"Group B"
"Group B"
missing
julia> levels(cv)
2-element Vector{String}:
"Group B"
"Group A"
julia> sort(cv)
6-element CategoricalArray{Union{Missing, String},1,UInt32}:
"Group B"
"Group B"
"Group A"
"Group A"
missing
missing
По умолчанию вектор CategoricalVector
может представлять разных уровней. Вызвав функцию compress
, можно сократить занимаемую память:
julia> cv = compress(cv)
6-element CategoricalArray{Union{Missing, String},1,UInt8}:
"Group A"
missing
"Group A"
"Group B"
"Group B"
missing
Функция categorical
принимает дополнительный именованный аргумент compress
, значение true
которого равносильно вызову функции compress
для нового вектора:
julia> cv1 = categorical(["A", "B"], compress=true)
2-element CategoricalArray{String,1,UInt8}:
"A"
"B"
Если именованный аргумент ordered
имеет значение true
, полученный в итоге вектор CategoricalVector
будет упорядоченным, то есть его уровни допускают проверку порядка (вместо выдачи ошибки):
julia> cv2 = categorical(["A", "B"], ordered=true)
2-element CategoricalArray{String,1,UInt32}:
"A"
"B"
julia> cv1[1] < cv1[2]
ERROR: ArgumentError: Unordered CategoricalValue objects cannot be tested for order using <. Use isless instead, or call the ordered! function on the parent array to change this
julia> cv2[1] < cv2[2]
true
Проверить, является ли вектор CategoricalVector
упорядоченным, можно с помощью функции isordered
. Изменить упорядоченный вектор на неупорядоченный и наоборот можно с помощью функции ordered!
.
julia> isordered(cv1)
false
julia> ordered!(cv1, true)
2-element CategoricalArray{String,1,UInt8}:
"A"
"B"
julia> isordered(cv1)
true
julia> cv1[1] < cv1[2]
true