我使用sklearn.OneHotEncoder
对我的分类数据进行了编码,然后将它们输入到随机森林分类器中。一切似乎都有效,我得到了预测的输出。
有没有办法扭转编码并将输出转换回原始状态?
答案 0 :(得分:22)
一个很好的系统解决方法是从一些测试数据开始,然后使用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
Lines 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_
参数。
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
用作中间格式,因为它可以促进稀疏格式之间的快速转换。&#34;
现在,如果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
上实例化一个新的fit_transform
并运行X
。
from sklearn import preprocessing
ohc = preprocessing.OneHotEncoder() # all default params
out = ohc.fit_transform(X)
解决此问题的关键洞察力是理解active_features_
和out.indices
之间的关系。对于csr_matrix
,indices数组包含每个数据点的列号。但是,不保证对这些列号进行排序。要对它们进行排序,我们可以使用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对应于X
第一列中的3,因为3是它分配给第一个活动列的最小元素。 12对应于X
的第二列中的5。由于第一行占用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)
。
鉴于名为sklearn.OneHotEncoder
的{{1}}实例,编码数据(ohc
)从scipy.sparse.csr_matrix
或ohc.fit_transform
输出,称为ohc.transform
,原始数据out
的形状,使用以下内容恢复原始数据(n_samples, n_feature)
X
答案 1 :(得分:6)
只需使用ohe.active_features_
计算编码值的点积。它适用于稀疏和密集表示。例如:
from sklearn.preprocessing import OneHotEncoder
import numpy as np
orig = np.array([6, 9, 8, 2, 5, 4, 5, 3, 3, 6])
ohe = OneHotEncoder()
encoded = ohe.fit_transform(orig.reshape(-1, 1)) # input needs to be column-wise
decoded = encoded.dot(ohe.active_features_).astype(int)
assert np.allclose(orig, decoded)
关键的见解是OHE模型的active_features_
属性代表每个二进制列的原始值。因此,我们可以通过简单地使用active_features_
计算点积来解码二进制编码的数字。对于每个数据点,只有一个1
原始值的位置。
答案 2 :(得分:1)
简短的回答是“不”。编码器获取您的分类数据并自动将其转换为一组合理的数字。
答案越长,“不会自动”。但是,如果使用n_values参数提供显式映射,则可以在另一端实现自己的解码。有关如何完成此操作的一些提示,请参阅documentation。
那就是说,这是一个相当奇怪的问题。您可能希望使用DictVectorizer
答案 3 :(得分:1)
如果功能密集,如[1,2,4,5,6],则会遗漏多个号码。然后,我们可以将它们映射到相应的位置。
>>> import numpy as np
>>> from scipy import sparse
>>> def _sparse_binary(y):
... # one-hot codes of y with scipy.sparse matrix.
... row = np.arange(len(y))
... col = y - y.min()
... data = np.ones(len(y))
... return sparse.csr_matrix((data, (row, col)))
...
>>> y = np.random.randint(-2,2, 8).reshape([4,2])
>>> y
array([[ 0, -2],
[-2, 1],
[ 1, 0],
[ 0, -2]])
>>> yc = [_sparse_binary(y[:,i]) for i in xrange(2)]
>>> for i in yc: print i.todense()
...
[[ 0. 0. 1. 0.]
[ 1. 0. 0. 0.]
[ 0. 0. 0. 1.]
[ 0. 0. 1. 0.]]
[[ 1. 0. 0. 0.]
[ 0. 0. 0. 1.]
[ 0. 0. 1. 0.]
[ 1. 0. 0. 0.]]
>>> [i.shape for i in yc]
[(4, 4), (4, 4)]
这是一种妥协且简单的方法,但是argmax()可以很容易地反转,例如:
>>> np.argmax(yc[0].todense(), 1) + y.min(0)[0]
matrix([[ 0],
[-2],
[ 1],
[ 0]])
答案 4 :(得分:1)
请参阅https://stackoverflow.com/a/42874726/562769
import numpy as np
nb_classes = 6
data = [[2, 3, 4, 0]]
def indices_to_one_hot(data, nb_classes):
"""Convert an iterable of indices to one-hot encoded labels."""
targets = np.array(data).reshape(-1)
return np.eye(nb_classes)[targets]
def one_hot_to_indices(data):
indices = []
for el in data:
indices.append(list(el).index(1))
return indices
hot = indices_to_one_hot(orig_data, nb_classes)
indices = one_hot_to_indices(hot)
print(orig_data)
print(indices)
给出:
[[2, 3, 4, 0]]
[2, 3, 4, 0]
答案 5 :(得分:0)
熊猫方法:
要将分类变量转换为二进制变量,pd.get_dummies
可以执行此操作并将其转换回,您可以使用pd.Series.idxmax()
找到值为1的值的索引。然后,您可以映射到列表(根据原始数据在其中索引)或字典。
import pandas as pd
import numpy as np
col = np.random.randint(1,5,20)
df = pd.DataFrame({'A': col})
df.head()
A
0 2
1 2
2 1
3 1
4 3
df_dum = pd.get_dummies(df['A'])
df_dum.head()
1 2 3 4
0 0 1 0 0
1 0 1 0 0
2 1 0 0 0
3 1 0 0 0
4 0 0 1 0
df_n = df_dum.apply(lambda x: x.idxmax(), axis = 1)
df_n.head()
0 2
1 2
2 1
3 1
4 3
答案 6 :(得分:0)
从scikit-learn的0.20版本开始,active_features_
类的OneHotEncoder
属性已被弃用,因此我建议改为使用categories_
属性。
以下功能可以帮助您从经过一键编码的矩阵中恢复原始数据:
def reverse_one_hot(X, y, encoder):
reversed_data = [{} for _ in range(len(y))]
all_categories = list(itertools.chain(*encoder.categories_))
category_names = ['category_{}'.format(i+1) for i in range(len(encoder.categories_))]
category_lengths = [len(encoder.categories_[i]) for i in range(len(encoder.categories_))]
for row_index, feature_index in zip(*X.nonzero()):
category_value = all_categories[feature_index]
category_name = get_category_name(feature_index, category_names, category_lengths)
reversed_data[row_index][category_name] = category_value
reversed_data[row_index]['target'] = y[row_index]
return reversed_data
def get_category_name(index, names, lengths):
counter = 0
for i in range(len(lengths)):
counter += lengths[i]
if index < counter:
return names[i]
raise ValueError('The index is higher than the number of categorical values')
为了进行测试,我创建了一个小的数据集,其中包含用户对用户的评分
data = [
{'user_id': 'John', 'item_id': 'The Matrix', 'rating': 5},
{'user_id': 'John', 'item_id': 'Titanic', 'rating': 1},
{'user_id': 'John', 'item_id': 'Forrest Gump', 'rating': 2},
{'user_id': 'John', 'item_id': 'Wall-E', 'rating': 2},
{'user_id': 'Lucy', 'item_id': 'The Matrix', 'rating': 5},
{'user_id': 'Lucy', 'item_id': 'Titanic', 'rating': 1},
{'user_id': 'Lucy', 'item_id': 'Die Hard', 'rating': 5},
{'user_id': 'Lucy', 'item_id': 'Forrest Gump', 'rating': 2},
{'user_id': 'Lucy', 'item_id': 'Wall-E', 'rating': 2},
{'user_id': 'Eric', 'item_id': 'The Matrix', 'rating': 2},
{'user_id': 'Eric', 'item_id': 'Die Hard', 'rating': 3},
{'user_id': 'Eric', 'item_id': 'Forrest Gump', 'rating': 5},
{'user_id': 'Eric', 'item_id': 'Wall-E', 'rating': 4},
{'user_id': 'Diane', 'item_id': 'The Matrix', 'rating': 4},
{'user_id': 'Diane', 'item_id': 'Titanic', 'rating': 3},
{'user_id': 'Diane', 'item_id': 'Die Hard', 'rating': 5},
{'user_id': 'Diane', 'item_id': 'Forrest Gump', 'rating': 3},
]
data_frame = pandas.DataFrame(data)
data_frame = data_frame[['user_id', 'item_id', 'rating']]
ratings = data_frame['rating']
data_frame.drop(columns=['rating'], inplace=True)
如果我们要构建预测模型,则必须记住在对DataFrame
进行编码之前,先将其从变量ratings = data_frame['rating']
data_frame.drop(columns=['rating'], inplace=True)
中删除。
ohc = OneHotEncoder()
encoded_data = ohc.fit_transform(data_frame)
print(encoded_data)
然后我们继续进行编码
(0, 2) 1.0
(0, 6) 1.0
(1, 2) 1.0
(1, 7) 1.0
(2, 2) 1.0
(2, 5) 1.0
(3, 2) 1.0
(3, 8) 1.0
(4, 3) 1.0
(4, 6) 1.0
(5, 3) 1.0
(5, 7) 1.0
(6, 3) 1.0
(6, 4) 1.0
(7, 3) 1.0
(7, 5) 1.0
(8, 3) 1.0
(8, 8) 1.0
(9, 1) 1.0
(9, 6) 1.0
(10, 1) 1.0
(10, 4) 1.0
(11, 1) 1.0
(11, 5) 1.0
(12, 1) 1.0
(12, 8) 1.0
(13, 0) 1.0
(13, 6) 1.0
(14, 0) 1.0
(14, 7) 1.0
(15, 0) 1.0
(15, 4) 1.0
(16, 0) 1.0
(16, 5) 1.0
这将导致:
reverse_one_hot
编码后,我们可以使用上面定义的reverse_data = matrix_utils.reverse_one_hot(encoded_data, ratings, ohc)
print(pandas.DataFrame(reverse_data))
函数反转,就像这样:
category_1 category_2 target
0 John The Matrix 5
1 John Titanic 1
2 John Forrest Gump 2
3 John Wall-E 2
4 Lucy The Matrix 5
5 Lucy Titanic 1
6 Lucy Die Hard 5
7 Lucy Forrest Gump 2
8 Lucy Wall-E 2
9 Eric The Matrix 2
10 Eric Die Hard 3
11 Eric Forrest Gump 5
12 Eric Wall-E 4
13 Diane The Matrix 4
14 Diane Titanic 3
15 Diane Die Hard 5
16 Diane Forrest Gump 3
哪个给了我们
parsing
答案 7 :(得分:0)
将numpy.argmax()
与axis = 1
一起使用。
示例:
ohe_encoded = np.array([[0, 0, 1], [0, 1, 0], [0, 1, 0], [1, 0, 0]])
ohe_encoded
> array([[0, 0, 1],
[0, 1, 0],
[0, 1, 0],
[1, 0, 0]])
np.argmax(ohe_encoded, axis = 1)
> array([2, 1, 1, 0], dtype=int64)