обработка данных
June 5, 2023

Функциональный трансформер, как создавать и когда использовать

Функциональная трансформация поможет быстро создать преобразование колонок для пайплайна без сохранения состояния (создание других описывал здесь). Эта возможность реализована в классе FunctionTransformer из модуля sklearn.preprocessing. Например, это могут быть преобразования для создания цикличных признаков (из месяца, часа как здесь) или для извлечения слов из текста, его длины или других характеристик. Создадим демонстрационный набор из температур по месяцам и сделаем из месяца цикличный признак с помощью функционального трансформера:

import pandas as pd
import numpy as np
np.random.seed(5)

temp_avg_d = {1:0, 2:-2, 3:15, 4:20, 5:25, 6:28, 7:30, 8:27, 9:20, 10:12, 11:7, 12: 2}
mon_ar  = np.tile(np.arange(1, 13), 3)

ts = pd.DataFrame({'mon':mon_ar})
ts['t'] = ts['mon'].map(lambda x:temp_avg_d[x] + np.random.normal(loc=0, scale=2))
ts.head()

func


Основным аргументом необходимым классу FunctionTransformer является пользовательская функция:

from sklearn.preprocessing import FunctionTransformer

def sin_transformer(period):
    return FunctionTransformer(lambda x: np.sin(2 * np.pi* x / period ))

def cos_transformer(period):
    return FunctionTransformer(lambda x: np.cos(2 * np.pi * x / period ))

Теперь с помощью ColumnTransformer проверим работу трансформеров:

from sklearn.compose import ColumnTransformer

ct = ColumnTransformer(transformers=[('sin', sin_transformer(12), ['mon']), 
                      ('cos', cos_transformer(12), ['mon'])], remainder='passthrough')

ct.fit_transform(ts)[:5]

inverse_func


В FunctionTransformer можно передавать функцию обратного преобразования. В нашем случае оно неоднозначно (arcsin), но в демонстрационных целях приведем пример:

sin_tr = FunctionTransformer(lambda x: np.sin(2 * np.pi* x / 12), 
                             inverse_func=lambda x: np.arcsin(x)*12/(2*np.pi))

sin_tr.fit_transform(np.array([[1, 3, 6]])), sin_tr.inverse_transform(np.array([[0.5, 1, 0]]))

check_inverse


Даже вылетает предупреждение о том, что переданные функции не являются строго обратными. Сообщение можно приглушить с параметром check_inverse=False:

sin_tr = FunctionTransformer(lambda x: np.sin(2 * np.pi* x / 12), check_inverse=False,
                             inverse_func=lambda x: np.arcsin(x)*12/(2*np.pi))

sin_tr.fit_transform(np.array([[1, 3, 6]])), sin_tr.inverse_transform(np.array([[0.5, 1, 0]]))

feature_names_out


Данный аргумент определяет то, как будут называться колонки после преобразования, например, если попытаться извлечь без использования параметра, получим ошибку:

sin_tr.fit(ts[['mon']]).get_feature_names_out()

feature_names_out='one-to-one' позволит сохранить имена после преобразований:

sin_tr = FunctionTransformer(lambda x: np.sin(2 * np.pi* x / 12),
                             feature_names_out='one-to-one')

sin_tr.fit(ts[['mon']]).get_feature_names_out()

Можно изменить именование, если передать в параметр feature_names_out функцию, которая получает в качестве параметров ссылку на объект FunctionTransformer и старые имена, а возвращает новые имена:

sin_tr = FunctionTransformer(lambda x: np.sin(2 * np.pi* x / 12),
                             feature_names_out=lambda x, names: [f'tr_{it}' for it in names])


sin_tr.fit(ts[['mon']]).get_feature_names_out()

kw_args, inv_kw_args


Эти параметры позволяют передать словарь аргументов в функции func и inverse_func. Переделаем cos_transformer, определенный выше:

ct = ColumnTransformer(transformers=[('cos', 
                      FunctionTransformer(lambda x, period: np.cos(2 * np.pi* x / period ), 
                                                          kw_args={'period':12}), ['mon'])]
                       , remainder='passthrough')

ct.fit_transform(ts)[:5]