Data Science на автопилоте: 10 промтов, которые превращают Jupyter Notebook в конвейер от сырых данных до обученной модели

Data Science на автопилоте: 10 промтов, которые превращают Jupyter Notebook в конвейер от сырых данных до обученной модели

Большинство Data Science проектов умирает не из-за плохих алгоритмов, а из-за рутины. По оценкам, которые приводит Anaconda в отчёте «State of Data Science», дата-сайентисты тратят на очистку и подготовку данных больше времени, чем на моделирование — и это устойчивая тенденция уже несколько лет подряд. Pandas-цепочки, pd.merge с несовпадающими ключами, ValueError: could not convert string to float, забытый train_test_split(stratify=y), переобученный RandomForest с accuracy 0.99 на утечке данных — вот реальный рабочий день.

LLM здесь работают не как «заменитель аналитика», а как ускоритель рутины. Они отлично справляются с шаблонным кодом (boilerplate), переводом формулировки задачи в вызов sklearn, интерпретацией sklearn.metrics.classification_report и генерацией визуализаций. Там, где нужно понимать домен, ставить гипотезы и защищать результаты перед бизнесом — решает человек.

Ниже — 10 промтов, которые я реально использую в работе. Каждый — с описанием, примером и работающим кодом. Все библиотеки настоящие: pandas 2.x, scikit-learn 1.4+, matplotlib 3.8, seaborn 0.13, XGBoost 2.x, SHAP 0.45+.

1. EDA по одному описанию датасета

Задача: быстро получить первичную разведку без ручного перебора df.describe(), df.info() и .isna().

Промт:

Ты senior data scientist. Вот структура датасета: колонки и их типы (вставь вывод df.dtypes). Сгенерируй Python-код для EDA: пропуски, распределения числовых признаков, cardinality категориальных, корреляционная матрица (только числовые, метод Spearman), поиск константных и почти константных колонок. Используй pandas 2.x и seaborn. Выведи отчёт в виде словаря с ключами: missing, distributions, correlations, constant_cols.

Пример применения:

import pandas as pd, seaborn as sns, numpy as np

def quick_eda(df: pd.DataFrame) -> dict:
    report = {}
    report["missing"] = df.isna().mean().sort_values(ascending=False)
    num = df.select_dtypes(include=np.number)
    report["correlations"] = num.corr(method="spearman")
    nunique = df.nunique()
    report["constant_cols"] = nunique[nunique <= 1].index.tolist()
    report["near_constant"] = [
        c for c in df.columns
        if df[c].value_counts(normalize=True, dropna=False).iloc[0] > 0.99
    ]
    return report

Почему работает: LLM знает, что spearman устойчивее к выбросам и нелинейностям, чем pearson, и не забудет про near-constant признаки — типичную причину бесполезных фичей в моделях.

2. Умная обработка пропусков с обоснованием стратегии

Задача: не заливать всё средним «на автомате», а выбирать стратегию по типу признака и механизму пропусков.

Промт:

Для каждой колонки датасета предложи стратегию обработки пропусков: удаление, mean/median, mode, KNN-imputer или индикатор пропуска. Обоснуй выбор типом признака и долей пропусков. Учти разницу между MCAR, MAR и MNAR. Выдай код с sklearn SimpleImputer и KNNImputer, и отдельно — колонки, которые стоит просто дропнуть.

Пример:

from sklearn.impute import SimpleImputer, KNNImputer
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer

num_cols = ["age", "income", "tenure"]
cat_cols = ["city", "plan"]

num_pipe = Pipeline([("imp", SimpleImputer(strategy="median", add_indicator=True))])
cat_pipe = Pipeline([("imp", SimpleImputer(strategy="most_frequent"))])

pre = ColumnTransformer([
    ("num", num_pipe, num_cols),
    ("cat", cat_pipe, cat_cols),
])

add_indicator=True создаёт бинарную колонку «был ли пропуск» — это ловит MNAR-паттерн, когда сам факт отсутствия значения информативен (например, клиент не указал доход, потому что он нулевой).

3. Feature engineering из сырых колонок

Задача: из signup_date и last_login получить признаки, которые реально влияют на отток.

Промт:

У меня есть колонки created_at, last_activity_at, amount, category. Сгенерируй feature engineering: recency (дней с последней активности), tenure (дней с регистрации), частотные признаки по category, агрегаты amount за 7/30/90 дней. Избегай target leakage: не используй данные после даты предсказания. Верни функцию, принимающую DataFrame и возвращающую DataFrame с новыми фичами.

Пример:

def build_features(df: pd.DataFrame, ref_date: pd.Timestamp) -> pd.DataFrame:
    out = df.copy()
    out["tenure_days"] = (ref_date - out["created_at"]).dt.days
    out["recency_days"] = (ref_date - out["last_activity_at"]).dt.days
    for w in (7, 30, 90):
        mask = (ref_date - out["created_at"]).dt.days <= w
        out[f"amount_{w}d"] = out["amount"].where(mask, 0)
    return out

Ключевой момент: параметр ref_date вместо pd.Timestamp.now() — это защита от data leakage. При обучении ref_date берётся из train-периода, при инференсе — из момента предсказания.

4. Baseline-модель за один промт

Задача: получить честный baseline до того, как вы начнёте тюнить гиперпараметры.

Промт:

Задача — бинарная классификация. Целевая колонка: churn. Сгенерируй baseline-пайплайн: train_test_split со stratify и random_state=42, ColumnTransformer (StandardScaler для числовых, OneHotEncoder(handle_unknown="ignore") для категориальных), две модели — LogisticRegression и RandomForestClassifier. Выведи classification_report, confusion_matrix и ROC-AUC. Сравни и скажи, какая модель — baseline.

Пример:

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, roc_auc_score

X_tr, X_te, y_tr, y_te = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

for name, clf in [
    ("logreg", LogisticRegression(max_iter=1000, class_weight="balanced")),
    ("rf", RandomForestClassifier(n_estimators=400, n_jobs=-1, random_state=42)),
]:
    pipe = Pipeline([("pre", pre), ("clf", clf)])
    pipe.fit(X_tr, y_tr)
    proba = pipe.predict_proba(X_te)[:, 1]
    print(name, "ROC-AUC:", round(roc_auc_score(y_te, proba), 4))
    print(classification_report(y_te, pipe.predict(X_te)))

class_weight="balanced" важен, если классы несбалансированы — иначе accuracy будет вводить в заблуждение, а модель просто выучит majority class.

5. Диагностика переобучения и утечек

Задача: понять, почему на train метрика 0.99, а на test — 0.72.

Промт:

У меня ROC-AUC на train = 0.99, на test = 0.72. Дай чек-лист диагностики: 1) признаки с утечкой (корреляция с таргетом > 0.9), 2) временная утечка (фичи, вычисленные после target), 3) дубликаты строк между train и test, 4) слишком сложная модель. Выдай код, который проверяет каждый пункт.

Пример проверки утечечной корреляции:

corrs = X_tr.select_dtypes(include="number").corrwith(y_tr).abs().sort_values(ascending=False)
print(corrs.head(10))

# поиск дублей между train и test по ключевым фичам
key_cols = ["user_id", "created_at"]
leak = set(map(tuple, X_tr[key_cols].values)) & set(map(tuple, X_te[key_cols].values))
print("overlap rows:", len(leak))

На практике чаще всего виновата именно временная утечка: фича вида total_purchases содержит покупку, которая случилась после оттока. Лечится пересчётом фичей на дату среза.

6. Кросс-валидация и подбор гиперпараметров

Промт:

Подбери гиперпараметры для XGBoost с помощью Optuna. Задача — бинарная классификация с дисбалансом 1:20. Используй StratifiedKFold(n_splits=5, shuffle=True, random_state=42), оптимизируй ROC-AUC, 50 trials. Верни код с study.optimize и выводом best_params.

Пример:

import optuna
from sklearn.model_selection import StratifiedKFold, cross_val_score
from xgboost import XGBClassifier

def objective(trial):
    params = {
        "n_estimators": trial.suggest_int("n_estimators", 200, 800),
        "max_depth": trial.suggest_int("max_depth", 3, 10),
        "learning_rate": trial.suggest_float("learning_rate", 0.01, 0.3, log=True),
        "subsample": trial.suggest_float("subsample", 0.6, 1.0),
        "scale_pos_weight": 20,
        "eval_metric": "auc",
        "random_state": 42,
    }
    clf = XGBClassifier(**params)
    cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
    return cross_val_score(clf, X_tr, y_tr, cv=cv, scoring="roc_auc", n_jobs=-1).mean()

study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=50)
print(study.best_params)

Optuna использует TPE (Tree-structured Parzen Estimator) — байесовскую оптимизацию, которая сходится заметно быстрее grid search. Документация: optuna.readthedocs.io.

7. Интерпретация модели через SHAP

Задача: объяснить бизнесу, почему модель пометила клиента как уходящего.

Промт:

Построй SHAP-объяснение для обученной XGBoost-модели. Нужны: summary_plot (beeswarm), bar-plot важности, force_plot для одного конкретного клиента с index=42. Используй shap.TreeExplainer. Поясни, как читать каждый график.

Пример:

import shap

explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_te)

shap.summary_plot(shap_values, X_te, plot_type="dot")
shap.plots.waterfall(shap.Explanation(
    values=shap_values[42],
    base_values=explainer.expected_value,
    data=X_te.iloc[42],
    feature_names=X_te.columns.tolist()
))

SHAP-значения аддитивны: вклад каждой фичи складывается с базовым значением (средним предсказанием). Красные точки — высокие значения фичи, синие — низкие. Сдвиг вправо = повышает вероятность оттока. Оригинальная статья: Lundberg & Lee, «A Unified Approach to Interpreting Model Predictions», NeurIPS 2017.

8. Визуализация: графики, которые не стыдно показать

Промт:

Сгенерируй matplotlib/seaborn-код для отчёта: 1) ROC-кривая с AUC в легенде, 2) precision-recall curve, 3) калибровочная кривая (reliability diagram), 4) матрица ошибок с нормализацией. Стиль — минималистичный, читаемые подписи, единая палитра. Сохрани в figsize (12, 8) через subplots.

Пример:

import matplotlib.pyplot as plt
from sklearn.metrics import RocCurveDisplay, PrecisionRecallDisplay, ConfusionMatrixDisplay

fig, axes = plt.subplots(2, 2, figsize=(12, 8))
RocCurveDisplay.from_predictions(y_te, proba, ax=axes[0, 0])
PrecisionRecallDisplay.from_predictions(y_te, proba, ax=axes[0, 1])
ConfusionMatrixDisplay.from_predictions(y_te, pred, normalize="true", ax=axes[1, 0])

# калибровка
from sklearn.calibration import CalibrationDisplay
CalibrationDisplay.from_predictions(y_te, proba, n_bins=10, ax=axes[1, 1])

plt.tight_layout()
plt.savefig("report.png", dpi=150)

Калибровочная кривая — недооценённый график. Если модель выдаёт probability 0.8, а фактическая доля положительных в этом бине — 0.5, то калибровка сломана, и порог для бизнеса надо подбирать иначе.

9. Ревью ноутбука как код-ревью

Промт:

Проведи ревью моего ноутбука как senior DS. Найди: 1) data leakage, 2) фитинг препроцессоров на всём датасете вместо train, 3) отсутствие random_state, 4) магические числа в коде, 5) неинтерпретируемые названия переменных. Для каждого пункта — конкретная строка и как исправить.

Типичная находка:

# ПЛОХО: scaler обучен на всём X — утечка
scaler = StandardScaler().fit(X)
X_scaled = scaler.transform(X)

# ХОРОШО: scaler строго внутри пайплайна
pipe = Pipeline([("pre", pre), ("clf", clf)])
pipe.fit(X_tr, y_tr)  # препроцессинг учится только на train

Это самая частая ошибка в student-ноутбуках: fit на полном датасете до train_test_split. Метрика завышается, а в продакшене модель разваливается.

10. Сохранение модели и воспроизводимость

Промт:

Сгенерируй код для сохранения обученного пайплайна через joblib, логирования метрик и параметров в JSON, фиксации версий библиотек. Добавь проверку входных данных при инференсе (набор и типы колонок).

Пример:

import joblib, json, sklearn

joblib.dump(pipe, "model_v1.joblib")

meta = {
    "sklearn": sklearn.__version__,
    "features": X_tr.columns.tolist(),
    "target": "churn",
    "roc_auc_test": float(roc_auc_score(y_te, pipe.predict_proba(X_te)[:, 1])),
}
with open("model_v1_meta.json", "w") as f:
    json.dump(meta, f, indent=2)

def predict(df: pd.DataFrame):
    missing = set(meta["features"]) - set(df.columns)
    if missing:
        raise ValueError(f"Missing features: {missing}")
    return pipe.predict_proba(df[meta["features"]])[:, 1]

Что не стоит делегировать модели

LLM уверенно генерируют код, но плохо понимают контекст вашего бизнеса. Не отдавайте на откуп модели:

Задача Кому доверить
Выбор метрики (ROC-AUC vs PR-AUC vs бизнес-метрика) Человек + согласование со стейкхолдером
Определение порога для продакшена Человек (зависит от стоимости FP/FN)
Проверка причинно-следственных связей Человек (LLM путает корреляцию с причинностью)
Написание boilerplate, пайплайнов, графиков LLM
Чтение метрик, интерпретация SHAP LLM + проверка человеком

Отдельно про галлюцинации в коде: LLM иногда предлагает несуществующие параметры sklearn (например, class_weight там, где его нет, или устаревшие sklearn.cross_validation вместо sklearn.model_selection). Всегда проверяйте сигнатуры по официальной документации scikit-learn и запускайте код на маленьком сэмпле перед полным прогоном.

Итог

Промты не заменяют понимание статистики и домена — они снимают рутину между «понял задачу» и «получил baseline». Десять промтов выше покрывают 80% типового цикла: EDA, очистка, фичи, baseline, диагностика утечек, тюнинг, интерпретация, визуализация, ревью, деплой. Освоив их, вы будете тратить на механический код часы вместо дней, а время высвободится на то, что действительно требует экспертизы: постановку гипотез, дизайн эксперимента и разговор с бизнесом о цене ошибки.

Попробуйте начать с промтов №1 и №4 прямо сейчас — они дают самый быстрый эффект на любом новом датасете. А промты №5 и №9 держите под рукой: они спасают от публикации модели, которая красиво выглядит в ноутбуке и разваливается в продакшене.

Полезные источники:
- scikit-learn User Guide: https://scikit-learn.org/stable/user_guide.html
- pandas documentation: https://pandas.pydata.org/docs/
- XGBoost docs: https://xgboost.readthedocs.io/
- Optuna: https://optuna.readthedocs.io/
- SHAP: https://shap.readthedocs.io/
- Lundberg & Lee, NeurIPS 2017: https://papers.nips.cc/paper/2017/hash/8a20a8621978632d76c43dfd28b67767-Abstract.html
- Anaconda State of Data Science: https://www.anaconda.com/state-of-data-science

← All posts

Comments