Ответ 1
Хороший систематический способ понять это - начать с некоторых тестовых данных и работать через источник sklearn.OneHotEncoder
с ним. Если вы не очень заботитесь о том, как это работает и просто хотите получить быстрый ответ, пропустите снизу.
X = np.array([
[3, 10, 15, 33, 54, 55, 78, 79, 80, 99],
[5, 1, 3, 7, 8, 12, 15, 19, 20, 8]
]).T
n_values_
Строки 1763-1786 определяют параметр n_values_
. Это будет определено автоматически, если вы установите n_values='auto'
(по умолчанию). В качестве альтернативы вы можете указать максимальное значение для всех функций (int) или максимальное значение для каждой функции (массива). Предположим, что мы используем значение по умолчанию. Таким образом выполняются следующие строки:
n_samples, n_features = X.shape # 10, 2
n_values = np.max(X, axis=0) + 1 # [100, 21]
self.n_values_ = n_values
feature_indices_
Затем вычисляется параметр feature_indices_
.
n_values = np.hstack([[0], n_values]) # [0, 100, 21]
indices = np.cumsum(n_values) # [0, 100, 121]
self.feature_indices_ = indices
Таким образом, feature_indices_
- это всего лишь совокупная сумма n_values_
с добавлением 0.
Конструкция с разреженной матрицей
Затем из данных создается scipy.sparse.coo_matrix
. Он инициализируется из трех массивов: разреженных данных (все), индексов строк и индексов столбцов.
column_indices = (X + indices[:-1]).ravel()
# array([ 3, 105, 10, 101, 15, 103, 33, 107, 54, 108, 55, 112, 78, 115, 79, 119, 80, 120, 99, 108])
row_indices = np.repeat(np.arange(n_samples, dtype=np.int32), n_features)
# array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9], dtype=int32)
data = np.ones(n_samples * n_features)
# array([ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
out = sparse.coo_matrix((data, (row_indices, column_indices)),
shape=(n_samples, indices[-1]),
dtype=self.dtype).tocsr()
# <10x121 sparse matrix of type '<type 'numpy.float64'>' with 20 stored elements in Compressed Sparse Row format>
Обратите внимание, что coo_matrix
сразу преобразуется в scipy.sparse.csr_matrix
. coo_matrix
используется как промежуточный формат, потому что "облегчает быстрое преобразование среди разреженных форматов".
active_features_
Теперь, если n_values='auto'
, разреженная матрица csr сжимается до только столбцов с активными функциями. csr_matrix
возвращается, если sparse=True
, в противном случае она будет уплотнена перед возвратом.
if self.n_values == 'auto':
mask = np.array(out.sum(axis=0)).ravel() != 0
active_features = np.where(mask)[0] # array([ 3, 10, 15, 33, 54, 55, 78, 79, 80, 99, 101, 103, 105, 107, 108, 112, 115, 119, 120])
out = out[:, active_features] # <10x19 sparse matrix of type '<type 'numpy.float64'>' with 20 stored elements in Compressed Sparse Row format>
self.active_features_ = active_features
return out if self.sparse else out.toarray()
расшифровка
Теперь пусть работает наоборот. Мы хотели бы знать, как восстановить X
учитывая разреженную матрицу, которая возвращается вместе с функциями OneHotEncoder
подробно описанными выше. Предположим, что мы фактически OneHotEncoder
код выше, OneHotEncoder
новый OneHotEncoder
и выполнив fit_transform
по нашим данным X
from sklearn import preprocessing
ohc = preprocessing.OneHotEncoder() # all default params
out = ohc.fit_transform(X)
Ключевым понятием для решения этой проблемы является понимание взаимосвязи между active_features_
и out.indices
. Для csr_matrix
массив индексов содержит номера столбцов для каждой точки данных. Однако эти номера столбцов не гарантируются для сортировки. Чтобы отсортировать их, мы можем использовать метод sorted_indices
.
out.indices # array([12, 0, 10, 1, 11, 2, 13, 3, 14, 4, 15, 5, 16, 6, 17, 7, 18, 8, 14, 9], dtype=int32)
out = out.sorted_indices()
out.indices # array([ 0, 12, 1, 10, 2, 11, 3, 13, 4, 14, 5, 15, 6, 16, 7, 17, 8, 18, 9, 14], dtype=int32)
Мы видим, что перед сортировкой индексы фактически меняются по строкам. Другими словами, они упорядочиваются с последним столбцом первым и первым столбцом. Это видно из первых двух элементов: [12, 0]. 0 соответствует 3 в первом столбце X
, так как 3 является минимальным элементом, который был назначен первому активному столбцу. 12 соответствует 5 во втором столбце X
Поскольку первая строка занимает 10 различных столбцов, минимальный элемент второго столбца (1) получает индекс 10. Следующий наименьший (3) получает индекс 11, а третий наименьший (5) получает индекс 12. После сортировки индексы как и следовало ожидать.
Затем мы посмотрим на active_features_
:
ohc.active_features_ # array([ 3, 10, 15, 33, 54, 55, 78, 79, 80, 99, 101, 103, 105, 107, 108, 112, 115, 119, 120])
Обратите внимание, что существует 19 элементов, которые соответствуют количеству отдельных элементов в наших данных (один элемент, 8, был повторен один раз). Также обратите внимание, что они упорядочены. Функции, которые были в первом столбце X
, одинаковы, а функции во втором столбце просто суммируются со 100, что соответствует ohc.feature_indices_[1]
.
Оглядываясь на out.indices
, мы можем видеть, что максимальное число столбца 18, который один минус 19 активных функций в нашей кодировке. Небольшая мысль о связи здесь показывает, что индексы ohc.active_features_
соответствуют номерам столбцов в ohc.indices
. С этим мы можем декодировать:
import numpy as np
decode_columns = np.vectorize(lambda col: ohc.active_features_[col])
decoded = decode_columns(out.indices).reshape(X.shape)
Это дает нам:
array([[ 3, 105],
[ 10, 101],
[ 15, 103],
[ 33, 107],
[ 54, 108],
[ 55, 112],
[ 78, 115],
[ 79, 119],
[ 80, 120],
[ 99, 108]])
И мы можем вернуться к исходным значениям функций, вычитая смещения от ohc.feature_indices_
:
recovered_X = decoded - ohc.feature_indices_[:-1]
array([[ 3, 5],
[10, 1],
[15, 3],
[33, 7],
[54, 8],
[55, 12],
[78, 15],
[79, 19],
[80, 20],
[99, 8]])
Обратите внимание, что вам нужно будет иметь исходную форму X
, которая просто (n_samples, n_features)
.
TL; DR
Учитывая sklearn.OneHotEncoder
экземпляр под названием ohc
, кодированные данные (scipy.sparse.csr_matrix
), выводимым из ohc.fit_transform
или ohc.transform
называемого out
, и формы исходных данных (n_samples, n_feature)
, восстановить исходные данные X
с:
recovered_X = np.array([ohc.active_features_[col] for col in out.sorted_indices().indices])
.reshape(n_samples, n_features) - ohc.feature_indices_[:-1]