Beschriftungscodierung über mehrere Spalten in scikit-learn

216

Ich versuche, Scikit-Learns zu verwenden LabelEncoder, um Pandas DataFramevon String-Labels zu codieren . Da der Datenrahmen viele (50+) Spalten enthält, möchte ich vermeiden, LabelEncoderfür jede Spalte ein Objekt zu erstellen . Ich hätte lieber nur ein großes LabelEncoderObjekt, das in allen meinen Datenspalten funktioniert .

Wenn Sie das Ganze DataFramein werfen, LabelEncoderentsteht der folgende Fehler. Bitte beachten Sie, dass ich hier Dummy-Daten verwende. Tatsächlich habe ich es mit ungefähr 50 Spalten mit Daten mit Zeichenfolgenbeschriftung zu tun. Benötigen Sie also eine Lösung, die keine Spalten nach Namen referenziert.

import pandas
from sklearn import preprocessing 

df = pandas.DataFrame({
    'pets': ['cat', 'dog', 'cat', 'monkey', 'dog', 'dog'], 
    'owner': ['Champ', 'Ron', 'Brick', 'Champ', 'Veronica', 'Ron'], 
    'location': ['San_Diego', 'New_York', 'New_York', 'San_Diego', 'San_Diego', 
                 'New_York']
})

le = preprocessing.LabelEncoder()

le.fit(df)

Traceback (letzter Aufruf zuletzt): Datei "", Zeile 1, in Datei "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/preprocessing/label.py", Zeile 103, in fit y = column_or_1d (y, warn = True) Datei "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py", Zeile 306, in column_or_1d erhöhen ValueError ("fehlerhafte Eingabeform { 0} ". Format (Form)) ValueError: falsche Eingabeform (6, 3)

Irgendwelche Gedanken darüber, wie man dieses Problem umgehen kann?

Bryan
quelle
Warum versuchst du das zu tun?
Fred Foo
Vereinfachung der Codierung einer mehrspaltigen dataframeZeichenfolgendaten. Ich picke die Codierungsobjekte, möchte also vermeiden, dass 50 separate Objekte ausgewählt / entfernt werden müssen. Ich frage mich auch, ob es eine Möglichkeit gibt, dass der Encoder die Daten vereinfacht, dh nur eine Zeile mit einem Bezeichner für jede eindeutige Kombination von Variablen in jeder Spalte zurückgibt.
Bryan
Es gibt eine einfache Möglichkeit, dies alles in Pandas zu tun, indem Sie ein Wörterbuch mit Wörterbüchern an die replaceMethode übergeben. Siehe diese Antwort unten
Ted Petrou

Antworten:

451

Sie können dies jedoch leicht tun,

df.apply(LabelEncoder().fit_transform)

EDIT2:

In scikit-learn 0.20 wird empfohlen

OneHotEncoder().fit_transform(df)

da der OneHotEncoder jetzt die Eingabe von Zeichenfolgen unterstützt. Mit dem ColumnTransformer ist es möglich, OneHotEncoder nur auf bestimmte Spalten anzuwenden.

BEARBEITEN:

Da diese Antwort vor über einem Jahr stammt und viele positive Stimmen (einschließlich eines Kopfgeldes) hervorgerufen hat, sollte ich sie wahrscheinlich weiter ausbauen.

Für inverse_transform und transform musst du ein bisschen hacken.

from collections import defaultdict
d = defaultdict(LabelEncoder)

Damit behalten Sie jetzt alle Spalten LabelEncoderals Wörterbuch bei.

# Encoding the variable
fit = df.apply(lambda x: d[x.name].fit_transform(x))

# Inverse the encoded
fit.apply(lambda x: d[x.name].inverse_transform(x))

# Using the dictionary to label future data
df.apply(lambda x: d[x.name].transform(x))
Napitupulu Jon
quelle
1
Das ist erstaunlich, aber wie können wir in diesem Fall die inverse Transformation anwenden?
Supreeth Meka
10
Aber wenn ich diese Lösung in einer Pipeline verwenden möchte, z. B. separate Anpassung und Transformation (Anpassung an Zug und dann Verwendung auf Test-Set -> Wiederverwendung des gelernten Wörterbuchs), wird dies unterstützt df.apply(LabelEncoder().fit_transform)?
Georg Heiler
2
Wie kann dies dazu gebracht werden, LabelBinarizerstattdessen zu arbeiten und das Wörterbuch für einen Testsatz wiederzuverwenden? Ich habe es versucht d = defaultdict(LabelBinarizer)und dann wird fit = df.apply(lambda x: d[x.name].fit_transform(x))aber eine Ausnahme ausgelöst : Exception: Data must be 1-dimensional. Ich bin mir nicht sicher, wie der resultierende DataFrame aussehen soll ... Vielleicht sollte jede Spalte die binärisierten Vektoren enthalten.
Qululu
4
Schöne Lösung. Wie kann ich nur in einer bestimmten Spalte transformieren?
Stenlytw
1
Wie mache ich das, wenn ich den Codierungs-Juste für eine Spalte umkehren möchte?
Ib D
95

Wie von larsmans erwähnt, verwendet LabelEncoder () nur ein 1-d-Array als Argument . Es ist jedoch recht einfach, einen eigenen Beschriftungscodierer zu erstellen, der mit mehreren Spalten Ihrer Wahl arbeitet und einen transformierten Datenrahmen zurückgibt. Mein Code hier basiert teilweise auf Zac Stewarts ausgezeichnetem Blog-Beitrag, der hier zu finden ist .

Erstellen einer Klasse , dass reagiert auf die beinhaltet einfach einen benutzerdefinierten Encoder Erstellen fit(), transform()und fit_transform()Methoden. In Ihrem Fall könnte ein guter Start ungefähr so ​​aussehen:

import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.pipeline import Pipeline

# Create some toy data in a Pandas dataframe
fruit_data = pd.DataFrame({
    'fruit':  ['apple','orange','pear','orange'],
    'color':  ['red','orange','green','green'],
    'weight': [5,6,3,4]
})

class MultiColumnLabelEncoder:
    def __init__(self,columns = None):
        self.columns = columns # array of column names to encode

    def fit(self,X,y=None):
        return self # not relevant here

    def transform(self,X):
        '''
        Transforms columns of X specified in self.columns using
        LabelEncoder(). If no columns specified, transforms all
        columns in X.
        '''
        output = X.copy()
        if self.columns is not None:
            for col in self.columns:
                output[col] = LabelEncoder().fit_transform(output[col])
        else:
            for colname,col in output.iteritems():
                output[colname] = LabelEncoder().fit_transform(col)
        return output

    def fit_transform(self,X,y=None):
        return self.fit(X,y).transform(X)

Angenommen, wir möchten unsere beiden kategorialen Attribute ( fruitund color) codieren und dabei das numerische Attribut in weightRuhe lassen. Wir könnten dies wie folgt tun:

MultiColumnLabelEncoder(columns = ['fruit','color']).fit_transform(fruit_data)

Welches transformiert unseren fruit_dataDatensatz von

Geben Sie hier die Bildbeschreibung ein zu

Geben Sie hier die Bildbeschreibung ein

Wenn Sie ihm einen Datenrahmen übergeben, der ausschließlich aus kategorialen Variablen besteht, und den columnsParameter weglassen , wird jede Spalte codiert (was meiner Meinung nach ursprünglich gesucht wurde):

MultiColumnLabelEncoder().fit_transform(fruit_data.drop('weight',axis=1))

Das verwandelt sich

Geben Sie hier die Bildbeschreibung ein zu

Geben Sie hier die Bildbeschreibung ein.

Beachten Sie, dass es wahrscheinlich erstickt, wenn versucht wird, bereits numerische Attribute zu codieren (fügen Sie Code hinzu, um dies zu handhaben, wenn Sie möchten).

Ein weiteres nettes Feature ist, dass wir diesen benutzerdefinierten Transformator in einer Pipeline verwenden können:

encoding_pipeline = Pipeline([
    ('encoding',MultiColumnLabelEncoder(columns=['fruit','color']))
    # add more pipeline steps as needed
])
encoding_pipeline.fit_transform(fruit_data)
PriceHardman
quelle
2
Gerade erkannt, implizieren die Daten, dass eine Orange grün gefärbt ist. Hoppla. ;)
PriceHardman
5
Dies ist eine gute Möglichkeit, Daten einmal zu transformieren, aber was ist, wenn ich diese Transformation für einen Validierungssatz wiederverwenden möchte? Sie müssten erneut fit_transform und es könnten Probleme auftreten, z. B. dass mein neuer Datensatz nicht alle Kategorien für alle Variablen enthält. Angenommen, die Farbe Grün wird in meinem neuen Datensatz nicht angezeigt. Dies wird die Codierung durcheinander bringen.
Ben
3
Einverstanden mit @Ben. Dies ahmt sklearn über die Methodennamen hinaus überhaupt nicht nach. Wenn Sie versuchen würden, dies in eine Pipeline zu setzen, würde es nicht funktionieren
Tgsmith61591
3
Um sicherzustellen, dass die Etikettencodierung sowohl für den Zug als auch für die Testsätze konsistent ist, sollten Sie die Codierung für Ihren gesamten Datensatz durchführen (Zug + Test). Dies kann entweder erfolgen, bevor Sie sie in Zug und Test aufteilen, oder Sie können sie kombinieren, die Codierung durchführen und sie wieder aufteilen.
PriceHardman
2
Wie wäre es rückwärts? zurück zum Original dekodieren?
user702846
18

Seit scikit-learn 0.20 können Sie verwenden sklearn.compose.ColumnTransformerund sklearn.preprocessing.OneHotEncoder:

Wenn Sie nur kategoriale Variablen haben, OneHotEncoderdirekt:

from sklearn.preprocessing import OneHotEncoder

OneHotEncoder(handle_unknown='ignore').fit_transform(df)

Wenn Sie heterogen typisierte Funktionen haben:

from sklearn.compose import make_column_transformer
from sklearn.preprocessing import RobustScaler
from sklearn.preprocessing import OneHotEncoder

categorical_columns = ['pets', 'owner', 'location']
numerical_columns = ['age', 'weigth', 'height']
column_trans = make_column_transformer(
    (categorical_columns, OneHotEncoder(handle_unknown='ignore'),
    (numerical_columns, RobustScaler())
column_trans.fit_transform(df)

Weitere Optionen in der Dokumentation: http://scikit-learn.org/stable/modules/compose.html#columntransformer-for-heterogene-data

Ogrisel
quelle
inverse_transform()wird von ColumnTransformer jedoch nicht unterstützt. Zumindest momentan nicht: github.com/scikit-learn/scikit-learn/issues/11463 . Das ist ein großer Nachteil für meine Bewerbung und wird wahrscheinlich auch für andere sein.
Sander Vanden Hautte
16

Wir brauchen keinen LabelEncoder.

Sie können die Spalten in Kategorien konvertieren und dann ihre Codes abrufen. Ich habe unten ein Wörterbuchverständnis verwendet, um diesen Prozess auf jede Spalte anzuwenden und das Ergebnis wieder in einen Datenrahmen derselben Form mit identischen Indizes und Spaltennamen zu verpacken.

>>> pd.DataFrame({col: df[col].astype('category').cat.codes for col in df}, index=df.index)
   location  owner  pets
0         1      1     0
1         0      2     1
2         0      0     0
3         1      1     2
4         1      3     1
5         0      2     1

Um ein Zuordnungswörterbuch zu erstellen, können Sie die Kategorien einfach mithilfe eines Wörterbuchverständnisses auflisten:

>>> {col: {n: cat for n, cat in enumerate(df[col].astype('category').cat.categories)} 
     for col in df}

{'location': {0: 'New_York', 1: 'San_Diego'},
 'owner': {0: 'Brick', 1: 'Champ', 2: 'Ron', 3: 'Veronica'},
 'pets': {0: 'cat', 1: 'dog', 2: 'monkey'}}
Alexander
quelle
Wenn ich für eine Spalte (Beispielzielvariable: Y) zurück (rückwärts) gehen möchte, wie mache ich das?
Ib D
9

Dies beantwortet Ihre Frage nicht direkt (auf die Naputipulu Jon und PriceHardman fantastische Antworten haben).

Für einige Klassifizierungsaufgaben usw. können Sie diese jedoch verwenden

pandas.get_dummies(input_df) 

Dies kann einen Datenrahmen mit kategorialen Daten eingeben und einen Datenrahmen mit Binärwerten zurückgeben. Variablenwerte werden im resultierenden Datenrahmen in Spaltennamen codiert. Mehr

Anurag Priyadarshi
quelle
6

Angenommen, Sie versuchen lediglich, ein sklearn.preprocessing.LabelEncoder()Objekt zu erhalten, mit dem Ihre Spalten dargestellt werden können, müssen Sie lediglich Folgendes tun:

le.fit(df.columns)

Im obigen Code haben Sie eine eindeutige Nummer, die jeder Spalte entspricht. Genauer gesagt erhalten Sie eine 1: 1-Zuordnung von df.columnszu le.transform(df.columns.get_values()). Um die Codierung einer Spalte zu erhalten, übergeben Sie sie einfach an le.transform(...). Im Folgenden wird beispielsweise die Codierung für jede Spalte angegeben:

le.transform(df.columns.get_values())

Angenommen, Sie möchten ein sklearn.preprocessing.LabelEncoder()Objekt für alle Ihre Zeilenbeschriftungen erstellen , können Sie Folgendes tun:

le.fit([y for x in df.get_values() for y in x])

In diesem Fall haben Sie höchstwahrscheinlich nicht eindeutige Zeilenbeschriftungen (wie in Ihrer Frage gezeigt). Um zu sehen, welche Klassen der Encoder erstellt hat, können Sie dies tun le.classes_. Sie werden feststellen, dass dies die gleichen Elemente wie in haben sollte set(y for x in df.get_values() for y in x). Verwenden Sie erneut, um eine Zeilenbeschriftung in eine codierte Beschriftung umzuwandeln le.transform(...). Wenn Sie beispielsweise die Bezeichnung für die erste Spalte im df.columnsArray und die erste Zeile abrufen möchten , können Sie Folgendes tun:

le.transform([df.get_value(0, df.columns[0])])

Die Frage, die Sie in Ihrem Kommentar hatten, ist etwas komplizierter, kann aber dennoch beantwortet werden:

le.fit([str(z) for z in set((x[0], y) for x in df.iteritems() for y in x[1])])

Der obige Code bewirkt Folgendes:

  1. Bilden Sie eine eindeutige Kombination aller Paare von (Spalte, Zeile)
  2. Stellen Sie jedes Paar als String-Version des Tupels dar. Dies ist eine Problemumgehung, um die LabelEncoderKlasse zu überwinden, die keine Tupel als Klassennamen unterstützt.
  3. Passt die neuen Artikel an die LabelEncoder.

Die Verwendung dieses neuen Modells ist etwas komplizierter. Angenommen, wir möchten die Darstellung für dasselbe Element extrahieren, das wir im vorherigen Beispiel nachgeschlagen haben (die erste Spalte in df.columns und die erste Zeile), können wir dies tun:

le.transform([str((df.columns[0], df.get_value(0, df.columns[0])))])

Denken Sie daran, dass jede Suche jetzt eine Zeichenfolgendarstellung eines Tupels ist, das die (Spalte, Zeile) enthält.

TehTechGuy
quelle
5

Nein, LabelEncodermacht das nicht. Es werden 1-D-Arrays von Klassenbezeichnungen verwendet und 1-D-Arrays erstellt. Es wurde entwickelt, um Klassenbeschriftungen bei Klassifizierungsproblemen zu behandeln, nicht bei willkürlichen Daten. Für jeden Versuch, sie für andere Zwecke zu erzwingen, ist Code erforderlich, um das eigentliche Problem in das gelöste Problem (und die Lösung zurück in den ursprünglichen Bereich) umzuwandeln.

Fred Foo
quelle
Ok, was ist angesichts dessen Ihr Vorschlag, wie ich Zeichenfolgenbezeichnungen am besten einzeln codieren kann DataFrame?
Bryan
@Bryan Schau dir den LabelEncoderCode an und passe ihn an. Ich benutze Pandas nicht selbst, also weiß ich nicht, wie schwer das sein wird.
Fred Foo
Ich werde auch andere pandasLeute bei dieser Frage eine Pause einlegen lassen - ich bin sicher, dass ich nicht die einzige Person mit dieser Herausforderung bin, also hoffe ich, dass es da draußen eine vorgefertigte Lösung gibt.
Bryan
5

Dies ist eineinhalb Jahre später, aber auch ich musste in der Lage sein, .transform()mehrere Pandas-Datenrahmenspalten gleichzeitig zu erstellen (und auch in der Lage zu .inverse_transform()sein). Dies erweitert den hervorragenden Vorschlag von @PriceHardman oben:

class MultiColumnLabelEncoder(LabelEncoder):
    """
    Wraps sklearn LabelEncoder functionality for use on multiple columns of a
    pandas dataframe.

    """
    def __init__(self, columns=None):
        self.columns = columns

    def fit(self, dframe):
        """
        Fit label encoder to pandas columns.

        Access individual column classes via indexig `self.all_classes_`

        Access individual column encoders via indexing
        `self.all_encoders_`
        """
        # if columns are provided, iterate through and get `classes_`
        if self.columns is not None:
            # ndarray to hold LabelEncoder().classes_ for each
            # column; should match the shape of specified `columns`
            self.all_classes_ = np.ndarray(shape=self.columns.shape,
                                           dtype=object)
            self.all_encoders_ = np.ndarray(shape=self.columns.shape,
                                            dtype=object)
            for idx, column in enumerate(self.columns):
                # fit LabelEncoder to get `classes_` for the column
                le = LabelEncoder()
                le.fit(dframe.loc[:, column].values)
                # append the `classes_` to our ndarray container
                self.all_classes_[idx] = (column,
                                          np.array(le.classes_.tolist(),
                                                  dtype=object))
                # append this column's encoder
                self.all_encoders_[idx] = le
        else:
            # no columns specified; assume all are to be encoded
            self.columns = dframe.iloc[:, :].columns
            self.all_classes_ = np.ndarray(shape=self.columns.shape,
                                           dtype=object)
            for idx, column in enumerate(self.columns):
                le = LabelEncoder()
                le.fit(dframe.loc[:, column].values)
                self.all_classes_[idx] = (column,
                                          np.array(le.classes_.tolist(),
                                                  dtype=object))
                self.all_encoders_[idx] = le
        return self

    def fit_transform(self, dframe):
        """
        Fit label encoder and return encoded labels.

        Access individual column classes via indexing
        `self.all_classes_`

        Access individual column encoders via indexing
        `self.all_encoders_`

        Access individual column encoded labels via indexing
        `self.all_labels_`
        """
        # if columns are provided, iterate through and get `classes_`
        if self.columns is not None:
            # ndarray to hold LabelEncoder().classes_ for each
            # column; should match the shape of specified `columns`
            self.all_classes_ = np.ndarray(shape=self.columns.shape,
                                           dtype=object)
            self.all_encoders_ = np.ndarray(shape=self.columns.shape,
                                            dtype=object)
            self.all_labels_ = np.ndarray(shape=self.columns.shape,
                                          dtype=object)
            for idx, column in enumerate(self.columns):
                # instantiate LabelEncoder
                le = LabelEncoder()
                # fit and transform labels in the column
                dframe.loc[:, column] =\
                    le.fit_transform(dframe.loc[:, column].values)
                # append the `classes_` to our ndarray container
                self.all_classes_[idx] = (column,
                                          np.array(le.classes_.tolist(),
                                                  dtype=object))
                self.all_encoders_[idx] = le
                self.all_labels_[idx] = le
        else:
            # no columns specified; assume all are to be encoded
            self.columns = dframe.iloc[:, :].columns
            self.all_classes_ = np.ndarray(shape=self.columns.shape,
                                           dtype=object)
            for idx, column in enumerate(self.columns):
                le = LabelEncoder()
                dframe.loc[:, column] = le.fit_transform(
                        dframe.loc[:, column].values)
                self.all_classes_[idx] = (column,
                                          np.array(le.classes_.tolist(),
                                                  dtype=object))
                self.all_encoders_[idx] = le
        return dframe.loc[:, self.columns].values

    def transform(self, dframe):
        """
        Transform labels to normalized encoding.
        """
        if self.columns is not None:
            for idx, column in enumerate(self.columns):
                dframe.loc[:, column] = self.all_encoders_[
                    idx].transform(dframe.loc[:, column].values)
        else:
            self.columns = dframe.iloc[:, :].columns
            for idx, column in enumerate(self.columns):
                dframe.loc[:, column] = self.all_encoders_[idx]\
                    .transform(dframe.loc[:, column].values)
        return dframe.loc[:, self.columns].values

    def inverse_transform(self, dframe):
        """
        Transform labels back to original encoding.
        """
        if self.columns is not None:
            for idx, column in enumerate(self.columns):
                dframe.loc[:, column] = self.all_encoders_[idx]\
                    .inverse_transform(dframe.loc[:, column].values)
        else:
            self.columns = dframe.iloc[:, :].columns
            for idx, column in enumerate(self.columns):
                dframe.loc[:, column] = self.all_encoders_[idx]\
                    .inverse_transform(dframe.loc[:, column].values)
        return dframe.loc[:, self.columns].values

Beispiel:

Wenn dfund df_copy()gemischte pandasDatenrahmen sind, können Sie die MultiColumnLabelEncoder()auf dtype=objectfolgende Weise auf die Spalten anwenden :

# get `object` columns
df_object_columns = df.iloc[:, :].select_dtypes(include=['object']).columns
df_copy_object_columns = df_copy.iloc[:, :].select_dtypes(include=['object']).columns

# instantiate `MultiColumnLabelEncoder`
mcle = MultiColumnLabelEncoder(columns=object_columns)

# fit to `df` data
mcle.fit(df)

# transform the `df` data
mcle.transform(df)

# returns output like below
array([[1, 0, 0, ..., 1, 1, 0],
       [0, 5, 1, ..., 1, 1, 2],
       [1, 1, 1, ..., 1, 1, 2],
       ..., 
       [3, 5, 1, ..., 1, 1, 2],

# transform `df_copy` data
mcle.transform(df_copy)

# returns output like below (assuming the respective columns 
# of `df_copy` contain the same unique values as that particular 
# column in `df`
array([[1, 0, 0, ..., 1, 1, 0],
       [0, 5, 1, ..., 1, 1, 2],
       [1, 1, 1, ..., 1, 1, 2],
       ..., 
       [3, 5, 1, ..., 1, 1, 2],

# inverse `df` data
mcle.inverse_transform(df)

# outputs data like below
array([['August', 'Friday', '2013', ..., 'N', 'N', 'CA'],
       ['April', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
       ['August', 'Monday', '2014', ..., 'N', 'N', 'NJ'],
       ..., 
       ['February', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
       ['April', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
       ['March', 'Tuesday', '2013', ..., 'N', 'N', 'NJ']], dtype=object)

# inverse `df_copy` data
mcle.inverse_transform(df_copy)

# outputs data like below
array([['August', 'Friday', '2013', ..., 'N', 'N', 'CA'],
       ['April', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
       ['August', 'Monday', '2014', ..., 'N', 'N', 'NJ'],
       ..., 
       ['February', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
       ['April', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
       ['March', 'Tuesday', '2013', ..., 'N', 'N', 'NJ']], dtype=object)

Sie können über die Indizierung auf einzelne Spaltenklassen, Spaltenbeschriftungen und Spaltencodierer zugreifen, die für jede Spalte verwendet werden:

mcle.all_classes_
mcle.all_encoders_
mcle.all_labels_

Jason Wolosonovich
quelle
Hallo Jason, mcle.all_labels_ scheint nicht zu funktionieren (Python 3.5, Conda 4.3.29, Sklearn 0.18.1, Pandas 0.20.1. Ich erhalte: AttributeError: Das Objekt 'MultiColumnLabelEncoder' hat kein Attribut 'all_labels_'
Jason
@Jason Hallo, tut mir leid, ich habe das bis heute nicht gesehen: / aber wenn ich raten müsste, würde ich sagen, dass Sie nur die fitMethode von oben verwendet haben, die erst dann Etiketten erzeugt, wenn Sie sie anwenden ( transform/ fit_transform) die Daten.
Jason Wolosonovich
Ich denke, Sie müssen ein besseres Beispiel geben - ich konnte nicht alle Ihre Codes erneut ausführen.
user702846
2

Nach den Kommentaren zur Lösung von @PriceHardman würde ich die folgende Version der Klasse vorschlagen:

class LabelEncodingColoumns(BaseEstimator, TransformerMixin):
def __init__(self, cols=None):
    pdu._is_cols_input_valid(cols)
    self.cols = cols
    self.les = {col: LabelEncoder() for col in cols}
    self._is_fitted = False

def transform(self, df, **transform_params):
    """
    Scaling ``cols`` of ``df`` using the fitting

    Parameters
    ----------
    df : DataFrame
        DataFrame to be preprocessed
    """
    if not self._is_fitted:
        raise NotFittedError("Fitting was not preformed")
    pdu._is_cols_subset_of_df_cols(self.cols, df)

    df = df.copy()

    label_enc_dict = {}
    for col in self.cols:
        label_enc_dict[col] = self.les[col].transform(df[col])

    labelenc_cols = pd.DataFrame(label_enc_dict,
        # The index of the resulting DataFrame should be assigned and
        # equal to the one of the original DataFrame. Otherwise, upon
        # concatenation NaNs will be introduced.
        index=df.index
    )

    for col in self.cols:
        df[col] = labelenc_cols[col]
    return df

def fit(self, df, y=None, **fit_params):
    """
    Fitting the preprocessing

    Parameters
    ----------
    df : DataFrame
        Data to use for fitting.
        In many cases, should be ``X_train``.
    """
    pdu._is_cols_subset_of_df_cols(self.cols, df)
    for col in self.cols:
        self.les[col].fit(df[col])
    self._is_fitted = True
    return self

Diese Klasse passt zum Encoder des Trainingssatzes und verwendet beim Transformieren die angepasste Version. Die erste Version des Codes finden Sie hier .

Dror
quelle
2

Ein kurzer Weg zu LabelEncoder()mehreren Spalten mit einem dict():

from sklearn.preprocessing import LabelEncoder
le_dict = {col: LabelEncoder() for col in columns }
for col in columns:
    le_dict[col].fit_transform(df[col])

und Sie können dies verwenden le_dict, um jede andere Spalte zu kennzeichnen:

le_dict[col].transform(df_another[col])
Tom
quelle
2

Es ist möglich, dies alles direkt in Pandas zu tun und ist für eine einzigartige Fähigkeit der replaceMethode gut geeignet .

Lassen Sie uns zunächst ein Wörterbuch mit Wörterbüchern erstellen, in denen die Spalten und ihre Werte ihren neuen Ersatzwerten zugeordnet werden.

transform_dict = {}
for col in df.columns:
    cats = pd.Categorical(df[col]).categories
    d = {}
    for i, cat in enumerate(cats):
        d[cat] = i
    transform_dict[col] = d

transform_dict
{'location': {'New_York': 0, 'San_Diego': 1},
 'owner': {'Brick': 0, 'Champ': 1, 'Ron': 2, 'Veronica': 3},
 'pets': {'cat': 0, 'dog': 1, 'monkey': 2}}

Da dies immer eine Eins-zu-Eins-Zuordnung ist, können wir das innere Wörterbuch invertieren, um eine Zuordnung der neuen Werte zum Original wiederherzustellen.

inverse_transform_dict = {}
for col, d in transform_dict.items():
    inverse_transform_dict[col] = {v:k for k, v in d.items()}

inverse_transform_dict
{'location': {0: 'New_York', 1: 'San_Diego'},
 'owner': {0: 'Brick', 1: 'Champ', 2: 'Ron', 3: 'Veronica'},
 'pets': {0: 'cat', 1: 'dog', 2: 'monkey'}}

Jetzt können wir die einzigartige Fähigkeit der replaceMethode nutzen, um eine verschachtelte Liste von Wörterbüchern zu erstellen und die äußeren Schlüssel als Spalten und die inneren Schlüssel als Werte zu verwenden, die wir ersetzen möchten.

df.replace(transform_dict)
   location  owner  pets
0         1      1     0
1         0      2     1
2         0      0     0
3         1      1     2
4         1      3     1
5         0      2     1

Wir können leicht zum Original zurückkehren, indem wir die replaceMethode erneut verketten

df.replace(transform_dict).replace(inverse_transform_dict)
    location     owner    pets
0  San_Diego     Champ     cat
1   New_York       Ron     dog
2   New_York     Brick     cat
3  San_Diego     Champ  monkey
4  San_Diego  Veronica     dog
5   New_York       Ron     dog
Ted Petrou
quelle
2

Nach vielen Suchen und Experimenten mit einigen Antworten hier und anderswo denke ich, dass Ihre Antwort hier ist :

pd.DataFrame (column = df.columns, data = LabelEncoder (). fit_transform (df.values.flatten ()). reshape (df.shape))

Dadurch bleiben Kategorienamen über Spalten hinweg erhalten:

import pandas as pd
from sklearn.preprocessing import LabelEncoder

df = pd.DataFrame([['A','B','C','D','E','F','G','I','K','H'],
                   ['A','E','H','F','G','I','K','','',''],
                   ['A','C','I','F','H','G','','','','']], 
                  columns=['A1', 'A2', 'A3','A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10'])

pd.DataFrame(columns=df.columns, data=LabelEncoder().fit_transform(df.values.flatten()).reshape(df.shape))

    A1  A2  A3  A4  A5  A6  A7  A8  A9  A10
0   1   2   3   4   5   6   7   9   10  8
1   1   5   8   6   7   9   10  0   0   0
2   1   3   9   6   8   7   0   0   0   0
Christopher
quelle
2

Ich habe den Quellcode ( https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/preprocessing/label.py ) von LabelEncoder überprüft. Es basierte auf einer Reihe von Numpy-Transformationen, von denen eine np.unique () ist. Und diese Funktion akzeptiert nur 1-d-Array-Eingaben. (Korrigieren Sie mich, wenn ich falsch liege).

Sehr grobe Ideen ... Identifizieren Sie zuerst, welche Spalten LabelEncoder benötigen, und durchlaufen Sie dann jede Spalte.

def cat_var(df): 
    """Identify categorical features. 

    Parameters
    ----------
    df: original df after missing operations 

    Returns
    -------
    cat_var_df: summary df with col index and col name for all categorical vars
    """
    col_type = df.dtypes
    col_names = list(df)

    cat_var_index = [i for i, x in enumerate(col_type) if x=='object']
    cat_var_name = [x for i, x in enumerate(col_names) if i in cat_var_index]

    cat_var_df = pd.DataFrame({'cat_ind': cat_var_index, 
                               'cat_name': cat_var_name})

    return cat_var_df



from sklearn.preprocessing import LabelEncoder 

def column_encoder(df, cat_var_list):
    """Encoding categorical feature in the dataframe

    Parameters
    ----------
    df: input dataframe 
    cat_var_list: categorical feature index and name, from cat_var function

    Return
    ------
    df: new dataframe where categorical features are encoded
    label_list: classes_ attribute for all encoded features 
    """

    label_list = []
    cat_var_df = cat_var(df)
    cat_list = cat_var_df.loc[:, 'cat_name']

    for index, cat_feature in enumerate(cat_list): 

        le = LabelEncoder()

        le.fit(df.loc[:, cat_feature])    
        label_list.append(list(le.classes_))

        df.loc[:, cat_feature] = le.transform(df.loc[:, cat_feature])

    return df, label_list

Der zurückgegebene df ist derjenige nach der Codierung, und label_list zeigt Ihnen, was all diese Werte in der entsprechenden Spalte bedeuten. Dies ist ein Ausschnitt aus einem Datenprozess-Skript, das ich für die Arbeit geschrieben habe. Lassen Sie mich wissen, wenn Sie glauben, dass es weitere Verbesserungen geben könnte.

EDIT: Ich möchte hier nur erwähnen, dass die oben genannten Methoden mit Datenrahmen funktionieren, ohne dass die besten fehlen. Nicht sicher, wie es in Richtung Datenrahmen funktioniert, enthält fehlende Daten. (Ich hatte ein Problem mit fehlenden Prozeduren, bevor ich die obigen Methoden ausführte.)

willaccc
quelle
1

Wenn wir eine einzelne Spalte haben, um die Etikettencodierung durchzuführen, und ihre inverse Transformation, ist es einfach, wie es geht, wenn es mehrere Spalten in Python gibt

def stringtocategory(dataset):
    '''
    @author puja.sharma
    @see The function label encodes the object type columns and gives label      encoded and inverse tranform of the label encoded data
    @param dataset dataframe on whoes column the label encoding has to be done
    @return label encoded and inverse tranform of the label encoded data.
   ''' 
   data_original = dataset[:]
   data_tranformed = dataset[:]
   for y in dataset.columns:
       #check the dtype of the column object type contains strings or chars
       if (dataset[y].dtype == object):
          print("The string type features are  : " + y)
          le = preprocessing.LabelEncoder()
          le.fit(dataset[y].unique())
          #label encoded data
          data_tranformed[y] = le.transform(dataset[y])
          #inverse label transform  data
          data_original[y] = le.inverse_transform(data_tranformed[y])
   return data_tranformed,data_original
Puja Sharma
quelle
1

Wenn Sie beide Arten von Daten im Datenrahmen numerisch und kategorisch haben, können Sie Folgendes verwenden: Hier ist X mein Datenrahmen, der beide Variablen kategorisch und numerisch enthält

from sklearn import preprocessing
le = preprocessing.LabelEncoder()

for i in range(0,X.shape[1]):
    if X.dtypes[i]=='object':
        X[X.columns[i]] = le.fit_transform(X[X.columns[i]])

Hinweis: Diese Technik ist gut, wenn Sie nicht daran interessiert sind, sie zurück zu konvertieren.

Vikas Gupta
quelle
1

Verwenden von Neuraxle

TLDR; Sie können hier die FlattenForEach- Wrapper-Klasse verwenden, um Ihre df einfach wie folgt zu transformieren : FlattenForEach(LabelEncoder(), then_unflatten=True).fit_transform(df).

Mit dieser Methode kann Ihr Label-Encoder in eine reguläre Scikit-Lern-Pipeline passen und diese transformieren . Importieren wir einfach:

from sklearn.preprocessing import LabelEncoder
from neuraxle.steps.column_transformer import ColumnTransformer
from neuraxle.steps.loop import FlattenForEach

Gleicher gemeinsam genutzter Encoder für Spalten:

So wird ein gemeinsam genutzter LabelEncoder auf alle Daten angewendet, um sie zu codieren:

    p = FlattenForEach(LabelEncoder(), then_unflatten=True)

Ergebnis:

    p, predicted_output = p.fit_transform(df.values)
    expected_output = np.array([
        [6, 7, 6, 8, 7, 7],
        [1, 3, 0, 1, 5, 3],
        [4, 2, 2, 4, 4, 2]
    ]).transpose()
    assert np.array_equal(predicted_output, expected_output)

Unterschiedliche Encoder pro Spalte:

Und so wird ein erster eigenständiger LabelEncoder auf die Haustiere angewendet, und ein zweiter wird für den Spaltenbesitzer und den Standort freigegeben. Um genau zu sein, haben wir hier eine Mischung aus verschiedenen und gemeinsam genutzten Label-Encodern:

    p = ColumnTransformer([
        # A different encoder will be used for column 0 with name "pets":
        (0, FlattenForEach(LabelEncoder(), then_unflatten=True)),
        # A shared encoder will be used for column 1 and 2, "owner" and "location":
        ([1, 2], FlattenForEach(LabelEncoder(), then_unflatten=True)),
    ], n_dimension=2)

Ergebnis:

    p, predicted_output = p.fit_transform(df.values)
    expected_output = np.array([
        [0, 1, 0, 2, 1, 1],
        [1, 3, 0, 1, 5, 3],
        [4, 2, 2, 4, 4, 2]
    ]).transpose()
    assert np.array_equal(predicted_output, expected_output)
Guillaume Chevalier
quelle
0

Hauptsächlich verwendete @ Alexander Antwort, musste aber einige Änderungen vornehmen -

cols_need_mapped = ['col1', 'col2']

mapper = {col: {cat: n for n, cat in enumerate(df[col].astype('category').cat.categories)} 
     for col in df[cols_need_mapped]}

for c in cols_need_mapped :
    df[c] = df[c].map(mapper[c])

Für eine spätere Wiederverwendung können Sie die Ausgabe einfach in einem JSON-Dokument speichern. Wenn Sie sie benötigen, lesen Sie sie ein und verwenden die oben beschriebene .map()Funktion.

bbennett36
quelle
0

Das Problem ist die Form der Daten (pd dataframe), die Sie an die Anpassungsfunktion übergeben. Sie müssen 1d Liste bestehen.

ali sadr
quelle
0
import pandas as pd
from sklearn.preprocessing import LabelEncoder

train=pd.read_csv('.../train.csv')

#X=train.loc[:,['waterpoint_type_group','status','waterpoint_type','source_class']].values
# Create a label encoder object 
def MultiLabelEncoder(columnlist,dataframe):
    for i in columnlist:

        labelencoder_X=LabelEncoder()
        dataframe[i]=labelencoder_X.fit_transform(dataframe[i])
columnlist=['waterpoint_type_group','status','waterpoint_type','source_class','source_type']
MultiLabelEncoder(columnlist,train)

Hier lese ich eine CSV von Ort und in Funktion übergebe ich die Spaltenliste, die ich kennzeichnen möchte, und den Datenrahmen, den ich anwenden möchte.


quelle
0

Wie wäre es damit?

def MultiColumnLabelEncode(choice, columns, X):
    LabelEncoders = []
    if choice == 'encode':
        for i in enumerate(columns):
            LabelEncoders.append(LabelEncoder())
        i=0    
        for cols in columns:
            X[:, cols] = LabelEncoders[i].fit_transform(X[:, cols])
            i += 1
    elif choice == 'decode': 
        for cols in columns:
            X[:, cols] = LabelEncoders[i].inverse_transform(X[:, cols])
            i += 1
    else:
        print('Please select correct parameter "choice". Available parameters: encode/decode')

Es ist nicht das effizienteste, aber es funktioniert und es ist super einfach.

Dominik Novotný
quelle