В продолжение темы препроцессинга категориальных переменных в sklearn pipeline Python.
См. также пост
Препроцессинг категориальных переменных в sklearn pipeline Python с LabelEncoder.
Т.е. предыдущий пост про технику
препроцессинга на основе LabelEncoder, а в данном посте я покажу, как работаю с
LeaveOneOutEncoder.
LeaveOneOutEncoder пожалуй самый необычный способ
препроцессинга категориальных переменных.
LeaveOneOutEncoder считается очень удобным, когда у вас много (от нескольких десятков до нескольких сотен) уровней фактора, а также когда часть уровней фактора очень немногочисленны. В моей практике самый лучший пример - уровень зарплат по регионам. У нас 89 регионов в России, причем, львиная доля приходится на Москву, меньше на Санкт-Петербург, а потом уже на остальные регионы, а на некоторые приходится совсем единицы.
Пример
У нас есть такой датасет
df= pd.DataFrame({ 'y': [10,2,3,4,5,6,7,8], 'a': [np.nan, 'b','a', 'b','a', 'b','a', 'b' ],
'c': ['a', 'b','a', 'b','a', 'b','b', 'b' ]})
Где y - целевая переменная, которую мы предсказываем, а a, c - категориальные переменные, на основе которых мы прогнозируем y.
После препроцессинга по методу
LeaveOneOutEncoder категориальные переменные принимают такой вид
0 1
0 5.625 6.0
1 5.000 5.4
2 5.000 6.0
3 5.000 5.4
4 5.000 6.0
5 5.000 5.4
6 5.000 5.4
7 5.000 5.4
Вы еще не знакомы с техникой
препроцессинга категориальных переменных по методу LeaveOneOutEncoder? После препроцессинга переменные становятся типа numeric, т.е. из категориальных превращаются в числовые.
LeaveOneOutEncoder - это среднее по каждому уровню фактора. Возьмем переменную c. Уровню переменной a соответствуют значения целевой переменной Y 10, 3 и 5. Среднее значение по ним равно (10+3+5)/3 = 6. Таким образом, в категориальной переменной с уровень a принимает значение 6.0. А если у нас уровень пропущен, т.е поле пустое, то оно принимает значение среднего по всем данным. В переменной a есть пропущенное значение (np.nan), после препроцессинга оно принимает значение 5.625. Оно получается как среднее значение по всем переменным: (10+2+3+4+5+6+7+8)/8=5.625.
Простой код препроцессинга
lb = df[['a', 'c']]
enc = LeaveOneOutEncoder()
encc = enc.fit(np.asarray(lb), df['y'])
enc_data = enc.transform(np.asarray(lb))
enc_data
Обратите внимание, в команде fit у нас участвует целевая переменная. И поэтому код для pipeline у нас будет немного отличаться от того, что мы делали, например, для LabelEncoder.
LeaveOneOutEncoder в в sklearn pipeline Python
загружаем необходимые пакеты
import pandas as pd
import numpy as np
from sklearn import preprocessing
import sklearn
from sklearn.pipeline import Pipeline
from sklearn.pipeline import FeatureUnion
from category_encoders import LeaveOneOutEncoder
from sklearn import linear_model
Toy датасет
df= pd.DataFrame({ 'y': [10,2,3,4,5,6,7,8], 'a': [np.nan, 'b','a', 'b','a', 'b','a', 'b' ],
'c': ['a', 'b','a', 'b','a', 'b','b', 'b' ], 'b': [5,5,3,4,8,6,7,3],})
Я добавил еще переменную b только для того, чтобы показать, что мы можем брать в
препроцессинг любое количество переменных. И декларируем формулу по выбору этим переменных
class MultiColumn():
def __init__(self,columns = None):
self.columns = columns # array of column names to encode
def fit(self,X,y=None):
return self
def transform(self, X):
return X[self.columns]
Теперь объект class для
препроцессинга категориальных переменных с помощью LeaveOneOutEncoder.
lb = df[['a', 'c']]
class MyLEncoder(BaseEstimator, TransformerMixin):
def transform(self, X, **fit_params):
enc = LeaveOneOutEncoder()
encc = enc.fit(np.asarray(lb), y)
enc_data = encc.transform(np.asarray(X))
return enc_data
def fit_transform(self, X,y=None, **fit_params):
self.fit(X,y, **fit_params)
return self.transform(X)
def fit(self, X, y, **fit_params):
return self
На самом деле все просто. И выходим на pipeline
Делаем predict на нашем датасете.
Создаем его и проверяем.
Работает. Буду рад критике и замечаниям.