web-dev-qa-db-fra.com

label-encoder encodant les valeurs manquantes

J'utilise l'encodeur d'étiquettes pour convertir les données catégoriques en valeurs numériques.

Comment LabelEncoder gère-t-il les valeurs manquantes?

from sklearn.preprocessing import LabelEncoder
import pandas as pd
import numpy as np
a = pd.DataFrame(['A','B','C',np.nan,'D','A'])
le = LabelEncoder()
le.fit_transform(a)

Sortie:

array([1, 2, 3, 0, 4, 1])

Pour l'exemple ci-dessus, le codeur d'étiquette a modifié les valeurs NaN en une catégorie. Comment saurais-je quelle catégorie représente les valeurs manquantes?

19
saurabh agarwal

N'utilisez pas LabelEncoder avec les valeurs manquantes. Je ne sais pas quelle version de scikit-learn vous utilisez, mais dans 0.17.1, votre code déclenche TypeError: unorderable types: str() > float().

Comme vous pouvez le voir dans le code source il utilise numpy.unique par rapport aux données à coder, ce qui soulève TypeError si des valeurs manquantes sont trouvées. Si vous voulez encoder des valeurs manquantes, commencez par changer son type en chaîne:

a[pd.isnull(a)]  = 'NaN'
10
dukebody

Bonjour un petit bidouillage informatique que j'ai fait pour mon propre travail:

from sklearn.preprocessing import LabelEncoder
import pandas as pd
import numpy as np
a = pd.DataFrame(['A','B','C',np.nan,'D','A'])
le = LabelEncoder()
### fit with the desired col, col in position 0 for this example
fit_by = pd.Series([i for i in a.iloc[:,0].unique() if type(i) == str])
le.fit(fit_by)
### Set transformed col leaving np.NaN as they are
a["transformed"] = fit_by.apply(lambda x: le.transform([x])[0] if type(x) == str else x)
3
Kerem T

Vous pouvez renseigner les na par des valeurs, puis changer le type de colonne dataframe en chaîne pour que tout fonctionne correctement.

from sklearn.preprocessing import LabelEncoder
import pandas as pd
import numpy as np
a = pd.DataFrame(['A','B','C',np.nan,'D','A'])
a.fillna(99)
le = LabelEncoder()
le.fit_transform(a.astype(str))
2
raghu nanden

C'est ma solution car je n'étais pas satisfaite des solutions présentées ici. J'avais besoin d'un LabelEncoder qui conserve mes valeurs manquantes sous la forme 'NaN' pour pouvoir utiliser ensuite un Imputer. J'ai donc écrit ma propre classe LabelEncoder. Cela fonctionne avec DataFrames.

from sklearn.base import BaseEstimator
from sklearn.base import TransformerMixin
from sklearn.preprocessing import LabelEncoder

class LabelEncoderByCol(BaseEstimator, TransformerMixin):
    def __init__(self,col):
        #List of column names in the DataFrame that should be encoded
        self.col = col
        #Dictionary storing a LabelEncoder for each column
        self.le_dic = {}
        for el in self.col:
            self.le_dic[el] = LabelEncoder()

    def fit(self,x,y=None):
        #Fill missing values with the string 'NaN'
        x[self.col] = x[self.col].fillna('NaN')
        for el in self.col:
            #Only use the values that are not 'NaN' to fit the Encoder
            a = x[el][x[el]!='NaN']
            self.le_dic[el].fit(a)
        return self

    def transform(self,x,y=None):
        #Fill missing values with the string 'NaN'
        x[self.col] = x[self.col].fillna('NaN')
        for el in self.col:
            #Only use the values that are not 'NaN' to fit the Encoder
            a = x[el][x[el]!='NaN']
            #Store an ndarray of the current column
            b = x[el].get_values()
            #Replace the elements in the ndarray that are not 'NaN'
            #using the transformer
            b[b!='NaN'] = self.le_dic[el].transform(a)
            #Overwrite the column in the DataFrame
            x[el]=b
        #return the transformed DataFrame
        return x

Vous pouvez entrer un DataFrame, pas seulement une série à 1 dim. avec col, vous pouvez choisir les colonnes à encoder.

Je voudrais ici quelques commentaires.

2
Niclas von Caprivi

vous pouvez également utiliser un masque pour remplacer le bloc de données d'origine après l'étiquetage

df = pd.DataFrame({'A': ['x', np.NaN, 'z'], 'B': [1, 6, 9], 'C': [2, 1, np.NaN]})

    A   B   C
0   x   1   2.0
1   NaN 6   1.0
2   z   9   NaN

dfTmp = df
mask = df_1.isnull()
       A    B   C
0   False   False   False
1   True    False   False
2   False   False   True

df = df.astype(str).apply(LabelEncoder().fit_transform)
df.where(~mask, original)

A   B   C
0   1.0 0   1.0
1   NaN 1   0.0
2   2.0 2   NaN
1
ulrich

J'ai rencontré le même problème mais rien de ce qui précède n'a fonctionné pour moi. J'ai donc ajouté une nouvelle ligne aux données d'entraînement composée uniquement de "nan".

0
silent_dev

Adresses de codeur suivantes Aucune valeur dans chaque catégorie.

class MultiColumnLabelEncoder:
    def __init__(self):
        self.columns = None
        self.led = defaultdict(preprocessing.LabelEncoder)

    def fit(self, X):
        self.columns = X.columns
        for col in self.columns:
            cat = X[col].unique()
            cat = [x if x is not None else "None" for x in cat]
            self.led[col].fit(cat)
        return self

    def fit_transform(self, X):
        if self.columns is None:
            self.fit(X)
        return self.transform(X)

    def transform(self, X):
        return X.apply(lambda x:  self.led[x.name].transform(x.apply(lambda e: e if e is not None else "None")))

    def inverse_transform(self, X):
        return X.apply(lambda x: self.led[x.name].inverse_transform(x))

Exemple d'utilisation

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


print(df)

   location     owner    pets
0  San_Diego     Champ     cat
1   New_York       Ron     dog
2   New_York     Brick     cat
3  San_Diego      None  monkey
4  San_Diego  Veronica     dog
5       None       Ron     dog

le = MultiColumnLabelEncoder()
le.fit(df)

transformed = le.transform(df)
print(transformed)

   location  owner  pets
0         2      1     0
1         0      3     1
2         0      0     0
3         2      2     2
4         2      4     1
5         1      3     1

inverted = le.inverse_transform(transformed)
print(inverted)

        location     owner    pets
0  San_Diego     Champ     cat
1   New_York       Ron     dog
2   New_York     Brick     cat
3  San_Diego      None  monkey
4  San_Diego  Veronica     dog
5       None       Ron     dog
0
Ashok Kumar Pant

Un moyen facile est-ce

C'est un exemple de Titanic

LABEL_COL = ["Sex", "Embarked"]

def label(df):
    _df = df.copy()
    le = LabelEncoder()
    for col in LABEL_COL:
        # Not NaN index
        idx = ~_df[col].isna()
        _df.loc[idx, col] \
            = le.fit(_df.loc[idx, col]).transform(_df.loc[idx, col])
    return _df
0
chankane

Voici comment je l'ai fait:

import pandas as pd
from sklearn.preprocessing import LabelEncoder

UNKNOWN_TOKEN = '<unknown>'
a = pd.Series(['A','B','C', 'D','A'], dtype=str).unique().tolist()
a.append(UNKNOWN_TOKEN)
le = LabelEncoder()
le.fit_transform(a)
embedding_map = dict(Zip(le.classes_, le.transform(le.classes_)))

et lors de l'application à de nouvelles données de test:

test_df = test_df.apply(lambda x: x if x in embedding_map else UNKNOWN_TOKEN)
le.transform(test_df)
0
muon