Konvertieren Sie ein Array von Indizes in ein 1-Hot-codiertes Numpy-Array


227

Angenommen, ich habe ein 1d-Numpy-Array

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

Ich möchte dies als 2d 1-Hot-Array codieren

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

Gibt es einen schnellen Weg, dies zu tun? Schneller als nur eine Schleife a, um Elemente von festzulegen b.

Antworten:


395

Ihr Array adefiniert die Spalten der Nicht-Null-Elemente im Ausgabearray. Sie müssen auch die Zeilen definieren und dann eine ausgefallene Indizierung verwenden:

>>> 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
Wunderschönen. Verallgemeinern Sie es ein wenig: b = np.zeros((a.size, a.max()+1))dann `b [np.arange (a.size), a] = 1`
James Atwood

10
@ JamesAtwood es hängt von der Anwendung ab, aber ich würde das Maximum zu einem Parameter machen und es nicht aus den Daten berechnen.
Mohammad Moghimi

1
@MohammadMoghimi Klar, macht für mich Sinn.
James Atwood

7
Was ist, wenn 'a' 2d war? und Sie wollen eine 3-D-One-Hot-Matrix?
AD

8
Kann jemand auf eine Erklärung verweisen, warum dies funktioniert, aber das Slice mit [:, a] nicht?
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
Diese Lösung ist die einzige, die für eine Eingangs-ND-Matrix in eine heiße N + 1D-Matrix nützlich ist. Beispiel: input_matrix = np.asarray ([[0,1,1], [1,1,2]]); np.eye (3) [input_matrix] # Ausgabe 3D-Tensor
Isaías

5
+1, da dies der akzeptierten Lösung vorgezogen werden sollte. Für eine allgemeinere Lösung valuessollte jedoch ein Numpy-Array anstelle einer Python-Liste verwendet werden, dann funktioniert es in allen Dimensionen, nicht nur in 1D.
Alex

8
Beachten Sie, dass die Verwendung np.max(values) + 1der Anzahl der Buckets möglicherweise nicht wünschenswert ist, wenn Ihr Datensatz zufällig ausgewählt wurde und zufällig keinen Maximalwert enthält. Die Anzahl der Buckets sollte eher ein Parameter sein, und es kann eine Bestätigung / Überprüfung vorhanden sein, um zu überprüfen, ob jeder Wert innerhalb von 0 (inkl.) Liegt und die Anzahl der Buckets (excl).
NightElfik

2
Für mich ist diese Lösung die beste und kann leicht auf jeden Tensor verallgemeinert werden: def one_hot (x, Tiefe = 10): return np.eye (Tiefe) [x]. Beachten Sie, dass die Angabe des Tensors x als Index einen Tensor von x.shape-Augenreihen zurückgibt.
Cecconeurale

4
Einfache Möglichkeit, diese Lösung zu "verstehen" und warum sie für N-Dims funktioniert (ohne numpyDokumente zu lesen ): An jeder Stelle in der ursprünglichen Matrix ( values) haben wir eine Ganzzahl k, und wir "setzen" den 1-heißen Vektor eye(n)[k]an diese Stelle . Dies fügt eine Dimension hinzu, da wir einen Vektor an der Stelle eines Skalars in der ursprünglichen Matrix "platzieren".
Avivr

35

Wenn Sie Keras verwenden, gibt es dafür ein eingebautes Dienstprogramm:

from keras.utils.np_utils import to_categorical   

categorical_labels = to_categorical(int_labels, num_classes=3)

Und es ist so ziemlich das Gleiche wie die Antwort von @ YXD (siehe Quellcode ).


32

Folgendes finde ich nützlich:

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

Hier num_classessteht für die Anzahl der Klassen, die Sie haben. Wenn Sie also einen aVektor mit der Form (10000,) haben, transformiert diese Funktion ihn in (10000, C) . Beachten Sie, dass ader Index null ist, dh one_hot(np.array([0, 1]), 2)geben wird [[1, 0], [0, 1]].

Genau das, was Sie haben wollten, glaube ich.

PS: Die Quelle sind Sequenzmodelle - deeplearning.ai


Was ist der Grund für die Ausführung von np.squeeze (), da np.eye(num_classes)[a.reshape(-1)]. What you are simply doing is using Sie mit np.eye` eine (diagonale Größe) viele heißcodierte Arrays erhalten ? Sie erstellen eine Diagonalmatrix mit jedem Klassenindex als 1 Rest Null und später mit den bereitgestellten Indizes durch a.reshape(-1)Erzeugen der Ausgabe entsprechend dem Index in np.eye(). Ich habe die Notwendigkeit nicht verstanden, np.sqeezeda wir es verwenden, um einfach einzelne Dimensionen zu entfernen, die wir niemals haben werden, wie es in der Dimension der Ausgabe immer sein wird(a_flattened_size, num_classes)
Anu

27

Sie können verwenden sklearn.preprocessing.LabelBinarizer:

Beispiel:

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

Ausgabe:

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

Unter anderem können Sie initialisieren, sklearn.preprocessing.LabelBinarizer()so dass die Ausgabe von transformspärlich ist.


21

Sie können auch mit Augenfunktion von numpy:

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


1
Für mehr Klarheit np.identity(num_classes)[indices]könnte die Verwendung besser sein. Gute Antwort!
Oliver

5

Hier ist eine Funktion, die einen 1-D-Vektor in ein 2-D-One-Hot-Array konvertiert.

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

Nachfolgend einige Anwendungsbeispiele:

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

Beachten Sie, dass dies nur für Vektoren funktioniert (und es gibt keine assertMöglichkeit, die Vektorform zu überprüfen;)).
Johndodo

1
+1 für den verallgemeinerten Ansatz und die Parameterprüfung. Als gängige Praxis empfehle ich jedoch, KEINE Asserts zu verwenden, um Eingaben zu überprüfen. Verwenden Sie Asserts nur, um interne Zwischenbedingungen zu überprüfen. Konvertieren Sie lieber alles assert ___in if not ___ raise Exception(<Reason>).
Fnunnari

3

Für 1-Hot-Codierung

   one_hot_encode=pandas.get_dummies(array)

Beispielsweise

CODIERUNG GENIESSEN


Vielen Dank für den Kommentar, aber eine kurze Beschreibung der Funktionsweise des Codes wäre sehr hilfreich!
Clarus

Bitte beziehen Sie sich auf das Beispiel
Shubham Mishra

@Clarus Checkout das folgende Beispiel. Sie können auf die eine Hot-Codierung jedes Werts in Ihrem np-Array zugreifen, indem Sie einen one_hot_encode [Wert] ausführen. >>> 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

Ich denke die kurze Antwort ist nein. Für einen allgemeineren Fall in nDimensionen habe ich mir Folgendes ausgedacht:

# 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

Ich frage mich, ob es eine bessere Lösung gibt - ich mag es nicht, dass ich diese Listen in den letzten beiden Zeilen erstellen muss. Wie auch immer, ich habe einige Messungen mit durchgeführt timeitund es scheint, dass die numpy-basierte ( indices/ arange) und die iterative Version ungefähr gleich funktionieren .


2

Um die ausgezeichnete Antwort von K3 --- rnc näher zu erläutern , hier eine allgemeinere Version:

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]

Hier ist auch ein schneller und schmutziger Benchmark dieser Methode und eine Methode aus der derzeit akzeptierten Antwort von YXD (leicht geändert, so dass sie dieselbe API bieten, außer dass letztere nur mit 1D-ndarrays funktioniert):

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

Die letztere Methode ist ~ 35% schneller (MacBook Pro 13 2015), die erstere ist jedoch allgemeiner:

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

Sie können den folgenden Code zum Konvertieren in einen One-Hot-Vektor verwenden:

let x ist der normale Klassenvektor mit einer einzelnen Spalte mit den Klassen 0 bis zu einer bestimmten Zahl:

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

wenn 0 keine Klasse ist; dann entferne +1.


1

Ich bin kürzlich auf ein Problem der gleichen Art gestoßen und habe diese Lösung gefunden, die sich nur dann als zufriedenstellend herausstellte, wenn Sie Zahlen haben, die innerhalb einer bestimmten Formation liegen. Zum Beispiel, wenn Sie die folgende Liste einmalig codieren möchten:

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

Gehen Sie voran, die veröffentlichten Lösungen sind bereits oben erwähnt. Aber was ist, wenn man diese Daten berücksichtigt:

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

Wenn Sie dies mit den oben genannten Methoden tun, werden Sie wahrscheinlich 90 One-Hot-Spalten erhalten. Dies liegt daran, dass alle Antworten so etwas wie enthalten n = np.max(a)+1. Ich habe eine allgemeinere Lösung gefunden, die für mich funktioniert hat und die ich mit Ihnen teilen wollte:

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)

Ich hoffe, jemand hat die gleichen Einschränkungen bei den oben genannten Lösungen festgestellt, und dies könnte sich als nützlich erweisen


1

Eine solche Art der Codierung ist normalerweise Teil eines Numpy-Arrays. Wenn Sie ein Numpy-Array wie dieses verwenden:

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

dann gibt es eine sehr einfache Möglichkeit, dies in 1-Hot-Codierung umzuwandeln

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

Das ist es.


1
  • p wird ein 2d ndarray sein.
  • Wir wollen wissen, welcher Wert der höchste in einer Reihe ist, um dort 1 und überall sonst 0 zu setzen.

saubere und einfache Lösung:

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

Verwenden eines Neuraxle- Pipeline-Schritts:

  1. Richten Sie Ihr Beispiel ein
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. Führen Sie die eigentliche Konvertierung durch
from neuraxle.steps.numpy import OneHotEncoder
encoder = OneHotEncoder(nb_columns=4)
b_pred = encoder.transform(a)
  1. Stellen Sie sicher, dass es funktioniert
assert b_pred == b

Link zur Dokumentation: neuraxle.steps.numpy.OneHotEncoder


0

Hier ist eine Beispielfunktion, die ich geschrieben habe, um dies basierend auf den obigen Antworten und meinem eigenen Anwendungsfall zu tun:

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

Ich füge zur Vervollständigung eine einfache Funktion hinzu, die nur numpy-Operatoren verwendet:

   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

Als Eingabe wird eine Wahrscheinlichkeitsmatrix verwendet: z.

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

Und es wird zurückkehren

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


0

Hier ist eine dimensionsunabhängige Standalone-Lösung.

Dadurch wird jedes N-dimensionale Array arrnichtnegativer Ganzzahlen in ein eindimensionales N + 1-dimensionales Array konvertiert one_hot, wobei dies one_hot[i_1,...,i_N,c] = 1bedeutet arr[i_1,...,i_N] = c. Sie können die Eingabe über wiederherstellennp.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

Verwenden Sie den folgenden Code. Es funktioniert am besten.

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

Fand es hier PS Du musst nicht in den Link gehen.


5
Sie sollten vermeiden, Schleifen mit Numpy zu verwenden
Kenan
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.