Python 将二维矩阵转换为三维单热矩阵numpy

Python 将二维矩阵转换为三维单热矩阵numpy,python,numpy,vectorization,one-hot-encoding,Python,Numpy,Vectorization,One Hot Encoding,我有np矩阵,我想把它转换成一个3d数组,用一个热编码的元素作为第三维。有没有一种不在每行上循环的方法 乙二醇 应制成 b=[[1,0,0,0], [0,0,1,0], [0,1,0,0], [0,0,0,1]] 方法#1 这是一个厚颜无耻的一行,滥用比较- (np.arange(a.max()) == a[...,None]-1).astype(int) 样本运行- In [120]: a Out[120]: array([[1, 7, 5, 3], [2, 4, 1

我有np矩阵,我想把它转换成一个3d数组,用一个热编码的元素作为第三维。有没有一种不在每行上循环的方法 乙二醇

应制成

b=[[1,0,0,0], [0,0,1,0],
   [0,1,0,0], [0,0,0,1]]
方法#1 这是一个厚颜无耻的一行,滥用比较-

(np.arange(a.max()) == a[...,None]-1).astype(int)
样本运行-

In [120]: a
Out[120]: 
array([[1, 7, 5, 3],
       [2, 4, 1, 4]])

In [121]: (np.arange(a.max()) == a[...,None]-1).astype(int)
Out[121]: 
array([[[1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 1],
        [0, 0, 0, 0, 1, 0, 0],
        [0, 0, 1, 0, 0, 0, 0]],

       [[0, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 1, 0, 0, 0],
        [1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 1, 0, 0, 0]]])
In [157]: a
Out[157]: 
array([[1, 7, 5, 3],
       [2, 4, 1, 4]])

In [158]: onehot_sparse(a).toarray()
Out[158]: 
array([[0, 1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 1, 0, 0, 0, 0],
       [0, 0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0, 0]])

In [159]: onehot_sparse(a-1).toarray()
Out[159]: 
array([[1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0],
       [1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0]])
对于基于
0的
索引,它将是-

In [122]: (np.arange(a.max()+1) == a[...,None]).astype(int)
Out[122]: 
array([[[0, 1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 1],
        [0, 0, 0, 0, 0, 1, 0, 0],
        [0, 0, 0, 1, 0, 0, 0, 0]],

       [[0, 0, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0],
        [0, 1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0]]])
如果一个热编码要覆盖从最小值到最大值的值范围,则用最小值进行偏移,然后将其馈送到基于
0
索引的建议方法。这也适用于本文后面讨论的其他方法

这是一个在同一台计算机上运行的示例-

In [223]: a
Out[223]: 
array([[ 6, 12, 10,  8],
       [ 7,  9,  6,  9]])

In [224]: a_off = a - a.min() # feed a_off to proposed approaches

In [225]: (np.arange(a_off.max()+1) == a_off[...,None]).astype(int)
Out[225]: 
array([[[1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 1],
        [0, 0, 0, 0, 1, 0, 0],
        [0, 0, 1, 0, 0, 0, 0]],

       [[0, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 1, 0, 0, 0],
        [1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 1, 0, 0, 0]]])
如果对于
1的
True
0的
布尔数组,您可以跳过
.astype(int)
转换

进近#2 我们还可以初始化一个零数组,并使用索引将其索引到输出中。因此,对于基于
0的
索引,我们将-

def onehot_initialization(a):
    ncols = a.max()+1
    out = np.zeros(a.shape + (ncols,), dtype=int)
    out[all_idx(a, axis=2)] = 1
    return out
辅助函数-

# https://stackoverflow.com/a/46103129/ @Divakar
def all_idx(idx, axis):
    grid = np.ogrid[tuple(map(slice, idx.shape))]
    grid.insert(axis, idx)
    return tuple(grid)
在处理更大范围的值时,这应该特别有效

对于基于
1的
索引,只需输入
a-1

方法#3:稀疏矩阵解 现在,如果您正在寻找稀疏数组作为输出和AFAIK,因为scipy的内置稀疏矩阵只支持
2D
格式,那么您可以得到一个稀疏输出,它是前面显示的输出的一个重塑版本,前两个轴合并,第三个轴保持不变。基于
0的
索引的实现如下所示-

from scipy.sparse import coo_matrix
def onehot_sparse(a):
    N = a.size
    L = a.max()+1
    data = np.ones(N,dtype=int)
    return coo_matrix((data,(np.arange(N),a.ravel())), shape=(N,L))
同样,对于基于
1的
索引,只需输入
a-1

样本运行-

In [120]: a
Out[120]: 
array([[1, 7, 5, 3],
       [2, 4, 1, 4]])

In [121]: (np.arange(a.max()) == a[...,None]-1).astype(int)
Out[121]: 
array([[[1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 1],
        [0, 0, 0, 0, 1, 0, 0],
        [0, 0, 1, 0, 0, 0, 0]],

       [[0, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 1, 0, 0, 0],
        [1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 1, 0, 0, 0]]])
In [157]: a
Out[157]: 
array([[1, 7, 5, 3],
       [2, 4, 1, 4]])

In [158]: onehot_sparse(a).toarray()
Out[158]: 
array([[0, 1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 1, 0, 0, 0, 0],
       [0, 0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0, 0]])

In [159]: onehot_sparse(a-1).toarray()
Out[159]: 
array([[1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0],
       [1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0]])
如果您同意使用稀疏输出,那么这将比前两种方法要好得多

基于0的索引的运行时比较

案例1:

案例2:

挤出最佳性能 为了获得最佳性能,我们可以修改方法#2,在
2D
形状的输出数组上使用索引,还可以使用
uint8
dtype来提高内存效率和更快的分配,就像这样-

def onehot_initialization_v2(a):
    ncols = a.max()+1
    out = np.zeros( (a.size,ncols), dtype=np.uint8)
    out[np.arange(a.size),a.ravel()] = 1
    out.shape = a.shape + (ncols,)
    return out
时间安排-

In [178]: a = np.random.randint(0,100,(100,100))

In [179]: %timeit onehot_initialization(a)
     ...: %timeit onehot_initialization_v2(a)
     ...: 
1000 loops, best of 3: 474 µs per loop
10000 loops, best of 3: 128 µs per loop

In [180]: a = np.random.randint(0,500,(100,100))

In [181]: %timeit onehot_initialization(a)
     ...: %timeit onehot_initialization_v2(a)
     ...: 
100 loops, best of 3: 2.38 ms per loop
1000 loops, best of 3: 213 µs per loop
编辑:我刚刚意识到我的答案已经包含在被接受的答案中了。不幸的是,作为一个未注册的用户,我不能再删除它了

作为对已接受答案的补充:如果要编码的类数量非常少,并且可以接受
np.bool
数组作为输出,我发现以下方法甚至更快一些:

def onehot_initialization_v3(a):
    ncols = a.max() + 1
    labels_one_hot = (a.ravel()[np.newaxis] == np.arange(ncols)[:, np.newaxis]).T
    labels_one_hot.shape = a.shape + (ncols,)
    return labels_one_hot
时间安排(10节课):

但是,如果类的数量增加(现在为100个类),则会发生变化:


因此,根据您的问题,两者都可能是更快的版本。

如果您试图为您的机器学习模型创建一个热张量(您安装了
tensorflow
keras
),那么您可以从或使用
one_hot
功能

这就是我正在使用的,并且对高维数据很有效

下面是使用示例:

>>将tensorflow作为tf导入
>>>tf.one_hot([[0,2],[1,3]],4).numpy()
数组([[1,0,0,0.]),
[0., 0., 1., 0.]],
[[0., 1., 0., 0.],
[0,0,0,1.]],dtype=float32)

我称之为Numpythonic!实际上,您可能不需要这里的
.astype(int)
,因为这会导致不必要的复制。这更像是针对OP的注释,以及他们可能首先需要布尔数组的方式。作为一个
np.int8
查看可能会省去很多不厚颜无耻的内容!我认为这真的是一个很棒的技巧,我只想与大家分享一下,你甚至可以通过对魔线稍加修改,使它能够处理数组或任意数量的维度:
(np.arange(a.max())==a[…,None]-1)。astype(int)
def onehot_initialization_v3(a):
    ncols = a.max() + 1
    labels_one_hot = (a.ravel()[np.newaxis] == np.arange(ncols)[:, np.newaxis]).T
    labels_one_hot.shape = a.shape + (ncols,)
    return labels_one_hot
a = np.random.randint(0,10,(100,100))
assert np.all(onehot_initialization_v2(a) == onehot_initialization_v3(a))
%timeit onehot_initialization_v2(a)
%timeit onehot_initialization_v3(a)

# 102 µs ± 1.66 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
# 79.3 µs ± 815 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
a = np.random.randint(0,100,(100,100))
assert np.all(onehot_initialization_v2(a) == one_hot_initialization_v3(a))
%timeit onehot_initialization_v2(a)
%timeit onehot_initialization_v3(a)

# 132 µs ± 1.4 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
# 639 µs ± 3.12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)