Converter matriz de índices em matriz numpy codificada a 1 quente

227

Digamos que eu tenho uma matriz numpy 1d

a = array([1,0,3])

Gostaria de codificar isso como uma matriz 2d 1-hot

b = array([[0,1,0,0], [1,0,0,0], [0,0,0,1]])

Existe uma maneira rápida de fazer isso? Mais rápido do que apenas fazer um loop apara definir elementos b, é isso.

James Atwood
fonte

Respostas:

395

Sua matriz adefine as colunas dos elementos diferentes de zero na matriz de saída. Você também precisa definir as linhas e usar a indexação sofisticada:

>>> a = np.array([1, 0, 3])
>>> b = np.zeros((a.size, a.max()+1))
>>> b[np.arange(a.size),a] = 1
>>> b
array([[ 0.,  1.,  0.,  0.],
       [ 1.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.]])
YXD
fonte
111
Lindo. Generalizando um pouco b = np.zeros((a.size, a.max()+1)):, então `b [np.arange (a.size), a] = 1`
James Atwood
10
@ JamesAtwood depende da aplicação, mas eu faria o parâmetro max e não o calcularia a partir dos dados.
Mohammad Moghimi
1
@MohammadMoghimi Claro, faz sentido para mim.
James Atwood
7
e se 'a' fosse 2d? e você quer uma matriz one-hot 3-d?
AD
8
Alguém pode apontar para uma explicação de por que isso funciona, mas a fatia com [:, a] não?
N. McA.
168
>>> values = [1, 0, 3]
>>> n_values = np.max(values) + 1
>>> np.eye(n_values)[values]
array([[ 0.,  1.,  0.,  0.],
       [ 1.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.]])
K3 --- rnc
fonte
9
Esta solução é a única útil para uma matriz ND de entrada em uma matriz N + 1D quente. Exemplo: input_matrix = np.asarray ([[0,1,1], [1,1,2]]); np.eye (3) [input_matrix] # output tensor 3D
Isaías
5
+1 porque isso deve ser preferido em relação à solução aceita. Para uma solução mais geral, porém, valuesdeve ser um array Numpy, em vez de uma lista Python, e funciona em todas as dimensões, não apenas em 1D.
21417 Alex
8
Observe que tomar np.max(values) + 1como número de buckets pode não ser desejável se o seu conjunto de dados for amostrado aleatoriamente e, por acaso, ele pode não conter o valor máximo. O número de buckets deve ser um parâmetro e pode ser feita uma asserção / verificação para verificar se cada valor está dentro de 0 (incl) e a contagem de buckets (excl).
precisa saber é o seguinte
2
Para mim, essa solução é a melhor e pode ser facilmente generalizada para qualquer tensor: def one_hot (x, depth = 10): return np.eye (depth) [x]. Observe que fornecer o tensor x como índice retorna um tensor de linhas de olho x.shape.
Cecconeurale 27/03
4
Maneira fácil de "entender" esta solução e por que ela funciona para N-dims (sem ler numpydocumentos): em cada local na matriz original ( values), temos um número inteiro ke "colocamos" o vetor 1 quente eye(n)[k]nesse local . Isso adiciona uma dimensão, porque estamos "colocando" um vetor no local de um escalar na matriz original.
avivr 24/09/19
35

Caso você esteja usando keras, existe um utilitário interno para isso:

from keras.utils.np_utils import to_categorical   

categorical_labels = to_categorical(int_labels, num_classes=3)

E faz praticamente o mesmo que a resposta do @ YXD (consulte o código-fonte ).

Jodo
fonte
32

Aqui está o que eu acho útil:

def one_hot(a, num_classes):
  return np.squeeze(np.eye(num_classes)[a.reshape(-1)])

Aqui num_classesrepresenta o número de classes que você tem. Portanto, se você tiver um avetor com formato de (10000,), essa função o transforma em (10000, C) . Observe que aé indexado a zero, ou seja one_hot(np.array([0, 1]), 2), dará[[1, 0], [0, 1]] .

Exatamente o que você queria ter, eu acredito.

PS: a fonte é modelos de sequência - deeplearning.ai

D.Samchuk
fonte
Além disso, qual é o motivo de fazer np.squeeze () desde que obtenha o (tamanho do vetor a) muitas matrizes codificadas quentes usando np.eye(num_classes)[a.reshape(-1)]. What you are simply doing is using np.eye`, você está criando uma matriz diagonal com cada índice de classe como 1 resto zero e posteriormente usando os índices fornecidos por a.reshape(-1)produzir a saída que corresponde ao índice de np.eye(). Eu não entendi a necessidade de np.sqeezeuma vez que usá-lo para simplesmente remover dimensões individuais que nunca teremos como na dimensão da saída será sempre(a_flattened_size, num_classes)
Anu
27

Você pode usar sklearn.preprocessing.LabelBinarizer :

Exemplo:

import sklearn.preprocessing
a = [1,0,3]
label_binarizer = sklearn.preprocessing.LabelBinarizer()
label_binarizer.fit(range(max(a)+1))
b = label_binarizer.transform(a)
print('{0}'.format(b))

resultado:

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

Entre outras coisas, você pode inicializar sklearn.preprocessing.LabelBinarizer()para que a saída de transformseja esparsa.

Franck Dernoncourt
fonte
21

Você também pode usar a função ocular de numpy:

numpy.eye(number of classes)[vector containing the labels]

Carma
fonte
1
Para maior clareza, o uso np.identity(num_classes)[indices]pode ser melhor. Boa resposta!
Oliver Oliver
5

Aqui está uma função que converte um vetor 1-D em um array 2-D one-hot.

#!/usr/bin/env python
import numpy as np

def convertToOneHot(vector, num_classes=None):
    """
    Converts an input 1-D vector of integers into an output
    2-D array of one-hot vectors, where an i'th input value
    of j will set a '1' in the i'th row, j'th column of the
    output array.

    Example:
        v = np.array((1, 0, 4))
        one_hot_v = convertToOneHot(v)
        print one_hot_v

        [[0 1 0 0 0]
         [1 0 0 0 0]
         [0 0 0 0 1]]
    """

    assert isinstance(vector, np.ndarray)
    assert len(vector) > 0

    if num_classes is None:
        num_classes = np.max(vector)+1
    else:
        assert num_classes > 0
        assert num_classes >= np.max(vector)

    result = np.zeros(shape=(len(vector), num_classes))
    result[np.arange(len(vector)), vector] = 1
    return result.astype(int)

Abaixo está um exemplo de uso:

>>> a = np.array([1, 0, 3])

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

>>> convertToOneHot(a, num_classes=10)
array([[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
       [1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]])
stackoverflowuser2010
fonte
Observe que isso funciona apenas em vetores (e não há assertcomo verificar a forma do vetor;)).
Johndodo 12/05
1
+1 para a abordagem generalizada e verificação de parâmetros. No entanto, como prática comum, sugiro NÃO usar declarações para realizar verificações nas entradas. Use afirmações apenas para verificar as condições intermediárias internas. Em vez disso, converta tudo assert ___em if not ___ raise Exception(<Reason>).
fnunnari 23/09/19
3

Para codificação 1 quente

   one_hot_encode=pandas.get_dummies(array)

Por exemplo

APRECIE CODIFICAÇÃO

Shubham Mishra
fonte
Obrigado pelo comentário, mas uma breve descrição do que o código está fazendo seria muito útil!
Clarus
consulte o exemplo
Shubham Mishra 10/04
@Clarus Checkout no exemplo abaixo. Você pode acessar a uma codificação quente de cada valor em sua matriz np executando um one_hot_encode [value]. >>> import numpy as np >>> import pandas >>> a = np.array([1,0,3]) >>> one_hot_encode=pandas.get_dummies(a) >>> print(one_hot_encode) 0 1 3 0 0 1 0 1 1 0 0 2 0 0 1 >>> print(one_hot_encode[1]) 0 1 1 0 2 0 Name: 1, dtype: uint8 >>> print(one_hot_encode[0]) 0 0 1 1 2 0 Name: 0, dtype: uint8 >>> print(one_hot_encode[3]) 0 0 1 0 2 1 Name: 3, dtype: uint8
Deepak
2

Eu acho que a resposta curta é não. Para um caso mais genérico em ndimensões, vim com isso:

# For 2-dimensional data, 4 values
a = np.array([[0, 1, 2], [3, 2, 1]])
z = np.zeros(list(a.shape) + [4])
z[list(np.indices(z.shape[:-1])) + [a]] = 1

Gostaria de saber se existe uma solução melhor - não gosto que tenha que criar essas listas nas duas últimas linhas. De qualquer forma, fiz algumas medições timeite parece que as versões com numpybase ( indices/ arange) e iterativa têm o mesmo desempenho.

David Nemeskey
fonte
2

Apenas para elaborar a excelente resposta do K3 --- rnc , aqui está uma versão mais genérica:

def onehottify(x, n=None, dtype=float):
    """1-hot encode x with the max value n (computed from data if n is None)."""
    x = np.asarray(x)
    n = np.max(x) + 1 if n is None else n
    return np.eye(n, dtype=dtype)[x]

Além disso, aqui está uma referência rápida e suja desse método e um método da resposta atualmente aceita pelo YXD (ligeiramente alterada, para que eles ofereçam a mesma API, exceto que o último funciona apenas com ndarrays 1D):

def onehottify_only_1d(x, n=None, dtype=float):
    x = np.asarray(x)
    n = np.max(x) + 1 if n is None else n
    b = np.zeros((len(x), n), dtype=dtype)
    b[np.arange(len(x)), x] = 1
    return b

O último método é ~ 35% mais rápido (MacBook Pro 13 2015), mas o primeiro é mais geral:

>>> import numpy as np
>>> np.random.seed(42)
>>> a = np.random.randint(0, 9, size=(10_000,))
>>> a
array([6, 3, 7, ..., 5, 8, 6])
>>> %timeit onehottify(a, 10)
188 µs ± 5.03 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit onehottify_only_1d(a, 10)
139 µs ± 2.78 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Emil Melnikov
fonte
2

Você pode usar o seguinte código para converter em um vetor quente:

deixe x é o vetor de classe normal com uma única coluna com classes 0 a algum número:

import numpy as np
np.eye(x.max()+1)[x]

se 0 não é uma classe; depois remova o +1.

Inaam Ilahi
fonte
1

Recentemente, encontrei um problema do mesmo tipo e encontrei a referida solução, que acabou sendo satisfatória apenas se você tiver números que se enquadram em uma determinada formação. Por exemplo, se você deseja codificar com hot-list a seguinte lista:

all_good_list = [0,1,2,3,4]

vá em frente, as soluções publicadas já são mencionadas acima. Mas e se considerar esses dados:

problematic_list = [0,23,12,89,10]

Se você fizer isso com os métodos mencionados acima, provavelmente terminará com 90 colunas de uma só vez. Isso ocorre porque todas as respostas incluem algo parecido n = np.max(a)+1. Encontrei uma solução mais genérica que funcionou para mim e queria compartilhar com você:

import numpy as np
import sklearn
sklb = sklearn.preprocessing.LabelBinarizer()
a = np.asarray([1,2,44,3,2])
n = np.unique(a)
sklb.fit(n)
b = sklb.transform(a)

Espero que alguém tenha encontrado as mesmas restrições nas soluções acima e isso possa ser útil

Hans T
fonte
1

Esse tipo de codificação geralmente faz parte de um array numpy. Se você estiver usando uma matriz numpy como esta:

a = np.array([1,0,3])

então existe uma maneira muito simples de converter isso em codificação 1-hot

out = (np.arange(4) == a[:,None]).astype(np.float32)

É isso aí.

Sudeep K Rana
fonte
1
  • p será uma segunda matriz.
  • Queremos saber qual valor é o mais alto consecutivo, colocar 1 e em qualquer outro lugar 0.

solução limpa e fácil:

max_elements_i = np.expand_dims(np.argmax(p, axis=1), axis=1)
one_hot = np.zeros(p.shape)
np.put_along_axis(one_hot, max_elements_i, 1, axis=1)
MiFi
fonte
1

Usando uma etapa do pipeline Neuraxle :

  1. Configure seu exemplo
import numpy as np
a = np.array([1,0,3])
b = np.array([[0,1,0,0], [1,0,0,0], [0,0,0,1]])
  1. Faça a conversão real
from neuraxle.steps.numpy import OneHotEncoder
encoder = OneHotEncoder(nb_columns=4)
b_pred = encoder.transform(a)
  1. Afirme que funciona
assert b_pred == b

Link para a documentação: neuraxle.steps.numpy.OneHotEncoder

Guillaume Chevalier
fonte
0

Aqui está um exemplo de função que eu escrevi para fazer isso com base nas respostas acima e no meu próprio caso de uso:

def label_vector_to_one_hot_vector(vector, one_hot_size=10):
    """
    Use to convert a column vector to a 'one-hot' matrix

    Example:
        vector: [[2], [0], [1]]
        one_hot_size: 3
        returns:
            [[ 0.,  0.,  1.],
             [ 1.,  0.,  0.],
             [ 0.,  1.,  0.]]

    Parameters:
        vector (np.array): of size (n, 1) to be converted
        one_hot_size (int) optional: size of 'one-hot' row vector

    Returns:
        np.array size (vector.size, one_hot_size): converted to a 'one-hot' matrix
    """
    squeezed_vector = np.squeeze(vector, axis=-1)

    one_hot = np.zeros((squeezed_vector.size, one_hot_size))

    one_hot[np.arange(squeezed_vector.size), squeezed_vector] = 1

    return one_hot

label_vector_to_one_hot_vector(vector=[[2], [0], [1]], one_hot_size=3)
Aaron Lelevier
fonte
0

Estou adicionando para concluir uma função simples, usando apenas operadores numpy:

   def probs_to_onehot(output_probabilities):
        argmax_indices_array = np.argmax(output_probabilities, axis=1)
        onehot_output_array = np.eye(np.unique(argmax_indices_array).shape[0])[argmax_indices_array.reshape(-1)]
        return onehot_output_array

Toma como entrada uma matriz de probabilidade: por exemplo:

[[0,03038822 0,65810204 0,16549407 0,3797123] ... [0,02771272 0,2760752 0,3280924 0,33458805]]

E vai voltar

[[0 1 0 0] ... [0 0 0 1]]

Jordy Van Landeghem
fonte
0

Aqui está uma solução independente de dimensionalidade.

Isso converterá qualquer matriz N-dimensional arrde números inteiros não negativos em uma matriz N-1 dimensional quente one_hot, onde one_hot[i_1,...,i_N,c] = 1significa arr[i_1,...,i_N] = c. Você pode recuperar a entrada vianp.argmax(one_hot, -1)

def expand_integer_grid(arr, n_classes):
    """

    :param arr: N dim array of size i_1, ..., i_N
    :param n_classes: C
    :returns: one-hot N+1 dim array of size i_1, ..., i_N, C
    :rtype: ndarray

    """
    one_hot = np.zeros(arr.shape + (n_classes,))
    axes_ranges = [range(arr.shape[i]) for i in range(arr.ndim)]
    flat_grids = [_.ravel() for _ in np.meshgrid(*axes_ranges, indexing='ij')]
    one_hot[flat_grids + [arr.ravel()]] = 1
    assert((one_hot.sum(-1) == 1).all())
    assert(np.allclose(np.argmax(one_hot, -1), arr))
    return one_hot
eqzx
fonte
0

Use o seguinte código. Funciona melhor.

def one_hot_encode(x):
"""
    argument
        - x: a list of labels
    return
        - one hot encoding matrix (number of labels, number of class)
"""
encoded = np.zeros((len(x), 10))

for idx, val in enumerate(x):
    encoded[idx][val] = 1

return encoded

Encontre aqui PS Você não precisa entrar no link.

Inaam Ilahi
fonte
5
Você deve evitar o uso de loops com numpy
Kenan