Feature Engineering для Machine Learning: техніки та питання на співбесідах 2026

Повний огляд технік feature engineering для машинного навчання: кодування категорій, масштабування ознак, відбір фіч, побудова pipeline та питання для співбесід у 2026 році.

Feature Engineering для Machine Learning: техніки кодування, масштабування та відбору ознак у 2026 році

Feature engineering залишається одним із найвпливовіших етапів у побудові моделей машинного навчання. Якість ознак визначає верхню межу точності моделі, і жодна кількість гіперпараметричного тюнінгу не компенсує погано підготовлені дані. Ця стаття охоплює ключові техніки feature engineering, які активно використовуються у продакшн-системах у 2026 році, та розглядає типові питання з цієї теми на технічних співбесідах.

Чому feature engineering — це критично

За оцінками практиків, до 80% часу у проєктах data science витрачається на підготовку та трансформацію даних. Грамотний feature engineering може підвищити точність моделі на 10-30% без зміни алгоритму. На співбесідах ця тема перевіряє глибину розуміння даних, а не лише знання API бібліотек.

Стратегії кодування категоріальних ознак для ML-моделей

Більшість алгоритмів машинного навчання працюють виключно з числовими даними. Категоріальні ознаки потребують перетворення у числовий формат, але спосіб кодування безпосередньо впливає на якість моделі. Ординальні змінні (де є природний порядок) та номінальні змінні (без порядку) вимагають різних підходів.

Label Encoding присвоює кожній категорії унікальне ціле число. Цей метод підходить для ординальних ознак, де порядок має значення (наприклад, small < medium < large). Для номінальних ознак використовується One-Hot Encoding, який створює окремий бінарний стовпець для кожної категорії, уникаючи хибних числових залежностей.

python
# encoding_strategies.py
import pandas as pd
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
from sklearn.compose import ColumnTransformer

df = pd.DataFrame({
    "size": ["small", "medium", "large", "medium", "small"],
    "color": ["red", "blue", "green", "red", "blue"],
    "price": [10, 25, 40, 22, 12]
})

# Label encoding for ordinal feature (size has natural order)
le = LabelEncoder()
df["size_encoded"] = le.fit_transform(df["size"])  # large=0, medium=1, small=2

# One-hot encoding for nominal feature (color has no order)
ct = ColumnTransformer(
    transformers=[
        ("onehot", OneHotEncoder(drop="first", sparse_output=False), ["color"])
    ],
    remainder="passthrough"  # Keep other columns unchanged
)
result = ct.fit_transform(df[["color", "price"]])
# Produces: color_green, color_red columns (blue dropped as reference)

Параметр drop="first" у OneHotEncoder видаляє одну категорію як референтну, що запобігає мультиколінеарності у лінійних моделях. Для дерев рішень та градієнтного бустингу цей параметр можна пропустити.

Масштабування ознак: StandardScaler, MinMaxScaler та RobustScaler

Масштабування ознак вирівнює діапазони числових змінних, щоб ознаки з великими абсолютними значеннями не домінували під час навчання. Це особливо критично для алгоритмів, чутливих до масштабу: лінійна регресія, SVM, k-NN та нейронні мережі.

Три основні скейлери scikit-learn відрізняються поведінкою у присутності викидів. StandardScaler центрує дані навколо нуля з одиничним стандартним відхиленням, але чутливий до екстремальних значень. MinMaxScaler стискає дані до діапазону [0, 1], що робить його ще вразливішим до викидів. RobustScaler використовує медіану та міжквартильний розмах (IQR), що забезпечує стійкість до аномальних значень.

python
# feature_scaling.py
import numpy as np
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler

# Simulated dataset: salary with outliers
data = np.array([[35000], [42000], [55000], [67000], [450000]])  # 450k is an outlier

# StandardScaler: mean=0, std=1 (sensitive to outliers)
standard = StandardScaler().fit_transform(data)
# Result: [-0.72, -0.68, -0.60, -0.53, 2.53] — outlier distorts the scale

# MinMaxScaler: maps to [0, 1] (very sensitive to outliers)
minmax = MinMaxScaler().fit_transform(data)
# Result: [0.0, 0.017, 0.048, 0.077, 1.0] — most values crushed near zero

# RobustScaler: uses median and IQR (robust to outliers)
robust = RobustScaler().fit_transform(data)
# Result: [-0.77, -0.5, 0.0, 0.46, 15.38] — outlier isolated, core data preserved

Практичне правило: якщо датасет містить значні викиди, варто починати з RobustScaler. Для даних з нормальним розподілом StandardScaler є стандартним вибором, а MinMaxScaler підходить для задач, де потрібен фіксований діапазон (наприклад, вхідні дані нейронних мереж).

Математичні трансформації для асиметричних розподілів

Реальні дані часто мають правосторонню асиметрію (right-skewed distribution), особливо фінансові показники: доходи, суми транзакцій, ціни нерухомості. Така асиметрія порушує припущення нормальності, на якому базуються багато алгоритмів, і може суттєво знизити якість моделі.

Логарифмічна трансформація — найпростіший спосіб зменшити асиметрію. Функція np.log1p (log(1+x)) безпечно обробляє нульові значення. Для складніших випадків існують параметричні трансформації: Box-Cox автоматично знаходить оптимальний степеневий параметр lambda, але вимагає строго додатних значень. Yeo-Johnson є розширенням Box-Cox, яке працює також із нульовими та від'ємними значеннями.

python
# skew_transformations.py
import numpy as np
from sklearn.preprocessing import PowerTransformer

# Right-skewed income data (common in real datasets)
income = np.array([[25000], [32000], [41000], [55000], [72000],
                   [150000], [320000], [890000]])

# Log transform: simple, effective for right-skewed data
log_income = np.log1p(income)  # log1p handles zero values safely

# Box-Cox: finds optimal power parameter automatically
pt = PowerTransformer(method="box-cox")  # Requires strictly positive values
income_boxcox = pt.fit_transform(income)
print(f"Optimal lambda: {pt.lambdas_[0]:.3f}")  # Shows learned parameter

# Yeo-Johnson: works with zero and negative values too
pt_yj = PowerTransformer(method="yeo-johnson")
income_yj = pt_yj.fit_transform(income)

На співбесідах часто запитують про різницю між Box-Cox і Yeo-Johnson. Ключова відмінність: Box-Cox потребує x > 0, тоді як Yeo-Johnson обробляє будь-які значення. У продакшн-коді PowerTransformer автоматично стандартизує вихідні дані (mean=0, std=1), що часто усуває потребу в окремому масштабуванні.

Відбір ознак: усунення шуму перед навчанням моделі

Надлишкові або нерелевантні ознаки збільшують час навчання, провокують перенавчання та ускладнюють інтерпретацію моделі. Існують три основні підходи до відбору ознак: фільтрувальні, обгорткові та вбудовані методи.

Фільтрувальні методи (Filter) оцінюють кожну ознаку незалежно від моделі, використовуючи статистичні тести. ANOVA F-test вимірює статистичну значущість кожної ознаки відносно цільової змінної. Обгорткові методи (Wrapper) ітеративно додають або видаляють ознаки, оцінюючи якість моделі на кожному кроці. Вбудовані методи (Embedded) виконують відбір під час навчання: L1-регуляризація (Lasso) обнуляє ваги нерелевантних ознак.

python
# feature_selection.py
from sklearn.datasets import make_classification
from sklearn.feature_selection import (
    SelectKBest, f_classif,  # Filter method
    SequentialFeatureSelector,  # Wrapper method
    SelectFromModel  # Embedded method
)
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LassoCV

# Dataset: 20 features, only 5 are informative
X, y = make_classification(
    n_samples=1000, n_features=20,
    n_informative=5, n_redundant=3, random_state=42
)

# Filter: ANOVA F-test selects top k features by statistical significance
selector_filter = SelectKBest(f_classif, k=8)
X_filtered = selector_filter.fit_transform(X, y)
print(f"Selected features: {selector_filter.get_support(indices=True)}")

# Embedded: L1 regularization (Lasso) zeros out irrelevant feature weights
lasso = LassoCV(cv=5, random_state=42).fit(X, y)
selector_embedded = SelectFromModel(lasso, prefit=True)
X_lasso = selector_embedded.transform(X)
print(f"Lasso kept {X_lasso.shape[1]} features out of {X.shape[1]}")

На практиці рекомендується комбінувати підходи: спочатку застосувати фільтрувальний метод для швидкого зменшення розмірності, потім використати вбудований метод для фінального відбору. Це забезпечує баланс між швидкістю та якістю відбору.

Готовий до співбесід з Data Science & ML?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Побудова продакшн-ready пайплайнів із ColumnTransformer

У реальних проєктах дані містять числові та категоріальні ознаки, пропущені значення та різні масштаби. Побудова єдиного пайплайну через Pipeline та ColumnTransformer у scikit-learn вирішує кілька проблем одночасно: запобігає витоку даних (data leakage) між тренувальною та тестовою вибірками, автоматизує всі кроки препроцесингу та забезпечує відтворюваність.

Column Transformer дозволяє застосовувати різні трансформації до різних груп стовпців: медіанну імпутацію та стандартизацію для числових ознак, заповнення модою та one-hot encoding для категоріальних. Весь ланцюг перетворень разом із моделлю зберігається як єдиний об'єкт, який можна серіалізувати та розгорнути у продакшн.

python
# production_pipeline.py
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import cross_val_score

# Define column groups by type
numeric_features = ["age", "income", "credit_score"]
categorical_features = ["education", "employment_type", "region"]

# Numeric pipeline: impute missing values, then scale
numeric_pipeline = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),  # Median resists outliers
    ("scaler", StandardScaler())
])

# Categorical pipeline: impute missing, then one-hot encode
categorical_pipeline = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("encoder", OneHotEncoder(handle_unknown="ignore", sparse_output=False))
])

# Combine into a single preprocessor
preprocessor = ColumnTransformer([
    ("num", numeric_pipeline, numeric_features),
    ("cat", categorical_pipeline, categorical_features)
])

# Full pipeline: preprocessing + model in one object
full_pipeline = Pipeline([
    ("preprocessor", preprocessor),
    ("classifier", GradientBoostingClassifier(n_estimators=200, random_state=42))
])

# Cross-validation applies preprocessing correctly within each fold
scores = cross_val_score(full_pipeline, X, y, cv=5, scoring="roc_auc")
print(f"AUC: {scores.mean():.3f} +/- {scores.std():.3f}")

Критичний момент: препроцесинг має відбуватися всередині кожного фолду крос-валідації. Якщо масштабування виконати до розбиття на фолди, інформація з тестової вибірки потрапить у тренувальну, що призведе до оптимістично завищених метрик. Конструкція Pipeline + cross_val_score автоматично гарантує коректний порядок.

Автоматизований feature engineering із Featuretools

Ручне створення ознак із реляційних таблиць є трудомістким процесом. Бібліотека Featuretools автоматизує цей етап через метод Deep Feature Synthesis (DFS). DFS створює нові ознаки шляхом агрегації та трансформації даних уздовж зв'язків між таблицями.

Для роботи з Featuretools необхідно створити EntitySet — опис реляційної структури даних із визначеними зв'язками між таблицями. Далі DFS генерує ознаки автоматично: підраховує кількість транзакцій, обчислює середні суми, знаходить максимальні значення та комбінує трансформації різної глибини.

python
# automated_feature_engineering.py
import featuretools as ft
import pandas as pd

# Define entities from relational tables
customers = pd.DataFrame({
    "customer_id": [1, 2, 3],
    "signup_date": pd.to_datetime(["2024-01-15", "2024-03-22", "2024-06-01"]),
    "region": ["US", "EU", "APAC"]
})

transactions = pd.DataFrame({
    "txn_id": range(1, 8),
    "customer_id": [1, 1, 1, 2, 2, 3, 3],
    "amount": [50, 120, 30, 200, 85, 340, 15],
    "category": ["food", "tech", "food", "tech", "travel", "food", "tech"]
})

# Create an EntitySet with relationships
es = ft.EntitySet(id="retail")
es = es.add_dataframe(dataframe=customers, dataframe_name="customers",
                      index="customer_id", time_index="signup_date")
es = es.add_dataframe(dataframe=transactions, dataframe_name="transactions",
                      index="txn_id")
es = es.add_relationship("customers", "customer_id",
                         "transactions", "customer_id")

# DFS generates features: COUNT, MEAN, MAX, STD of transactions per customer
feature_matrix, feature_defs = ft.dfs(
    entityset=es, target_dataframe_name="customers",
    max_depth=2,  # Controls complexity of generated features
    trans_primitives=["month", "weekday"],  # Transformation primitives
    agg_primitives=["count", "mean", "std", "max"]  # Aggregation primitives
)
print(f"Generated {len(feature_defs)} features from 2 tables")

Параметр max_depth контролює глибину комбінацій: значення 2 означає, що DFS може застосувати агрегацію поверх трансформації (наприклад, MEAN(MONTH(signup_date))). Збільшення глибини експоненційно збільшує кількість ознак, тому після генерації обов'язково слід застосувати відбір ознак для видалення шуму.

Типові питання з feature engineering на технічних співбесідах

Нижче наведено питання, які часто ставлять на технічних співбесідах для позицій Data Scientist та ML Engineer у 2026 році.

Яка різниця між Label Encoding та One-Hot Encoding? Коли застосовувати кожен?

Label Encoding присвоює кожній категорії ціле число та підходить для ординальних ознак, де порядок має значення (рівень освіти, розмір). One-Hot Encoding створює окремий бінарний стовпець для кожної категорії та використовується для номінальних ознак без природного порядку (колір, місто). Застосування Label Encoding до номінальних ознак може навчити модель хибних числових залежностей.

Що таке data leakage і як feature engineering може його спричинити?

Data leakage — це ситуація, коли інформація з тестової або майбутньої вибірки потрапляє у тренувальні дані. У контексті feature engineering це відбувається, коли масштабування або імпутацію виконують до розбиття на train/test. Рішення: використовувати Pipeline, де всі трансформації застосовуються виключно до тренувальних даних у кожному фолді крос-валідації.

Як обрати між StandardScaler, MinMaxScaler та RobustScaler?

StandardScaler підходить для даних, наближених до нормального розподілу. MinMaxScaler використовується, коли потрібен фіксований діапазон [0, 1], наприклад для нейронних мереж. RobustScaler є оптимальним вибором при наявності значних викидів, оскільки базується на медіані та IQR замість середнього та стандартного відхилення.

Коли варто використовувати Box-Cox замість простого логарифмування?

Логарифмічна трансформація є окремим випадком Box-Cox (при lambda=0). Box-Cox автоматично підбирає оптимальний параметр lambda, що може дати кращий результат для розподілів, де логарифм не забезпечує достатньої нормалізації. Для даних з нульовими або від'ємними значеннями слід використовувати Yeo-Johnson.

Як працює Deep Feature Synthesis і коли його доцільно застосовувати?

DFS автоматично генерує ознаки з реляційних даних, застосовуючи агрегаційні (COUNT, MEAN, MAX) та трансформаційні (MONTH, WEEKDAY) примітиви вздовж зв'язків між таблицями. Його доцільно застосовувати при роботі з декількома пов'язаними таблицями, де ручне створення ознак зайняло б значний час. Після генерації необхідно виконати відбір ознак для видалення нерелевантних.

Назвіть три методи відбору ознак та поясніть різницю між ними.

Фільтрувальні методи (Filter) оцінюють ознаки незалежно від моделі через статистичні тести та є найшвидшими. Обгорткові методи (Wrapper) оцінюють підмножини ознак за якістю конкретної моделі та є найточнішими, але повільними. Вбудовані методи (Embedded) виконують відбір під час навчання через регуляризацію (L1/Lasso) та забезпечують баланс між швидкістю та якістю.

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Висновок

Feature engineering — це фундамент побудови ефективних ML-моделей, і глибоке розуміння цього етапу відрізняє junior-фахівця від senior data scientist. Основні висновки:

  • Обирати метод кодування категоріальних ознак залежно від їхнього типу: Label Encoding для ординальних, One-Hot Encoding для номінальних змінних
  • Використовувати RobustScaler як перший вибір при наявності викидів у даних, StandardScaler для чистих нормальних розподілів
  • Застосовувати PowerTransformer (Box-Cox або Yeo-Johnson) для зменшення асиметрії розподілів перед навчанням лінійних моделей
  • Комбінувати фільтрувальні та вбудовані методи відбору ознак для ефективного зменшення розмірності
  • Завжди виконувати препроцесинг усередині Pipeline для запобігання data leakage між фолдами крос-валідації
  • Розглядати Featuretools для автоматизації feature engineering з реляційних даних, але обов'язково застосовувати відбір ознак після генерації

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Теги

#feature engineering
#machine learning
#data science
#python
#scikit-learn

Поділитися

Пов'язані статті