Feature Names в Scikit-learn: как не потерять названия колонок
Управление именами признаков в Scikit Learn
Имена признаков (feature names) играют ключевую роль при интерпретации моделей, отладке пайплайнов и визуализации. В Scikit Learn начиная с версии 0.23 появилась единая система работы с названиями колонок через метод get_feature_names_out(). Рекомендуется передавать данные в виде pandas DataFrame, чтобы библиотека автоматически сохраняла имена.
Основной подход: обучение модели или трансформера на DataFrame, после чего доступны атрибуты feature_names_in_ (названия исходных признаков) и метод get_feature_names_out() (названия после преобразования).
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
df = pd.DataFrame({
'age': [25, 30, 35],
'income': [50000, 60000, 70000],
'city': ['Moscow', 'SPb', 'Kazan']
})
y = [0, 1, 0]
# Модель, сохраняющая имена
rf = RandomForestClassifier()
rf.fit(df, y)
print(rf.feature_names_in_) # ['age', 'income', 'city']
# Трансформер с get_feature_names_out
scaler = StandardScaler()
X_scaled = scaler.fit_transform(df[['age','income']]) # потеря имён
# Получаем имена
print(scaler.get_feature_names_out(['age','income'])) # ['age', 'income']библиотеки для машинного обучения python (библиотеки для машинного обучения в python (scikit-learn, tensorflow, pytorch))
Если применить к DataFrame, метод возвращает те же имена. В случае кодирования категорий (OneHotEncoder) имена создаются автоматически.
Как получить имена признаков после ColumnTransformer?
Часто используется ColumnTransformer для разных типов колонок. После вызова fit() доступен get_feature_names_out(), возвращающий результирующие имена.
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
ct = ColumnTransformer([
('numeric', StandardScaler(), ['age','income']),
('categ', OneHotEncoder(), ['city'])
])
X_trans = ct.fit_transform(df)
print(ct.get_feature_names_out())
# ['numeric__age', 'numeric__income', 'categ__Moscow', 'categ__SPb', 'categ__Kazan']Feature names python (имена признаков в python)
Типичная ошибка: использование старых версий sklearn (<0.23), где get_feature_names_out() отсутствует. Решение: обновить библиотеку или вручную задавать имена через список.
Как задать имена признаков вручную?
Когда данные не являются DataFrame (numpy массив), имена можно передать через параметр feature_names_out у некоторых трансформеров (например, FunctionTransformer) или после преобразования задать список вручную.
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
X = np.array([[1, 2], [3, 4]])
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(X)
# По умолчанию имена генерируются как 'x0', 'x1', 'x0^2', 'x0 x1', 'x1^2'
names = poly.get_feature_names_out(['alpha', 'beta'])
print(names) # ['alpha', 'beta', 'alpha^2', 'alpha beta', 'beta^2']
искусственный интеллект на языке python (искусственный интеллект на python)
Как сохранить имена признаков в Pipeline?
При использовании Pipeline имена можно получить после fit(), если все шаги поддерживают get_feature_names_out(). Результат будет комбинированным.
from sklearn.pipeline import Pipeline
pipe = Pipeline([
('scaler', StandardScaler()),
('poly', PolynomialFeatures(degree=2, include_bias=False))
])
pipe.fit(df[['age','income']])
print(pipe.get_feature_names_out()) # ['age', 'income', 'age^2', 'age income', 'income^2']Расширенные примеры работы с именами признаков
Пример 1: GridSearchCV с отслеживанием имён
При поиске гиперпараметров признаки могут меняться. Использование DataFrame гарантирует, что имена сохранятся внутри модели.
from sklearn.model_selection import GridSearchCV
from sklearn.tree import DecisionTreeClassifier
param_grid = {'max_depth': [3,5]}
gs = GridSearchCV(DecisionTreeClassifier(), param_grid, cv=2)
gs.fit(df, y)
best_model = gs.best_estimator_
print(best_model.feature_names_in_)['age', 'income', 'city']
Пример 2: Восстановление имён после PCA
PCA теряет исходные имена, так как создаёт новые компоненты. Однако можно задать свои имена.
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
principal_components = pca.fit_transform(df[['age','income','city_encoded']])
# Создаём имена вручную
feature_names = [f'PC{i+1}' for i in range(principal_components.shape[1])]
print(feature_names) # ['PC1', 'PC2']['PC1', 'PC2']
Пример 3: Функция трансформации с сохранением имён
FunctionTransformer позволяет задать пользовательскую функцию и указать feature_names_out.
from sklearn.preprocessing import FunctionTransformer
def log_transform(X):
return np.log1p(X)
transformer = FunctionTransformer(
func=log_transform,
feature_names_out=lambda self, names: [f'log_{n}' for n in names]
)
X_log = transformer.fit_transform(df[['age','income']])
print(transformer.get_feature_names_out(['age','income']))['log_age', 'log_income']
Пример 4: Обработка категорий с OneHotEncoder
После кодирования имена содержат имя признака и значение категории.
ohe = OneHotEncoder(sparse_output=False, feature_name_combiner='concat')
encoded = ohe.fit_transform(df[['city']])
print(ohe.get_feature_names_out(['city']))['city_Moscow', 'city_SPb', 'city_Kazan']
Пример 5: Проблема с Pipeline и пайплайном без get_feature_names_out
Если один из шагов не поддерживает этот метод (например, старый трансформер), возникает ошибка. Решение: обернуть такой шаг в FunctionTransformer с явным указанием имён или использовать FeatureUnion.
from sklearn.pipeline import FeatureUnion
# Предположим, у нас есть кастомный трансформер без get_feature_names_out
# class OldTransformer:
# def fit(self, X, y=None): return self
# def transform(self, X): return X * 2
# Решение: обёртка
class NamedTransformer:
def __init__(self, names):
self.names = names
def fit(self, X, y=None):
return self
def transform(self, X):
return X * 2
def get_feature_names_out(self, input_features=None):
return self.names
union = FeatureUnion([
('old', NamedTransformer(['a','b'])),
('scaler', StandardScaler())
])
X_tr = union.fit_transform(df[['age','income']])
print(union.get_feature_names_out()) # ['old__a', 'old__b', 'scaler__age', 'scaler__income']