Вменить категориальные отсутствующие значения в scikit-learn

у меня есть данные панды с некоторыми столбцами текстового типа. Есть некоторые значения NaN вместе с этими текстовыми столбцами. То, что я пытаюсь сделать, - это приписать этим НАН sklearn.preprocessing.Imputer (замена NaN на наиболее частое значение). Проблема в реализации. Предположим, что существует фрейм данных Pandas df с 30 столбцами, 10 из которых имеют категориальный характер. Однажды я бегу:

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

Python генерирует error: 'could not convert string to float: 'run1'', где 'run1' - обычное (не отсутствующее) значение из первого столбца с категориальные данные.

любая помощь будет очень добро пожаловать!

6 ответов


чтобы использовать средние значения для числовых столбцов и наиболее частое значение для нечисловых столбцов, вы можете сделать что-то вроде этого. Можно различать целые числа и плавает. Думаю, вместо этого имеет смысл использовать медиану для целых столбцов.

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)

, который печатает,

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

можно использовать sklearn_pandas.CategoricalImputer для категориальных столбцов. Подробности:

во-первых, (из книги практическое машинное обучение с Scikit-Learn и TensorFlow) вы можете иметь подпипелины для числовых и строковых / категориальных функций, где первый трансформатор каждой подпипелины-это селектор, который принимает список имен столбцов (и full_pipeline.fit_transform() принимает фрейм данных pandas):

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

затем вы можете объединить эти трубопроводы с sklearn.pipeline.FeatureUnion, для пример:

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

теперь, в num_pipeline вы можете просто использовать sklearn.preprocessing.Imputer(), но в cat_pipline, вы можете использовать CategoricalImputer() С sklearn_pandas пакета.

Примечание: sklearn-pandas пакет можно установить с pip install sklearn-pandas, но импортируется как import sklearn_pandas


копирование и изменение ответа sveitser, я сделал imputer для панд.Объект серии

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)

чтобы использовать его, вы должны сделать следующее:

# 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

этот код заполняет серию с наиболее частой категорией:

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)

выходы:

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

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

вдохновленный ответами здесь и за неимением goto Imputer для всех случаев использования, я закончил писать это. Он поддерживает четыре стратегии для вменения mean, mode, median, fill и pd.DataFrame и Pd.Series.

mean и median работает только для числовых данных, mode и fill работает как для числовых, так и для категориальных данных.

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)

использование

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

похожие. Изменить Imputer на 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() находит наиболее частое значение для каждого столбца, а затем pandas.DataFrame.fillna() заполняет отсутствующие значения этими. Другое strategy значения по-прежнему обрабатываются таким же образом Imputer.