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

227

Disons que j'ai un tableau numpy 1d

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

Je voudrais encoder ceci en tant que tableau 2d 1-hot

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

Existe-t-il un moyen rapide de procéder? Plus rapide que de simplement boucler apour définir des éléments de b, c'est-à-dire.

James Atwood
la source

Réponses:

395

Votre tableau adéfinit les colonnes des éléments non nuls dans le tableau de sortie. Vous devez également définir les lignes, puis utiliser une indexation sophistiquée:

>>> 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
la source
111
Belle. Généraliser un peu b = np.zeros((a.size, a.max()+1)):, puis `b [np.arange (a.size), a] = 1`
James Atwood
10
@JamesAtwood cela dépend de l'application mais je ferais du max un paramètre et ne le calculerais pas à partir des données.
Mohammad Moghimi
1
@MohammadMoghimi Bien sûr, c'est logique pour moi.
James Atwood
7
et si «a» était 2d? et vous voulez une matrice 3-d one-hot?
AD
8
Quelqu'un peut-il expliquer pourquoi cela fonctionne, mais la tranche avec [:, a] ne fonctionne pas?
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
la source
9
Cette solution est la seule utile pour une matrice ND d'entrée vers une matrice N + 1D à chaud. Exemple: input_matrix = np.asarray ([[0,1,1], [1,1,2]]); np.eye (3) [input_matrix] # sortie tenseur 3D
Isaías
5
+1 car cela doit être préféré à la solution acceptée. Cependant, pour une solution plus générale, valuesdevrait être un tableau Numpy plutôt qu'une liste Python, alors cela fonctionne dans toutes les dimensions, pas seulement dans 1D.
Alex
8
Notez que la prise en np.max(values) + 1tant que nombre de compartiments peut ne pas être souhaitable si votre ensemble de données est, par exemple, échantillonné au hasard et que, par hasard, il peut ne pas contenir de valeur maximale. Le nombre de compartiments doit être plutôt un paramètre et une assertion / vérification peut être en place pour vérifier que chaque valeur est comprise entre 0 (incl) et le nombre de compartiments (excl).
NightElfik
2
Pour moi, cette solution est la meilleure et peut être facilement généralisée à n'importe quel tenseur: def one_hot (x, depth = 10): return np.eye (depth) [x]. Notez que le fait d'indiquer le tenseur x comme index renvoie un tenseur de rangées d'oeil x.shape.
cecconeurale
4
Un moyen facile de "comprendre" cette solution et pourquoi elle fonctionne pour les N-dims (sans lire les numpydocuments): à chaque emplacement de la matrice d'origine ( values), nous avons un entier k, et nous "plaçons" le vecteur 1-chaud eye(n)[k]à cet emplacement . Cela ajoute une dimension car nous «plaçons» un vecteur à l'emplacement d'un scalaire dans la matrice d'origine.
avivr
35

Dans le cas où vous utilisez des 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 cela fait à peu près la même chose que la réponse de @ YXD (voir code source ).

Jodo
la source
32

Voici ce que je trouve utile:

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

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

Exactement ce que vous vouliez avoir, je crois.

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

D.Samchuk
la source
aussi, quelle est la raison de faire np.squeeze () puisque récupérez la (taille du vecteur a) plusieurs tableaux encodés à chaud en utilisant np.eye(num_classes)[a.reshape(-1)]. What you are simply doing is using np.eye` vous créez une matrice diagonale avec chaque index de classe comme 1 reste zéro et plus tard en utilisant les index fournis en a.reshape(-1)produisant la sortie correspondant à l'indice en np.eye(). Je ne comprenais pas la nécessité np.sqeezecar nous l'utilisons pour simplement supprimer des dimensions uniques que nous n'aurons jamais car la dimension de sortie sera toujours(a_flattened_size, num_classes)
Anu
27

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

production:

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

Entre autres choses, vous pouvez initialiser de sklearn.preprocessing.LabelBinarizer()sorte que la sortie de transformsoit rare.

Franck Dernoncourt
la source
21

Vous pouvez également utiliser la fonction oculaire de numpy:

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

Karma
la source
1
Pour plus de clarté, l'utilisation np.identity(num_classes)[indices]pourrait être meilleure. Bonne réponse!
Oliver
5

Voici une fonction qui convertit un vecteur 1D en un tableau unidimensionnel 2D.

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

Voici un exemple 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]])
stackoverflowuser2010
la source
Notez que cela ne fonctionne que sur les vecteurs (et il n'y a pas assertde vérification de la forme des vecteurs;)).
johndodo
1
+1 pour l'approche généralisée et la vérification des paramètres. Cependant, comme pratique courante, je suggère de NE PAS utiliser d'assertions pour effectuer des vérifications sur les entrées. Utilisez les assertions uniquement pour vérifier les conditions intermédiaires internes. Convertissez plutôt tout assert ___en if not ___ raise Exception(<Reason>).
fnunnari
3

Pour 1-encodage à chaud

   one_hot_encode=pandas.get_dummies(array)

Par exemple

PROFITEZ DU CODAGE

Shubham Mishra
la source
Merci pour le commentaire, mais une brève description de ce que fait le code serait très utile!
Clarus
veuillez vous référer à l'exemple
Shubham Mishra
@Clarus Checkout l'exemple ci-dessous. Vous pouvez accéder à l'un codage à chaud de chaque valeur de votre tableau np en effectuant une [valeur] one_hot_encode. >>> 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

Je pense que la réponse courte est non. Pour un cas plus générique dans les ndimensions, j'ai trouvé 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 y a une meilleure solution - je n'aime pas que je doive créer ces listes dans les deux dernières lignes. Quoi qu'il en soit, j'ai fait quelques mesures avec timeitet il semble que les versions numpybasées sur ( indices/ arange) et les versions itératives fonctionnent à peu près de la même manière.

David Nemeskey
la source
2

Juste pour développer l' 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 benchmark rapide et sale de cette méthode et une méthode de la réponse actuellement acceptée par YXD (légèrement modifiée, afin qu'ils offrent la même API, sauf que cette dernière ne fonctionne qu'avec des 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

Cette dernière méthode est ~ 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)
Emil Melnikov
la source
2

Vous pouvez utiliser le code suivant pour la conversion en un vecteur unique:

soit x est le vecteur de classe normal ayant une seule colonne avec des 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.

Inaam Ilahi
la source
1

J'ai récemment rencontré un problème du même genre et j'ai trouvé cette solution qui s'est avérée satisfaisante uniquement si vous avez des nombres qui entrent dans une certaine formation. Par exemple, si vous souhaitez coder à chaud 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 que faire si l'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 vous retrouverez probablement avec 90 colonnes à chaud. C'est parce que 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 j'ai voulu 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 cela pourrait être utile

Hans T
la source
1

Ce type d'encodage fait généralement partie du tableau numpy. Si vous utilisez un tableau numpy comme celui-ci:

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

alors il y a un moyen très simple de convertir cela en un encodage à chaud

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

C'est tout.

Sudeep K Rana
la source
1
  • p sera un ndarray 2D.
  • Nous voulons savoir quelle valeur est la plus élevée d'affilée, pour y mettre 1 et partout ailleurs 0.

solution propre et facile:

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
la source
1

Utilisation d'une étape de pipeline Neuraxle :

  1. Configurez votre exemple
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. Faire la conversion réelle
from neuraxle.steps.numpy import OneHotEncoder
encoder = OneHotEncoder(nb_columns=4)
b_pred = encoder.transform(a)
  1. Affirmez que cela fonctionne
assert b_pred == b

Lien vers la documentation: neuraxle.steps.numpy.OneHotEncoder

Guillaume Chevalier
la source
0

Voici un exemple de fonction que j'ai écrit pour ce 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)
Aaron Lelevier
la source
0

J'ajoute pour terminer 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é: ex:

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

Et ça reviendra

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

Jordy Van Landeghem
la source
0

Voici une solution autonome indépendante de la dimension.

Cela convertira tout tableau N-dimensionnel arrd'entiers non négatifs en un tableau N + 1 unidimensionnel one_hot, où one_hot[i_1,...,i_N,c] = 1signifie arr[i_1,...,i_N] = c. Vous pouvez récupérer l'entrée 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
la source
0

Utilisez le code suivant. Cela fonctionne 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

Trouvé ici PS Vous n'avez pas besoin d'entrer dans le lien.

Inaam Ilahi
la source
5
Vous devriez éviter d'utiliser des boucles avec numpy
Kenan