Python 一个带>;的热编码numpy阵列;2暗

Python 一个带>;的热编码numpy阵列;2暗,python,numpy,machine-learning,one-hot-encoding,Python,Numpy,Machine Learning,One Hot Encoding,我有一个numpy数组的形状(1922241921)。最后一个维度是整数类,我想对其进行热编码。例如,如果我有12个类,我希望得到的数组的值是(192224192,12),最后一个维度是全零,但在与原始值对应的索引处是1 我可以用许多for循环天真地做到这一点,但我想知道是否有更好的方法来做到这一点。SciKit learn有一个编码器: from sklearn.preprocessing import OneHotEncoder # Data values = np.array([1, 3

我有一个numpy数组的形状
(1922241921)
。最后一个维度是整数类,我想对其进行热编码。例如,如果我有12个类,我希望得到的数组的值是
(192224192,12)
,最后一个维度是全零,但在与原始值对应的索引处是1


我可以用许多
for
循环天真地做到这一点,但我想知道是否有更好的方法来做到这一点。

SciKit learn有一个编码器:

from sklearn.preprocessing import OneHotEncoder

# Data
values = np.array([1, 3, 2, 4, 1, 2, 1, 3, 5])
val_reshape = values.reshape(len(values), 1)

# One-hot encoding
oh = OneHotEncoder(sparse = False) 
oh_arr = oh.fit_transform(val_reshape)

print(oh_arr)

output: 
[[1. 0. 0. 0. 0.]
 [0. 0. 1. 0. 0.]
 [0. 1. 0. 0. 0.]
 [0. 0. 0. 1. 0.]
 [1. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0.]
 [1. 0. 0. 0. 0.]
 [0. 0. 1. 0. 0.]
 [0. 0. 0. 0. 1.]]


您可以创建一个新的零数组并使用高级索引填充它

# sample array with 12 classes
np.random.seed(123)
a = np.random.randint(0, 12, (192, 224, 192, 1))

b = np.zeros((a.size, a.max() + 1))

# use advanced indexing to get one-hot encoding
b[np.arange(a.size), a.ravel()] = 1

# reshape to original form
b = b.reshape(a.shape[:-1] + (a.max() + 1,))

print(b.shape)
print(a[0, 0, 0])
print(b[0, 0, 0])
输出

(192, 224, 192, 12)
[2]
[0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

与数组整形类似,但与数组整形类似。

如果已知给定数组
a
m=a.max()+1的最大值,则可以在单个索引操作中执行此操作:

out = np.zeros(a.shape[:-1] + (m,), dtype=bool)
out[(*np.indices(a.shape[:-1], sparse=True), a[..., 0])] = True
如果删除不必要的尾部尺寸标注,则更容易:

a = np.squeeze(a)
out = np.zeros(a.shape + (m,), bool)
out[(*np.indices(a.shape, sparse=True), a)] = True
def onehot2(a, axis=None, dtype=bool):
    shape = np.array(a.shape)
    if axis is None:
        axis = (shape == 1).argmax()
    if shape[axis] != 1:
        raise ValueError(f'Dimension at {axis} is non-singleton')
    shape[axis] = a.max() + 1
    out = np.zeros(shape, dtype)
    ind = list(np.indices(a.shape, sparse=True))
    ind[axis] = a
    out[tuple(ind)] = True
    return out
索引中的显式元组是进行星型扩展所必需的

如果要将其扩展到任意维度,也可以这样做。以下内容将在轴
处的压缩数组中插入一个新维度。此处
是新轴在最终数组中的位置,与say
np.stack
一致,但与
列表不一致。插入

def onehot(a, axis=-1, dtype=bool):
    pos = axis if axis >= 0 else a.ndim + axis + 1
    shape = list(a.shape)
    shape.insert(pos, a.max() + 1)
    out = np.zeros(shape, dtype)
    ind = list(np.indices(a.shape, sparse=True))
    ind.insert(pos, a)
    out[tuple(ind)] = True
    return out
如果要展开单个维度,则通用解决方案可以找到第一个可用的单个维度:

a = np.squeeze(a)
out = np.zeros(a.shape + (m,), bool)
out[(*np.indices(a.shape, sparse=True), a)] = True
def onehot2(a, axis=None, dtype=bool):
    shape = np.array(a.shape)
    if axis is None:
        axis = (shape == 1).argmax()
    if shape[axis] != 1:
        raise ValueError(f'Dimension at {axis} is non-singleton')
    shape[axis] = a.max() + 1
    out = np.zeros(shape, dtype)
    ind = list(np.indices(a.shape, sparse=True))
    ind[axis] = a
    out[tuple(ind)] = True
    return out
要使用最后一个可用的单例,请将轴=(形状==1).argmax()替换为

axis = a.ndim - 1 - (shape[::-1] == 1).argmax()
以下是一些示例用法:

>>> np.random.seed(0x111)
>>> x = np.random.randint(5, size=(3, 2))
>>> x
array([[2, 3],
       [3, 1],
       [4, 0]])

>>> a = onehot(x, axis=-1, dtype=int)
>>> a.shape
(3, 2, 5)
>>> a
array([[[0, 0, 1, 0, 0],    # 2
        [0, 0, 0, 1, 0]],   # 3

       [[0, 0, 0, 1, 0],    # 3
        [0, 1, 0, 0, 0]],   # 1

       [[0, 0, 0, 0, 1],    # 4
        [1, 0, 0, 0, 0]]]   # 0

>>> b = onehot(x, axis=-2, dtype=int)
>>> b.shape
(3, 5, 2)
>>> b
array([[[0, 0],
        [0, 0],
        [1, 0],
        [0, 1],
        [0, 0]],

       [[0, 0],
        [0, 1],
        [0, 0],
        [1, 0],
        [0, 0]],

       [[0, 1],
        [0, 0],
        [0, 0],
        [0, 0],
        [1, 0]]])
onehot2
要求您将要添加的维度标记为单个维度:

>>> np.random.seed(0x111)
>>> y = np.random.randint(5, size=(3, 1, 2, 1))
>>> y
array([[[[2],
         [3]]],
       [[[3],
         [1]]],
       [[[4],
         [0]]]])

>>> c = onehot2(y, axis=-1, dtype=int)
>>> c.shape
(3, 1, 2, 5)
>>> c
array([[[[0, 0, 1, 0, 0],
         [0, 0, 0, 1, 0]]],

       [[[0, 0, 0, 1, 0],
         [0, 1, 0, 0, 0]]],

       [[[0, 0, 0, 0, 1],
         [1, 0, 0, 0, 0]]]])

>>> d = onehot2(y, axis=-2, dtype=int)
ValueError: Dimension at -2 is non-singleton

>>> e = onehot2(y, dtype=int)
>>> e.shape
(3, 5, 2, 1)
>>> e.squeeze()
array([[[0, 0],
        [0, 0],
        [1, 0],
        [0, 1],
        [0, 0]],

       [[0, 0],
        [0, 1],
        [0, 0],
        [1, 0],
        [0, 0]],

       [[0, 1],
        [0, 0],
        [0, 0],
        [0, 0],
        [1, 0]]])

如果不这样做,总索引数组会更短reshape@RitchieV. 我已经发布了一个答案,并将其推广到任意维度。请告诉我您是否有机会玩它。我是从手机上发的,所以不能保证代码是无错误的。这个答案很好地解决了我的问题。我唯一需要做的更改是将
a.max()+1
更改为我拥有的类的数量。这个特定的ML问题是分割,所以整个数组是我的标签,但不是每个类都表示在每个标签中,所以它必须是硬编码的。@ PDPDPDPD考虑了投票的MAD的答案,它实际上是更好的性能,包括一个通用函数,很高兴您修复了代码!看到使用
np.index
非常有趣,我需要获得更多关于fancy的经验indexing@RichieV. 我已还原了您的编辑。
onehot
中的索引是故意这样做的。它的意思是在
a.squeak
上操作,而不是问题中的
a
。但是关于这个错误你是对的:)@RichieV。我已经添加了一些示例来展示这两个函数是如何使用的,这符合您测试的精神。谢谢您的代码。这个答案非常棒,与其他一些答案相比速度非常快。@pdpd。里奇耶夫的答案非常相似。如果速度很重要的话,我会把它和我的进行比较。撕开和拆开是非常便宜的,因为它们不复制内存。