2015-04-23 15 views
83

Diciamo che ho un allineamento NumPy 1dNumpy 1-caldo serie

a = [1,0,3] 

Vorrei codificare questo come un 2d serie 1-caldo

b = [[0,1,0,0], [1,0,0,0], [0,0,0,1]] 

C'è un modo rapido per fare Questo? Più veloce del semplice ciclo a per impostare gli elementi di b, ovvero.

+0

Eventuali duplicati di [una codifica Hot usando numpy] (https://stackoverflow.com/questions/38592324/one-hot-encoding-using-numpy) –

risposta

162

L'array a definisce le colonne degli elementi diverso da zero nell'array di output. È necessario definire anche le righe e quindi utilizzare fantasia indicizzazione:

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

Bello. Generalizzandolo un po ': 'b = np.zeros ((a.size, a.max() + 1))', quindi 'b [np.arange (a.size), a] = 1' –

+6

@JamesAtwood dipende dall'applicazione, ma renderei il massimo un parametro e non calcolarlo dai dati. –

+0

@MohammadMoghimi Certo, ha senso per me. –

73
>>> 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.]]) 
+3

Questa soluzione è l'unica utile per una matrice N-D di input per una matrice N + 1D calda. Esempio: input_matrix = np.asarray ([[0,1,1], [1,1,2]]); np.eye (3) [input_matrix] # output tensore 3D –

+1

+1 perché questo deve essere preferito rispetto alla soluzione accettata. Per una soluzione più generale, però, 'values' dovrebbe essere un array Numpy piuttosto che un elenco Python, quindi funziona in tutte le dimensioni, non solo in 1D. – Alex

+2

Si noti che l'assunzione di 'np.max (valori) + 1' come numero di bucket potrebbe non essere desiderabile se il proprio set di dati viene pronunciato a caso e per caso non può contenere un valore massimo. Il numero di benne dovrebbe essere piuttosto un parametro e l'asserzione/controllo può essere in atto per verificare che ogni valore sia compreso tra 0 (incl) e il conteggio dei bucket (excl). – NightElfik

3

Ecco una funzione che converte un vettore 1-D ad una matrice calda 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) 

seguito alcune esempio d'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]]) 
+0

Si noti che questo funziona solo sui vettori (e non esiste alcun 'assert' per controllare la forma del vettore;)). – johndodo

2

Penso che la risposta è no. Per un caso più generico n dimensioni, sono arrivato fino 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 c'è una soluzione migliore - non mi piace che devo creare quelle liste nelle ultime due righe . Ad ogni modo, ho eseguito alcune misurazioni con timeit e sembra che le versioni basate su numpy (indices/arange) e le versioni iterative abbiano lo stesso rendimento.

20

È possibile utilizzare 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)) 

uscita:

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

Tra le altre cose, si può inizializzare sklearn.preprocessing.LabelBinarizer() in modo che l'uscita del transform è scarsa.

3

Nel caso in cui si utilizza keras, v'è una costruito nel programma di utilità per questo:

from keras.utils.np_utils import to_categorical 

categorical_labels = to_categorical(int_labels, num_classes=3) 
0

Ecco un esempio di funzione che ho scritto a fare questo sulla base delle risposte di cui sopra e il 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

Solo per elaborare sulla excellent answer da K3---rnc, qui è 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 punto di riferimento rapido e-sporco di questo metodo e un metodo dal currently accepted answer da YXD (leggermente modificato, in modo che offrono la stessa API tranne che quest'ultima funziona solo con 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 

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

recente ho incontrato un problema dello stesso tipo e hanno trovato detto soluzione che si è rivelata soddisfacente solo se hai numeri che vanno all'interno di una certa formazione. Ad esempio, se si desidera codificare una sola lista seguente:

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

andare avanti, le soluzioni pubblicate sono già menzionate sopra. Ma cosa succede se si considera questo dato:

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

Se lo si fa con i metodi di cui sopra, è probabile che finire con 90 colonne one-hot. Questo perché tutte le risposte includono qualcosa come n = np.max(a)+1. Ho trovato una soluzione più generica che ha funzionato per me e ha voluto condividere con voi:

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 incontrato stesse restrizioni sulle soluzioni di cui sopra e questo potrebbe rivelarsi utile