В тази публикация ще обсъдим как да създаваме класове, за да избираме конкретни колони/функции, да добавяме нови колони/функции и да ги комбинираме заедно, за да изградим чист конвейер с помощта на Python и Sklearn. В по-късна статия ще продължа темата за тръбопровода с примери за код, за да покажа как да използватеColumnTransformer и FeatureUnion.

Без допълнително обожание, нека се заровим в него.

0. Подгответе данните

Първо, нека подготвим данните. Тук ще използвам малка извадка от данни от скорошния ми проект за прогнозиране на класиране в състезание по бягане, за да създам малък набор от данни за илюстрация. X_train тук има само 4 колони/функции, включително пол, възраст, разстояние и единица за разстояние на участника. Целевата променлива y_train е получената продължителност на състезанието (в минути).

# get the X,y
X = train_df.drop('time', axis=1)
y = train_df['time']

# Split the data into training and test datasets
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size=0.5,random_state=0)
print(X_train.shape, y_train.shape)
X_train.sample(3)

1. Изберете персонализирани колони

С готовите данни, нека създадем прост клас ColumnSelector, за да изберете по избор подмножество от колони от даден набор от данни. Предимството е, че след като настроим класа, той може директно да се използва като стъпка в конвейер. Ще го видим по-късно в тази статия.

# first, create a custom column selector to select specific columns 
from sklearn.base import BaseEstimator, TransformerMixin

class ColumnSelector(BaseEstimator, TransformerMixin):
    '''select specific columns of a given dataset'''
    def __init__(self, subset):
        self.subset = subset
        
    def fit(self, X, y=None):
        return self
    
    def transform(self, X, y=None):
        return X.loc[:, self.subset]

customized_cols = ['distance','unit']
    
## test the ColumnSelector class
selected_cols = ColumnSelector(customized_cols)
selected_cols.transform(X_train).head()

Чудесно, успешно избрахме колоните/функциите, които ни интересуват, и сме готови да извършим други трансформации на намалените данни.

Но изчакайте, какво ще стане, ако имаме нужда от различни трансформации за различни типове данни? Например еднократно кодиране за колоната с единици и мащабиране за колоната за разстояние? Едно решение е да се създадат два класа за разграничаване на типа данни.

2. Изберете всички цифрови и всички категориални колони

След това нека създадем клас NumColSelector за избиране на всички цифрови колони и друг клас за избиране на всички категориални колони в данните за обучение.

# second, create a numeric-column selector and a categorical-column selector
class NumColSelector(BaseEstimator, TransformerMixin):
    '''select all numeric columns of a given dataset'''        
    def fit(self, X, y=None):
        return self
    
    def transform(self, X, y=None):
        return X.select_dtypes(include='number')

class CatColSelector(BaseEstimator, TransformerMixin):
    '''select all categorical columns of a given dataset'''        
    def fit(self, X, y=None):
        return self
    
    def transform(self, X, y=None):
        return X.select_dtypes(include='object')
    
    
## test the NumericColSelector class
NumColSelector().transform(X_train).head()
# CatColSelector().transform(X_train).head()

хубаво. Можем лесно да разделяме категорични и числови колони с помощта на тези два класа и да правим различни трансформации на данни върху тях по-късно.

Какво ще стане, ако искаме да включим нова колона/функция, която не е била в първоначалните данни за обучение? Например, искаме да създадем нова функция, която да опише средната възраст на всяка група от разстояние, което може да бъде полезно за прогнозиране на целевото променливо време.

3. Добавяне на нова колона

Сега нека създадем друг клас AgeMedianByDistGroup, за да добавим тази нова колона/функция.

# create a class to add a new feature AgeMedianByDistGroup
class AgeMedianByDistGroup(BaseEstimator, TransformerMixin):
    '''get the median age of each distance group''' 
    def __init__(self, train):
        self.age_median_by_dist_group = train.groupby('distance').apply(lambda x: x['age'].median())
        self.age_median_by_dist_group.name = 'age_median_by_dist_group'
        
    def fit(self, X=None, y=None):
        return self
    
    def transform(self, X, y=None):
        new_X = pd.merge(X, self.age_median_by_dist_group, 
                         left_on = 'distance', right_index=True, how='left')        
        X['age_median_by_dist_group'] = new_X['age_median_by_dist_group']
        return X

Както виждаме, средната възраст за бягане на 5 км е 36, а средната възраст за 8 км е 30.

4. Настройте окончателния тръбопровода

И накрая, нека изградим pipeline, като свържем всички заедно, използвайки класовете, които създадохме преди това. По принцип ще извършим следните стъпки към данните за обучение:

  • Стъпка 1: Добавете нова колона/функция с AgeMedianByDistGroup
  • Стъпка 2: Изберете всички цифрови колони/функции с NumColSelector
  • Стъпка 3: Фиксирайте всички стойности на nan към медианата с SimpleImputer
  • Стъпка 4: Нормализиране на стойностите до [0,1] диапазон с MinMaxScaler
# build the final pipeline
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import MinMaxScaler

pipe = Pipeline([
                ('add_new_col', AgeMedianByDistGroup(X_train)),
                ('get_num_cols', NumColSelector()),
                ('fix_nan', SimpleImputer(missing_values=np.nan, strategy='median')),
                ('scale_data', MinMaxScaler())
])

pipe.fit(X_train)

Крайният тръбопровод изглежда ясен, нали? Едно от най-големите предимства на използването на конвейери е, че автоматизира процеса и поддържа нашия код чист и организиран.

Чрез тази поредица от стъпки наборът от тестови данни и наборът от данни за задържане също могат лесно да бъдат трансформирани с помощта на един и същ конвейер без повторно кодиране. Окончателно трансформираните данни за обучение, както е показано по-долу, са нормализиран кадър от данни, такъв без NaN стойности в избраните от нас колони/функции.

Ето! Изградихме няколко трансформатора и ги комбинирахме в елегантен конвейер! Можем да използваме подготвените данни за обучение за по-късно моделиране.

Благодаря, че прочетохте моята статия! Надяваме се, че това ръководство ще ви помогне с текущия или следващия ви проект. Следващата ми публикация ще обсъди как да използвам ColumnTransformer и FeatureUnion за трансформация на колони в конвейера.

Последвайте ме и останете на линия! 😺 🍁 ☃️