Вменить категориальные отсутствующие значения в 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
.