将2D矩阵转换为3D One Hot Matrix Numpy

yhuiod9q  于 11个月前  发布在  其他
关注(0)|答案(5)|浏览(151)

我有np矩阵,我想把它转换为一个三维数组与一个热编码的元素作为第三维。有没有一种方法来做,而不是在每一行循环,例如

a=[[1,3],
   [2,4]]

字符串
要办成

b=[[1,0,0,0], [0,0,1,0],
   [0,1,0,0], [0,0,0,1]]

ubof19bj

ubof19bj1#

方法一

这里有一个厚颜无耻的一行字,滥用broadcasted比较-

(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]]])


对于0-based索引,它将是-

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]]])


如果one-hot encoding是覆盖从最小值到最大值的值范围,那么用最小值进行偏移,然后将其馈送到0-based索引的建议方法。这也适用于本文后面讨论的其他方法。
这里有一个样本运行在相同的-

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'sTrue0's的False的布尔数组没有问题,你可以跳过.astype(int)的转换。

方法二

我们也可以初始化一个零数组,并使用advanced-indexing索引到输出中。因此,对于0-based索引,我们将有-

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-based索引,只需将a-1作为输入。

方法三:稀疏矩阵求解

现在,如果你正在寻找稀疏数组作为输出和AFAIK,因为scipy的内置稀疏矩阵只支持2D,你可以得到一个稀疏输出,它是前面显示的输出的整形版本,前两个轴合并,第三个轴保持不变。0-based索引的实现看起来像这样-

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-based索引,只需将a-1作为输入。
样品运行-

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:

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

In [161]: %timeit (np.arange(a.max()+1) == a[...,None]).astype(int)
1000 loops, best of 3: 1.51 ms per loop

In [162]: %timeit onehot_initialization(a)
1000 loops, best of 3: 478 µs per loop

In [163]: %timeit onehot_sparse(a)
10000 loops, best of 3: 87.5 µs per loop

In [164]: %timeit onehot_sparse(a).toarray()
1000 loops, best of 3: 530 µs per loop


案例二:

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

In [167]: %timeit (np.arange(a.max()+1) == a[...,None]).astype(int)
100 loops, best of 3: 8.51 ms per loop

In [168]: %timeit onehot_initialization(a)
100 loops, best of 3: 2.52 ms per loop

In [169]: %timeit onehot_sparse(a)
10000 loops, best of 3: 87.1 µs per loop

In [170]: %timeit onehot_sparse(a).toarray()
100 loops, best of 3: 2.67 ms per loop

挤出最佳性能

为了挤出最佳性能,我们可以修改方法#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

crcmnpdw

crcmnpdw2#

如果您尝试为机器学习模型创建独热Tensor(您安装了tensorflowkeras),则可以从https://www.tensorflow.org/api_docs/python/tf/keras/backend/one_hothttps://www.tensorflow.org/api_docs/python/tf/one_hot使用one_hot函数
这就是我正在使用的,并且适用于高维数据。
下面是示例用法:

>>> import tensorflow as tf

>>> tf.one_hot([[0,2],[1,3]], 4).numpy()
array([[[1., 0., 0., 0.],
        [0., 0., 1., 0.]],

       [[0., 1., 0., 0.],
        [0., 0., 0., 1.]]], dtype=float32)

字符串

lsmd5eda

lsmd5eda3#

  • 编辑:* 我刚刚意识到我的答案已经包含在接受的答案中。不幸的是,作为未注册的用户,我不能再删除它。

作为对公认答案的补充:如果你有非常少的类要编码,并且如果你可以接受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节课):

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)


但是,如果班级数量增加(现在为100个班级),情况就会发生变化:

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)


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

epfja78i

epfja78i4#

下面是使用np.eye(单位矩阵)和numpy强大索引的最简单和最优雅的解决方案:

labels_3d = np.eye(N_CLASSES)[labels_2d]

字符串


的数据

fivyi3re

fivyi3re5#

我想贡献一个简单的高级索引解决方案,这是在一些其他的答案,在一个更容易访问的方式,而不利用“助手函数”和所有这一切。
我们有一个2维数组a,它是分类的。为了简单起见,让我们把它看作是二进制的,例如:

a = (np.random.rand(40) > 0.5).astype(int).reshape(5, 8)

print(a)
>>>    array([
       [1, 0, 1, 1, 0, 1, 1, 0],
       [0, 1, 1, 0, 0, 1, 0, 0],
       [0, 0, 0, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 0, 1, 1, 1],
       [1, 0, 1, 1, 0, 0, 0, 0]])

字符串
我们通过将其初始化为零来创建一个热编码数组:

onehot = np.zeros((5, 8, 2))


我们做高级索引来提取我们想把1放在one-hot编码数组上的位置。

x = np.arange(5).reshape(5, 1)
y = np.arange(8).reshape(1, 8)


重塑是为了使它们可以被广播。

onehot[x, y, a] = 1.


将首先广播x,y和a,它们是可广播到shape(5,8)的。然后它将提取该shape的数组,其值取自onehot数组。它将使该数组成为一个数组,并将这些更改拉回到onehot数组。
在最后一个命令中,我们告诉numpy在数组x,y,a给出的特定坐标中进行赋值。它们对应于onehot的3个维度。赋值的数量由广播的数组x,y,a中的元素数量决定。在这种情况下,它是形状(5,8),所以我们进行40次赋值。每个赋值都有自己的二维坐标,让我们用i和j来捐赠它们。赋值(i,j)在onehot中有目标索引,由(x[i, j], y[i, j], a[i, j])给出,额外的坐标用广播版本解释。x[i,j]只是i,y[i,j]是j。所以我们实际上是以onehot[i,j,a[i,j]]为目标。我们使用a作为最后一个坐标,这是第i,j '个条目的onehot目标。换句话说,第(i,j)^个命令是翻转onehot数组中对应于第(i,j)^个条目的所需类。

相关问题