Converti array di indici in array numpy con codifica a 1 hot


227

Diciamo che ho un array numpy 1d

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

Vorrei codificare questo come un array 2d 1-hot

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

C'è un modo rapido per farlo? Più veloce di un semplice ciclo aper impostare gli elementi di b, cioè.

Risposte:


395

L'array adefinisce le colonne degli elementi diversi da zero nell'array di output. È inoltre necessario definire le righe e quindi utilizzare l'indicizzazione di fantasia:

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

111
Bellissimo. Generalizzandolo un po ': b = np.zeros((a.size, a.max()+1)), quindi `b [np.arange (a.size), a] = 1`
James Atwood,

10
@JamesAtwood dipende dall'applicazione ma renderei il massimo un parametro e non lo calcolerei dai dati.
Mohammad Moghimi,

1
@MohammadMoghimi Certo, ha senso per me.
James Atwood,

7
e se 'a' fosse 2d? e vuoi una matrice 3D one-hot?
d.C.

8
Qualcuno può indicare una spiegazione del perché questo funziona, ma la sezione con [:, a] non lo fa?
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.]])

9
Questa soluzione è l'unica utile per una matrice ND in ingresso su una matrice N + 1D a caldo. Esempio: input_matrix = np.asarray ([[0,1,1], [1,1,2]]); np.eye (3) [input_matrix] # output tensore 3D
Isaías

5
+1 perché questo dovrebbe essere preferito rispetto alla soluzione accettata. Per una soluzione più generale, tuttavia, valuesdovrebbe essere un array Numpy anziché un elenco Python, quindi funziona in tutte le dimensioni, non solo in 1D.
Alex,

8
Si noti che l'assunzione np.max(values) + 1come numero di bucket potrebbe non essere desiderabile se il set di dati viene campionato in modo casuale e, per caso, potrebbe non contenere il valore massimo. Il numero di bucket dovrebbe essere piuttosto un parametro e l'asserzione / verifica può essere in atto per verificare che ciascun valore sia compreso tra 0 (incl) e il conteggio dei bucket (escl).
NightElfik

2
Per me questa soluzione è la migliore e può essere facilmente generalizzata a qualsiasi tensore: def one_hot (x, depth = 10): return np.eye (depth) [x]. Si noti che dare il tensore x come indice restituisce un tensore di file di occhi x.shape.
cecconeurale,

4
Modo semplice per "comprendere" questa soluzione e perché funziona con N-dim (senza leggere numpydocumenti): in ogni posizione nella matrice originale ( values), abbiamo un numero intero ke "mettiamo" il vettore 1-hot eye(n)[k]in quella posizione . Questo aggiunge una dimensione perché stiamo "mettendo" un vettore nella posizione di uno scalare nella matrice originale.
Avivr,

35

Nel caso in cui tu stia usando keras, esiste un'utilità integrata per questo:

from keras.utils.np_utils import to_categorical   

categorical_labels = to_categorical(int_labels, num_classes=3)

E fa praticamente lo stesso della risposta di @ YXD (vedi codice sorgente ).


32

Ecco cosa trovo utile:

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

Qui num_classessta per numero di classi che hai. Quindi se hai un avettore con forma di (10000,) questa funzione lo trasforma in (10000, C) . Si noti che aè a zero, cioè one_hot(np.array([0, 1]), 2)darà [[1, 0], [0, 1]].

Esattamente quello che volevi avere, credo.

PS: la fonte è modelli Sequence - deeplearning.ai


inoltre, qual è la ragione per fare np.squeeze () dato che ottieni (la dimensione di un vettore a) molte matrici con codifica a caldo usando np.eye(num_classes)[a.reshape(-1)]. What you are simply doing is using np.eye` stai creando una matrice diagonale con ogni indice di classe come 1 resto zero e successivamente usando gli indici forniti per a.reshape(-1)produrre l'uscita corrispondente all'indice in np.eye(). Non ho capito la necessità di np.sqeezedal momento che lo usiamo per rimuovere semplicemente singole dimensioni che non avremo mai come nella dimensione dell'output sarà sempre(a_flattened_size, num_classes)
Anu

27

Puoi usare sklearn.preprocessing.LabelBinarizer:

Esempio:

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

produzione:

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

Tra le altre cose, è possibile inizializzare in sklearn.preprocessing.LabelBinarizer()modo che l'output di transformsia scarso.


21

Puoi anche usare la funzione occhio di intorpidimento:

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


1
Per maggiore chiarezza, usare np.identity(num_classes)[indices]potrebbe essere migliore. Bella risposta!
Oliver,

5

Ecco una funzione che converte un vettore 1-D in un array one-hot 2-D.

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

Di seguito è riportato un esempio di utilizzo:

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

Si noti che questo funziona solo su vettori (e non è assertnecessario controllare la forma del vettore;)).
johndodo,

1
+1 per l'approccio generalizzato e controllo dei parametri. Tuttavia, come pratica comune, suggerisco di NON usare assert per eseguire controlli sugli input. Utilizzare le asserzioni solo per verificare le condizioni intermedie interne. Piuttosto, converti tutto assert ___in if not ___ raise Exception(<Reason>).
fnunnari,

3

Per la codifica 1-hot

   one_hot_encode=pandas.get_dummies(array)

Per esempio

GODITI LA CODIFICA


Grazie per il commento, ma una breve descrizione di ciò che sta facendo il codice sarebbe molto utile!
Clarus,

si prega di fare riferimento all'esempio
Shubham Mishra il

@Clarus Guarda l'esempio seguente. È possibile accedere a una codifica a caldo di ciascun valore nell'array np eseguendo one_hot_encode [valore]. >>> 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 l'

2

Penso che la risposta breve sia no. Per un caso più generico di ndimensioni, ho pensato a questo:

# 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

Mi chiedo se esiste una soluzione migliore: non mi piace che debba creare quegli elenchi nelle ultime due righe. Ad ogni modo, ho fatto alcune misurazioni con timeite sembra che le versioni basate numpysu ( indices/ arange) e iterative funzionino allo stesso modo.


2

Solo per elaborare l' eccellente risposta di K3 --- rnc , ecco una versione più generica:

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]

Inoltre, ecco un benchmark rapido e sporco di questo metodo e un metodo dalla risposta attualmente accettata da YXD (leggermente modificato, in modo che offrano la stessa API tranne che quest'ultimo funziona solo con 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

Quest'ultimo metodo è ~ 35% più veloce (MacBook Pro 13 2015), ma il primo è più generale:

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

2

È possibile utilizzare il seguente codice per la conversione in un vettore a uno caldo:

let x è il vettore di classe normale con una singola colonna con classi da 0 a un certo numero:

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

se 0 non è una classe; quindi rimuovere +1.


1

Di recente ho riscontrato un problema dello stesso tipo e ho trovato la soluzione che si è rivelata soddisfacente solo se si hanno numeri che rientrano in una determinata formazione. Ad esempio, se si desidera codificare a una hot lista seguente:

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

vai avanti, le soluzioni pubblicate sono già menzionate sopra. Ma cosa succede se si considerano questi dati:

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

Se lo fai con i metodi sopra menzionati, probabilmente finirai con 90 colonne one-hot. Questo perché tutte le risposte includono qualcosa di simile n = np.max(a)+1. Ho trovato una soluzione più generica che ha funzionato per me e che volevo condividere con te:

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)

Spero che qualcuno abbia riscontrato le stesse restrizioni sulle soluzioni precedenti e questo potrebbe tornare utile


1

Tale tipo di codifica di solito fa parte dell'array numpy. Se si utilizza un array numpy come questo:

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

allora c'è un modo molto semplice per convertirlo in codifica 1-hot

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

Questo è tutto.


1
  • p sarà un secondo ordine.
  • Vogliamo sapere quale valore è il più alto in una riga, per mettere lì 1 e ovunque altro 0.

soluzione pulita e semplice:

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

Utilizzando un passaggio della pipeline Neuraxle :

  1. Crea il tuo esempio
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. Effettua la conversione effettiva
from neuraxle.steps.numpy import OneHotEncoder
encoder = OneHotEncoder(nb_columns=4)
b_pred = encoder.transform(a)
  1. Asserire che funziona
assert b_pred == b

Link alla documentazione: neuraxle.steps.numpy.OneHotEncoder


0

Ecco una funzione di esempio che ho scritto per fare questo in base alle risposte sopra e al mio caso d'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)

0

Sto aggiungendo per il completamento una semplice funzione, usando solo operatori intorpiditi:

   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

Prende come input una matrice di probabilità: ad esempio:

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

E tornerà

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


0

Ecco una soluzione indipendente indipendente dalla dimensionalità.

Questo convertirà qualsiasi array N-dimensionale arrdi numeri interi non negativi in ​​un array N + 1 unidimensionale one_hot, dove one_hot[i_1,...,i_N,c] = 1significa arr[i_1,...,i_N] = c. È possibile recuperare l'input tramitenp.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

Usa il seguente codice. Funziona meglio.

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

Trovato qui PS Non è necessario accedere al collegamento.


5
Dovresti evitare di usare loop con intorpidimento
Kenan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.