web-dev-qa-db-fra.com

Imputez les valeurs manquantes catégoriques dans scikit-learn

J'ai des données sur les pandas avec des colonnes de type texte. Il y a quelques valeurs NaN avec ces colonnes de texte. Ce que j'essaie de faire est d'imputer ces NaN avec sklearn.preprocessing.Imputer (en remplaçant NaN par la valeur la plus fréquente). Le problème est en cours d’application . Supposons qu’il existe une base de données Pandas contenant 30 colonnes, dont 10 de nature catégorique ...

from sklearn.preprocessing import Imputer
imp = Imputer(missing_values='NaN', strategy='most_frequent', axis=0)
imp.fit(df) 

Python génère un error: 'could not convert string to float: 'run1'', où 'run1' est une valeur ordinaire (non manquante) de la première colonne avec des données catégorielles.

Toute aide est la bienvenue

44
night_bat

Pour utiliser les valeurs moyennes pour les colonnes numériques et la valeur la plus fréquente pour les colonnes non numériques, vous pouvez procéder de la sorte. Vous pouvez encore distinguer entre les entiers et les flottants. J'imagine qu'il pourrait être judicieux d'utiliser la médiane pour les colonnes entières à la place.

import pandas as pd
import numpy as np

from sklearn.base import TransformerMixin

class DataFrameImputer(TransformerMixin):

    def __init__(self):
        """Impute missing values.

        Columns of dtype object are imputed with the most frequent value 
        in column.

        Columns of other types are imputed with mean of column.

        """
    def fit(self, X, y=None):

        self.fill = pd.Series([X[c].value_counts().index[0]
            if X[c].dtype == np.dtype('O') else X[c].mean() for c in X],
            index=X.columns)

        return self

    def transform(self, X, y=None):
        return X.fillna(self.fill)

data = [
    ['a', 1, 2],
    ['b', 1, 1],
    ['b', 2, 2],
    [np.nan, np.nan, np.nan]
]

X = pd.DataFrame(data)
xt = DataFrameImputer().fit_transform(X)

print('before...')
print(X)
print('after...')
print(xt)

qui imprime,

before...
     0   1   2
0    a   1   2
1    b   1   1
2    b   2   2
3  NaN NaN NaN
after...
   0         1         2
0  a  1.000000  2.000000
1  b  1.000000  1.000000
2  b  2.000000  2.000000
3  b  1.333333  1.666667
77
sveitser

Vous pouvez utiliser sklearn_pandas.CategoricalImputer pour les colonnes catégoriques. Détails:

Tout d'abord, (tiré du livre Hands-On Machine Learning avec Scikit-Learn et TensorFlow), vous pouvez avoir des sous-lignes pour les fonctions numériques et chaînes/catégories, le premier transformateur de chaque sous-ligne étant un sélecteur prenant une liste de noms de colonnes (et la fonction full_pipeline.fit_transform() un pandas DataFrame):

class DataFrameSelector(BaseEstimator, TransformerMixin):
    def __init__(self, attribute_names):
        self.attribute_names = attribute_names
    def fit(self, X, y=None):
        return self
    def transform(self, X):
        return X[self.attribute_names].values

Vous pouvez ensuite combiner ces sous-pipelines avec sklearn.pipeline.FeatureUnion, par exemple:

full_pipeline = FeatureUnion(transformer_list=[
    ("num_pipeline", num_pipeline),
    ("cat_pipeline", cat_pipeline)
])

Maintenant, dans le num_pipeline, vous pouvez simplement utiliser sklearn.preprocessing.Imputer(), mais dans le cat_pipline, vous pouvez utiliser CategoricalImputer() à partir du paquetage sklearn_pandas.

note: le paquetsklearn-pandas peut être installé avec pip install sklearn-pandas, mais il est importé en tant que import sklearn_pandas

4
Austin

Inspiré par les réponses fournies ici et par le manque d'un goto Imputer pour tous les cas d'utilisation, j'ai fini par écrire ceci. Il prend en charge quatre stratégies d'imputation mean, mode, median, fill qui fonctionne à la fois sur pd.DataFrame et Pd.Series.

mean et median ne fonctionne que pour les données numériques, mode et fill fonctionne pour les données numériques et catégorielles.

class CustomImputer(BaseEstimator, TransformerMixin):
    def __init__(self, strategy='mean',filler='NA'):
       self.strategy = strategy
       self.fill = filler

    def fit(self, X, y=None):
       if self.strategy in ['mean','median']:
           if not all(X.dtypes == np.number):
               raise ValueError('dtypes mismatch np.number dtype is \
                                 required for '+ self.strategy)
       if self.strategy == 'mean':
           self.fill = X.mean()
       Elif self.strategy == 'median':
           self.fill = X.median()
       Elif self.strategy == 'mode':
           self.fill = X.mode().iloc[0]
       Elif self.strategy == 'fill':
           if type(self.fill) is list and type(X) is pd.DataFrame:
               self.fill = dict([(cname, v) for cname,v in Zip(X.columns, self.fill)])
       return self

   def transform(self, X, y=None):
       return X.fillna(self.fill)

usage 

>> df   
    MasVnrArea  FireplaceQu
Id  
1   196.0   NaN
974 196.0   NaN
21  380.0   Gd
5   350.0   TA
651 NaN     Gd


>> CustomImputer(strategy='mode').fit_transform(df)
MasVnrArea  FireplaceQu
Id      
1   196.0   Gd
974 196.0   Gd
21  380.0   Gd
5   350.0   TA
651 196.0   Gd

>> CustomImputer(strategy='fill', filler=[0, 'NA']).fit_transform(df)
MasVnrArea  FireplaceQu
Id      
1   196.0   NA
974 196.0   NA
21  380.0   Gd
5   350.0   TA
651 0.0     Gd 
2
Gautham Kumaran

Copiant et modifiant la réponse du serveur, j'ai créé un imputer pour un objet pandas.Series

import numpy
import pandas 

from sklearn.base import TransformerMixin

class SeriesImputer(TransformerMixin):

    def __init__(self):
        """Impute missing values.

        If the Series is of dtype Object, then impute with the most frequent object.
        If the Series is not of dtype Object, then impute with the mean.  

        """
    def fit(self, X, y=None):
        if   X.dtype == numpy.dtype('O'): self.fill = X.value_counts().index[0]
        else                            : self.fill = X.mean()
        return self

    def transform(self, X, y=None):
       return X.fillna(self.fill)

Pour l'utiliser, vous feriez:

# Make a series
s1 = pandas.Series(['k', 'i', 't', 't', 'e', numpy.NaN])


a  = SeriesImputer()   # Initialize the imputer
a.fit(s1)              # Fit the imputer
s2 = a.transform(s1)   # Get a new series
2
user1367204
  • strategy = 'most_frequent' ne peut être utilisé qu'avec des fonctionnalités quantitatives, pas qualitatives. Cet importateur personnalisé peut être utilisé à la fois qualitatif et quantitatif. Également avec scikit learn imputer, nous pouvons l’utiliser pour l’ensemble du bloc de données (si toutes les entités sont quantitatives) ou nous pouvons utiliser «for loop» avec une liste de types d’entités/colonnes similaires (voir l’exemple ci-dessous). Mais imputer personnalisé peut être utilisé avec toutes les combinaisons.

        from sklearn.preprocessing import Imputer
        impute = Imputer(strategy='mean')
        for cols in ['quantitative_column', 'quant']:  # here both are quantitative features.
              xx[cols] = impute.fit_transform(xx[[cols]])
    
  • Imputer personnalisé: 

       from sklearn.preprocessing import Imputer
       from sklearn.base import TransformerMixin
    
       class CustomImputer(TransformerMixin):
             def __init__(self, cols=None, strategy='mean'):
                   self.cols = cols
                   self.strategy = strategy
    
             def transform(self, df):
                   X = df.copy()
                   impute = Imputer(strategy=self.strategy)
                   if self.cols == None:
                          self.cols = list(X.columns)
                   for col in self.cols:
                          if X[col].dtype == np.dtype('O') : 
                                 X[col].fillna(X[col].value_counts().index[0], inplace=True)
                          else : X[col] = impute.fit_transform(X[[col]])
    
                   return X
    
             def fit(self, *_):
                   return self
    
  • Trame de données:

          X = pd.DataFrame({'city':['tokyo', np.NaN, 'london', 'seattle', 'san 
                                     francisco', 'tokyo'], 
              'boolean':['yes', 'no', np.NaN, 'no', 'no', 'yes'], 
              'ordinal_column':['somewhat like', 'like', 'somewhat like', 'like', 
                                'somewhat like', 'dislike'], 
              'quantitative_column':[1, 11, -.5, 10, np.NaN, 20]})
    
    
                city              boolean   ordinal_column  quantitative_column
            0   tokyo             yes       somewhat like   1.0
            1   NaN               no        like            11.0
            2   london            NaN       somewhat like   -0.5
            3   seattle           no        like            10.0
            4   san francisco     no        somewhat like   NaN
            5   tokyo             yes       dislike         20.0
    
  • 1) Peut être utilisé avec une liste de types similaires de fonctionnalités.

     cci = CustomImputer(cols=['city', 'boolean']) # here default strategy = mean
     cci.fit_transform(X)
    
  • peut être utilisé avec la stratégie = médiane

     sd = CustomImputer(['quantitative_column'], strategy = 'median')
     sd.fit_transform(X)
    
  • 3) Peut être utilisé avec un cadre de données complet, il utilisera la moyenne par défaut (ou nous pouvons également le changer avec la médiane. Pour les caractéristiques qualitatives, il utilise la stratégie = 'le plus fréquemment' et pour la moyenne/médiane quantitative.

     call = CustomImputer()
     call.fit_transform(X)   
    
2
Piyush

Ce code complète une série avec la catégorie la plus fréquente:

import pandas as pd
import numpy as np

# create fake data 
m = pd.Series(list('abca'))
m.iloc[1] = np.nan #artificially introduce nan

print('m = ')
print(m)

#make dummy variables, count and sort descending:
most_common = pd.get_dummies(m).sum().sort_values(ascending=False).index[0] 

def replace_most_common(x):
    if pd.isnull(x):
        return most_common
    else:
        return x

new_m = m.map(replace_most_common) #apply function to original data

print('new_m = ')
print(new_m)

Les sorties:

m =
0      a
1    NaN
2      c
3      a
dtype: object

new_m =
0    a
1    a
2    c
3    a
dtype: object
1
scottlittle

Il existe un package sklearn-pandas qui permet d'imputer une variable catégorique https://github.com/scikit-learn-contrib/sklearn-pandas#categoricalimputer

>>> from sklearn_pandas import CategoricalImputer
>>> data = np.array(['a', 'b', 'b', np.nan], dtype=object)
>>> imputer = CategoricalImputer()
>>> imputer.fit_transform(data)
array(['a', 'b', 'b', 'b'], dtype=object)
0
prashanth

Similaire. Modifiez Imputer pour strategy='most_frequent':

class GeneralImputer(Imputer):
    def __init__(self, **kwargs):
        Imputer.__init__(self, **kwargs)

    def fit(self, X, y=None):
        if self.strategy == 'most_frequent':
            self.fills = pd.DataFrame(X).mode(axis=0).squeeze()
            self.statistics_ = self.fills.values
            return self
        else:
            return Imputer.fit(self, X, y=y)

    def transform(self, X):
        if hasattr(self, 'fills'):
            return pd.DataFrame(X).fillna(self.fills).values.astype(str)
        else:
            return Imputer.transform(self, X)

pandas.DataFrame.mode() trouve la valeur la plus fréquente pour chaque colonne, puis pandas.DataFrame.fillna() remplit les valeurs manquantes avec celles-ci. Les autres valeurs strategy sont toujours traitées de la même manière par Imputer.

0
immarried