Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何反转sklearn.onehotcoder转换以恢复原始数据?_Python_Machine Learning_Scipy_Scikit Learn - Fatal编程技术网

Python 如何反转sklearn.onehotcoder转换以恢复原始数据?

Python 如何反转sklearn.onehotcoder转换以恢复原始数据?,python,machine-learning,scipy,scikit-learn,Python,Machine Learning,Scipy,Scikit Learn,我使用sklearn.onehotcoder对分类数据进行编码,并将它们输入到随机森林分类器。一切似乎都正常,我得到了预期的输出 有没有办法反转编码并将输出转换回原始状态?简短的回答是“否”。编码器获取分类数据并自动将其转换为一组合理的数字 较长的答案是“不自动”。但是,如果使用n_values参数提供显式映射,则可能可以在另一端实现自己的解码。有关如何实现这一点的提示,请参阅 尽管如此,这是一个相当奇怪的问题。相反,您可能希望使用一个一个很好的系统化方法来解决这个问题,就是从一些测试数据开始,

我使用
sklearn.onehotcoder
对分类数据进行编码,并将它们输入到随机森林分类器。一切似乎都正常,我得到了预期的输出

有没有办法反转编码并将输出转换回原始状态?

简短的回答是“否”。编码器获取分类数据并自动将其转换为一组合理的数字

较长的答案是“不自动”。但是,如果使用n_values参数提供显式映射,则可能可以在另一端实现自己的解码。有关如何实现这一点的提示,请参阅


尽管如此,这是一个相当奇怪的问题。相反,您可能希望使用一个

一个很好的系统化方法来解决这个问题,就是从一些测试数据开始,并使用它来处理源代码。如果你不太关心它是如何工作的,只是想快速得到答案,请跳到底部

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_值_ 确定
n\u值
参数。如果您设置了
n\u 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
特征指数_ 接下来,计算
特征索引
参数

n_values = np.hstack([[0], n_values])  # [0, 100, 21]
indices = np.cumsum(n_values)          # [0, 100, 121]
self.feature_indices_ = indices
因此,
feature\u index\uu
仅仅是
n\u值的累积和,前面加了一个0

稀疏矩阵构造 接下来,根据数据构造一个新的模型。它由三个数组初始化:稀疏数据(所有数组)、行索引和列索引

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>
解码 现在让我们反向工作。我们想知道如何恢复
X
,给定随上述
OneHotEncoder
功能一起返回的稀疏矩阵。假设我们通过实例化一个新的
onehotcoder
并对数据
X
运行
fit\u transform
来运行上述代码

from sklearn import preprocessing
ohc = preprocessing.OneHotEncoder()  # all default params
out = ohc.fit_transform(X)
解决此问题的关键在于理解
活动特性与
输出索引之间的关系。对于
csr\u矩阵
,索引数组包含每个数据点的列号。但是,这些列编号不能保证被排序。要对它们进行排序,我们可以使用
sorted\u index
方法

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。排序后,索引按我们预期的顺序排列

接下来,我们看一下
活动功能\uu

ohc.active_features_  # array([  3,  10,  15,  33,  54,  55,  78,  79,  80,  99, 101, 103, 105, 107, 108, 112, 115, 119, 120])
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]])
请注意,共有19个元素,对应于数据中不同元素的数量(一个元素8重复了一次)。还要注意,这些都是按顺序排列的。
X
第一列中的特征是相同的,第二列中的特征简单地加上100,对应于
ohc.feature\u index.[1]

回顾
out.index
,我们可以看到最大列数是18,这是一减去编码中的19个活动特征。稍微考虑一下这里的关系就会发现,
ohc.active\u特性的索引与
ohc.index
中的列号相对应。有了这个,我们可以解码:

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]])
   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
我们可以通过从ohc中减去偏移量来返回原始特征值。特征索引

ohc.active_features_  # array([  3,  10,  15,  33,  54,  55,  78,  79,  80,  99, 101, 103, 105, 107, 108, 112, 115, 119, 120])
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_示例,n_特征)

TL;博士 给定名为
ohc
sklearn.onehotcoder
实例,从
ohc.fit\u transform
ohc.transform
输出的编码数据(
scipy.sparse.csr\u矩阵
)和原始数据的形状
(n\u样本,n\u特征)
,使用以下方法恢复原始数据
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]

只需使用
ohe.active\u功能计算编码值的点积即可。它既适用于稀疏表示,也适用于密集表示。例如:

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\u features\u
属性表示每个二进制列的原始值。因此,我们可以通过简单地计算带有
活动特征的点积来解码二进制编码的数字。对于每个数据点,只有一个
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()工作并易于反转,例如:

如何进行热编码 看

如何逆转 给出:

[[2, 3, 4, 0]]
[2, 3, 4, 0]
方法: 要将分类变量转换为二进制变量,
pd.get\u 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

自scikit learn的0.20版以来,
OneHotEncoder
类的
active\u features\uuuuuuuuuuuuuuu属性已被弃用,因此我建议改用
categories
属性

以下功能可帮助您恢复原始da
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
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)
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_data = 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
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)