web-dev-qa-db-fra.com

Conversion d'un tableau d'indices en tableau numpy codé à chaud

Disons que j'ai un tableau 1p numpy

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

Je voudrais encoder ceci comme un tableau 2d 1-hot 

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

Y at-il un moyen rapide de faire cela? Plus rapide que de simplement parcourir a pour définir les éléments de b, c’est-à-dire.

137
James Atwood

Votre tableau a définit les colonnes des éléments non nuls du tableau en sortie. Vous devez également définir les lignes, puis utiliser une indexation sophistiquée:

>>> a = np.array([1, 0, 3])
>>> b = np.zeros((3, 4))
>>> b[np.arange(3), a] = 1
>>> b
array([[ 0.,  1.,  0.,  0.],
       [ 1.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.]])
267
YXD
>>> 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.]])
113
K3---rnc

Vous pouvez utiliser sklearn.preprocessing.LabelBinarizer :

Exemple:

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

sortie:

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

Entre autres choses, vous pouvez initialiser sklearn.preprocessing.LabelBinarizer() afin que la sortie de transform soit rare.

24
Franck Dernoncourt

Voici ce que je trouve utile:

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

Ici, num_classes représente le nombre de classes que vous avez. Donc, si vous avez un vecteur a de forme (10000,), cette fonction le transforme en (10000, C). Notez que a est indexé à zéro, c'est-à-dire que one_hot(np.array([0, 1]), 2) donnera [[1, 0], [0, 1]].

Je crois que c'est exactement ce que tu voulais avoir.

PS: la source est Modèles de séquence - deeplearning.ai

21
D.Samchuk

Si vous utilisez keras, il existe un utilitaire intégré pour cela:

from keras.utils.np_utils import to_categorical   

categorical_labels = to_categorical(int_labels, num_classes=3)

Et il fait à peu près la même chose que @ YXD répond (voir code-source ).

18
Jodo

numpy.eye (taille de la classe) [vecteur à convertir]

5
Karma

Voici une fonction qui convertit un vecteur unidimensionnel en une matrice bidimensionnelle à une densité.

#!/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)

Ci-dessous quelques exemples d'utilisation:

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

Je pense que la réponse courte est non. Pour un cas plus générique dans les dimensions n, j'ai proposé ceci:

# 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

Je me demande s'il existe une meilleure solution. Je n'aime pas être obligée de créer ces listes dans les deux dernières lignes. Quoi qu'il en soit, j'ai fait quelques mesures avec timeit et il semble que la version numpy- (indices/arange) et les versions itératives fonctionnent à peu près de la même façon. 

2
David Nemeskey

Vous pouvez utiliser le code suivant pour convertir en un vecteur one-hot:

let x est le vecteur de classe normal ayant une seule colonne avec les classes 0 à un certain nombre:

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

si 0 n'est pas une classe; puis supprimez +1.

1
Inaam Ilahi

J'ai récemment rencontré un problème du même genre et j'ai trouvé cette solution qui ne s'est révélée satisfaisante que si vous avez des chiffres correspondant à une certaine formation. Par exemple, si vous voulez encoder une liste à chaud dans la liste suivante:

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

allez-y, les solutions affichées sont déjà mentionnées ci-dessus. Mais qu'en est-il si on considère ces données:

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

Si vous le faites avec les méthodes mentionnées ci-dessus, vous obtiendrez probablement 90 colonnes one-hot. En effet, toutes les réponses incluent quelque chose comme n = np.max(a)+1. J'ai trouvé une solution plus générique qui a fonctionné pour moi et que je voulais partager avec vous:

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)

J'espère que quelqu'un a rencontré les mêmes restrictions sur les solutions ci-dessus et que cela pourrait être utile

1
Hans T

Utilisez le code suivant. Ça marche mieux.

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

Vous l'avez trouvé ici P.S Vous n'avez pas besoin d'entrer dans le lien.

1
Inaam Ilahi
  • nous voulons savoir quelle valeur est la plus haute de suite, y mettre 1 et partout ailleurs 0.
  • Solution propre et facile: 

extrait de code

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)
1
MiFi

Juste pour élaborer sur le excellente réponse de K3 --- rnc , voici une version plus générique:

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]

En outre, voici un test de performance rapide de cette méthode et une méthode de la réponse actuellement acceptée par YXD (légèrement modifiée, de sorte qu’elles offrent la même API, sauf que ce dernier ne fonctionne qu'avec 1D ndarrays):

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

Cette dernière méthode est environ 35% plus rapide (MacBook Pro 13 2015), mais la première est plus générale:

>>> 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)
1
Emil Melnikov

Ce type de codage fait généralement partie du tableau numpy. Si vous utilisez un tableau numpy comme ceci:

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

alors il existe un moyen très simple de convertir cela en encodage 1-hot

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

C'est tout.

1
Sudeep K Rana

Voici une solution autonome indépendante de la dimensionnalité.

Cela convertira tout tableau __dimensionnel arr de nombres entiers non négatifs en un tableau one_hot à une seule liste chaude, où one_hot[i_1,...,i_N,c] = 1 signifie arr[i_1,...,i_N] = c. Vous pouvez récupérer l’entrée via np.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
0
eqzx

Voici un exemple de fonction que j'ai écrit pour le faire en fonction des réponses ci-dessus et de mon propre cas d'utilisation:

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)
0
Aaron Lelevier

J'ajoute pour compléter une fonction simple, en utilisant uniquement des opérateurs 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

Il prend en entrée une matrice de probabilité: 

[[0.03038822 0.65810204 0.16549407 0.3797123] ... [0.02771272 0.2760752 0.3280924 0.33458805]] 

Et ça va revenir

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

0