init files

This commit is contained in:
dan
2025-12-12 20:19:59 +03:00
parent b78fad6cce
commit d1d016b2ed
13 changed files with 126491 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.idea
.venv
__pycache__

118190
dataset/ds.csv Normal file

File diff suppressed because it is too large Load Diff

53
dataset/fullinfo.md Normal file
View File

@@ -0,0 +1,53 @@
Коммуникации в Городе
Город в Т-Банке — это группа сервисов, которые помогают пользователям решать
ежедневные задачи: купить билеты в кино, заказать продукты, путешествовать.
В банке есть множество видов коммуникаций с клиентами. Первое разделение —
на внутренние и внешние. Внешние демонстрируются за пределами банка: например,
реклама в интернете, на телевидении и т. п. Внутренние — в приложении банка:
например, пуши, баннеры и т. д. Причем внутренние делятся еще на активные
и пассивные каналы. К активным каналам относятся пуши, СМС, имейлы, чаты;
к пассивным — баннеры, сторис и фулскрины.
В текущем датасете вам дано число коммуникаций в разбивке на активные и пассивные
и то, как клиенты реагировали на них: открыли, совершили покупку или просто получили.
Учтите, что клиент мог сделать заказ не только день в день коммуникации, но и позже.
Это в большей степени касается тяжелых сервисов, например путешествий.
Описание переменных
id уникальный идентификатор клиента
business_dt Дата
active_imp_ent Показы активных коммуникаций (Развлечения)
active_click_ent Касания активных коммуникаций (Развлечения)
active_imp_super Показы активных коммуникаций (Супермаркеты)
active_click_super Касания активных коммуникаций (Супермаркеты)
active_imp_transport Показы активных коммуникаций (Транспорт)
active_click_transport Касания активных коммуникаций (Транспорт)
active_imp_shopping Показы активных коммуникаций (Шопинг)
active_click_shopping Касания активных коммуникаций (Шопинг)
active_imp_hotel Показы активных коммуникаций (Отели)
active_click_hotel Касания активных коммуникаций (Отели)
active_imp_avia Показы активных коммуникаций (Авиабилеты)
active_click_avia Касания активных коммуникаций (Авиабилеты)
passive_imp_ent Показы пассивных коммуникаций (Развлечения)
passive_click_ent Касания пассивных коммуникаций (Развлечения)
passive_imp_super Показы пассивных коммуникаций (Супермаркеты)
passive_click_super Касания пассивных коммуникаций (Супермаркеты)
passive_imp_transport Показы пассивных коммуникаций (Транспорт)
passive_click_transport
Касания пассивных коммуникаций (Транспорт)
passive_imp_shopping Показы пассивных коммуникаций (Шопинг)
passive_click_
shopping
Касания пассивных коммуникаций (Шопинг)
passive_imp_hotel Показы пассивных коммуникаций (Отели)
passive_click_hotel Касания пассивных коммуникаций (Отели)
passive_imp_avia Показы пассивных коммуникаций (Авиабилеты)
passive_click_avia Касания пассивных коммуникаций (Авиабилеты)
orders_amt_ent Число заказов (Развлечения)
orders_amt_super Число заказов (Супермаркеты)
orders_amt_transport Число заказов (Транспорт)
orders_amt_shopping Число заказов (Шопинг)
orders_amt_hotel Число заказов (Отели)
orders_amt_avia Число заказов (Авиабилеты)
gender_cd Пол (M/F)
age Возраст в годах
device_platform_cd Обратная связь клиента

4
dataset/shortinfo.md Normal file
View File

@@ -0,0 +1,4 @@
Т-Банк: коммуникации в городе
Датасет содержит данные о коммуникациях с клиентами внутри экосистемы Город Т-Банка, включая активные и пассивные каналы и реакцию пользователей на них. Для каждого клиента по дням указано количество показов, кликов и совершенных заказов по разным категориям сервисов: развлечения, транспорт, шопинг, отели, супермаркеты и авиабилеты. Клиент мог совершить покупку как в день коммуникации, так и позже, что особенно важно для сложных сервисов вроде путешествий. Дополнительно доступны демографические признаки клиентов и информация об устройстве.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,55 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "3d3a9c98",
"metadata": {},
"source": [
"# EDA «Коммуникации в Городе»\n",
"\n",
"## Кратко о данных\n",
"- 118189 строк, 8339 клиентов, 35 исходных столбцов + инженерные (totals, CTR/CR, флаги).\n",
"- Диапазон дат: 20250109 — 20251104 (284 дня).\n",
"- Категории сервисов: ent, super, transport, shopping, hotel, avia; активные и пассивные показы/клики, заказы по категориям.\n",
"- Дубликаты по ключу (id, business_dt): нет.\n",
"\n",
"## Качество данных\n",
"- Пропуски: несущественные (NaN почти нет), отрицательных значений не обнаружено.\n",
"- Возраст: 1580 лет, p1/p99 = 22/68, мусора (<14 или >100) нет.\n",
"- Гендер: 68.5% M, 31.5% F. Платформа после нормализации: ~52.5% iOS, ~46.7% Android, 1.1% iPadOS.\n",
"- Признаки «заспамленности» и агрегаты на клиента добавлены: imp/click/order totals, CTR/CR, contact_days, avg_impressions_per_contact_day, order_categories_count.\n",
"\n",
"## Каналы и эффективность (агрегировано по всем строкам)\n",
"- Active impressions ≈ 219.5k, passive impressions ≈ 473.1k.\n",
"- Active clicks ≈ 147.3k (CTR_active ≈ 0.67), passive clicks ≈ 18.1k (CTR_passive ≈ 0.038).\n",
"- Заказы всего: 12439; CR click→order ≈ 7.5%, CR imp→order ≈ 1.8%.\n",
"- Дневных точек: 284; daily агрегаты подготовлены (CTR/CR, day_of_week).\n",
"\n",
"## Демография и устройство vs эффективность (по клиентским агрегатам)\n",
"- Таблицы по полу/возрастным группам/платформам готовы в `04_clients_segmentation.ipynb` (средние impressions/clicks/orders и CTR/CR).\n",
"- Гипотезы: в 05-м ноутбуке добавлены примеры MannWhitney по CTR active vs passive и по полу; можно расширять на платформы и возраст.\n",
"\n",
"## Лаги и сезонность\n",
"- Дневные ряды и метрики CTR/CR по времени и по дням недели — см. `03_time_and_lags.ipynb`.\n",
"- Лаги: реализованы кросс-корреляции orders vs impressions/clicks (hotel, avia) для lag 07; по клиентам — first_imp/click/order и распределения дней до заказа.\n",
"\n",
"## Сегменты и «усталость»\n",
"- Сегменты каналов: only_active / only_passive / both + метрики; бины по числу категорий заказов.\n",
"- «Заспамленность»: bin по avg_impressions_per_contact_day с CTR/CR; stacked доли категорий заказов по возрасту.\n",
"\n",
"## Модели как часть EDA\n",
"- На клиентском уровне собран датасет для задачи `has_any_order`; pipeline с OHE + StandardScaler + LogisticRegression и RandomForest (ROC-AUC и важности).\n",
"- Выводы по коэффициентам/важности доступны в `05_exploratory_models.ipynb`.\n",
"\n",
"## Что делать дальше\n",
"- Прогнать все ноутбуки end-to-end (данные готовы, зависимости в `.venv`): `jupyter lab` или `jupyter nbconvert --execute`.\n",
"- Уточнить нормализацию категорий (при необходимости) и при желании сохранить `dataset/ds_clean.parquet` (флаг в `01_load_and_clean.ipynb`).\n",
"- Добавить/актуализировать бизнес-гипотезы (категории, платформы, возраст) и зафиксировать p-value в таблице гипотез.\n",
"- При необходимости усилить визуализацию: календарные heatmap по CTR, ECDF лагов по каждой категории, PDP для топ-фичей модели.\n"
]
}
],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 5
}

41
preanalysis/eda_report.md Normal file
View File

@@ -0,0 +1,41 @@
# EDA «Коммуникации в Городе»
## Кратко о данных
- 118189 строк, 8339 клиентов, 35 исходных столбцов + инженерные (totals, CTR/CR, флаги).
- Диапазон дат: 20250109 — 20251104 (284 дня).
- Категории сервисов: ent, super, transport, shopping, hotel, avia; активные и пассивные показы/клики, заказы по категориям.
- Дубликаты по ключу (id, business_dt): нет.
## Качество данных
- Пропуски: несущественные (NaN почти нет), отрицательных значений не обнаружено.
- Возраст: 1580 лет, p1/p99 = 22/68, мусора (<14 или >100) нет.
- Гендер: 68.5% M, 31.5% F. Платформа после нормализации: ~52.5% iOS, ~46.7% Android, 1.1% iPadOS.
- Признаки «заспамленности» и агрегаты на клиента добавлены: imp/click/order totals, CTR/CR, contact_days, avg_impressions_per_contact_day, order_categories_count.
## Каналы и эффективность (агрегировано по всем строкам)
- Active impressions ≈ 219.5k, passive impressions ≈ 473.1k.
- Active clicks ≈ 147.3k (CTR_active ≈ 0.67), passive clicks ≈ 18.1k (CTR_passive ≈ 0.038).
- Заказы всего: 12439; CR click→order ≈ 7.5%, CR imp→order ≈ 1.8%.
- Дневных точек: 284; daily агрегаты подготовлены (CTR/CR, day_of_week).
## Демография и устройство vs эффективность (по клиентским агрегатам)
- Таблицы по полу/возрастным группам/платформам готовы в `04_clients_segmentation.ipynb` (средние impressions/clicks/orders и CTR/CR).
- Гипотезы: в 05-м ноутбуке добавлены примеры MannWhitney по CTR active vs passive и по полу; можно расширять на платформы и возраст.
## Лаги и сезонность
- Дневные ряды и метрики CTR/CR по времени и по дням недели — см. `03_time_and_lags.ipynb`.
- Лаги: реализованы кросс-корреляции orders vs impressions/clicks (hotel, avia) для lag 07; по клиентам — first_imp/click/order и распределения дней до заказа.
## Сегменты и «усталость»
- Сегменты каналов: only_active / only_passive / both + метрики; бины по числу категорий заказов.
- «Заспамленность»: bin по avg_impressions_per_contact_day с CTR/CR; stacked доли категорий заказов по возрасту.
## Модели как часть EDA
- На клиентском уровне собран датасет для задачи `has_any_order`; pipeline с OHE + StandardScaler + LogisticRegression и RandomForest (ROC-AUC и важности).
- Выводы по коэффициентам/важности доступны в `05_exploratory_models.ipynb`.
## Что делать дальше
- Прогнать все ноутбуки end-to-end (данные готовы, зависимости в `.venv`): `jupyter lab` или `jupyter nbconvert --execute`.
- Уточнить нормализацию категорий (при необходимости) и при желании сохранить `dataset/ds_clean.parquet` (флаг в `01_load_and_clean.ipynb`).
- Добавить/актуализировать бизнес-гипотезы (категории, платформы, возраст) и зафиксировать p-value в таблице гипотез.
- При необходимости усилить визуализацию: календарные heatmap по CTR, ECDF лагов по каждой категории, PDP для топ-фичей модели.

154
preanalysis/eda_utils.py Normal file
View File

@@ -0,0 +1,154 @@
from __future__ import annotations
from pathlib import Path
from typing import Dict, Iterable, List
import numpy as np
import pandas as pd
# Paths and column groups
DATA_PATH = Path("dataset/ds.csv")
CATEGORIES: List[str] = ["ent", "super", "transport", "shopping", "hotel", "avia"]
ACTIVE_IMP_COLS = [f"active_imp_{c}" for c in CATEGORIES]
PASSIVE_IMP_COLS = [f"passive_imp_{c}" for c in CATEGORIES]
ACTIVE_CLICK_COLS = [f"active_click_{c}" for c in CATEGORIES]
PASSIVE_CLICK_COLS = [f"passive_click_{c}" for c in CATEGORIES]
ORDER_COLS = [f"orders_amt_{c}" for c in CATEGORIES]
NUMERIC_COLS = (
ACTIVE_IMP_COLS
+ PASSIVE_IMP_COLS
+ ACTIVE_CLICK_COLS
+ PASSIVE_CLICK_COLS
+ ORDER_COLS
+ ["age"]
)
CAT_COLS = ["gender_cd", "device_platform_cd"]
def safe_divide(numerator: pd.Series | float, denominator: pd.Series | float) -> pd.Series:
"""Divide with protection against zero (works for Series and scalars)."""
if isinstance(denominator, pd.Series):
denom = denominator.replace(0, np.nan)
else:
denom = np.nan if float(denominator) == 0 else denominator
return numerator / denom
def normalize_gender(series: pd.Series) -> pd.Series:
cleaned = series.fillna("UNKNOWN").astype(str).str.strip().str.upper()
mapping = {"M": "M", "MALE": "M", "F": "F", "FEMALE": "F"}
return cleaned.map(mapping).fillna("UNKNOWN")
def normalize_device(series: pd.Series) -> pd.Series:
cleaned = series.fillna("unknown").astype(str).str.strip()
lowered = cleaned.str.lower().str.replace(" ", "").str.replace("_", "")
mapping = {"android": "Android", "ios": "iOS", "ipados": "iPadOS", "ipad": "iPadOS"}
mapped = lowered.map(mapping)
fallback = cleaned.str.title()
return mapped.fillna(fallback)
def add_age_group(df: pd.DataFrame) -> pd.DataFrame:
bins = [0, 25, 35, 45, 55, np.inf]
labels = ["<25", "25-34", "35-44", "45-54", "55+"]
df["age_group"] = pd.cut(df["age"], bins=bins, labels=labels, right=False)
return df
def add_totals(df: pd.DataFrame) -> pd.DataFrame:
df["active_imp_total"] = df[ACTIVE_IMP_COLS].sum(axis=1)
df["passive_imp_total"] = df[PASSIVE_IMP_COLS].sum(axis=1)
df["active_click_total"] = df[ACTIVE_CLICK_COLS].sum(axis=1)
df["passive_click_total"] = df[PASSIVE_CLICK_COLS].sum(axis=1)
df["orders_amt_total"] = df[ORDER_COLS].sum(axis=1)
df["click_total"] = df["active_click_total"] + df["passive_click_total"]
df["imp_total"] = df["active_imp_total"] + df["passive_imp_total"]
df["active_ctr"] = safe_divide(df["active_click_total"], df["active_imp_total"])
df["passive_ctr"] = safe_divide(df["passive_click_total"], df["passive_imp_total"])
df["ctr_all"] = safe_divide(df["click_total"], df["imp_total"])
df["cr_click2order"] = safe_divide(df["orders_amt_total"], df["click_total"])
df["cr_imp2order"] = safe_divide(df["orders_amt_total"], df["imp_total"])
return df
def add_flags(df: pd.DataFrame) -> pd.DataFrame:
df["has_active_comm"] = (df[ACTIVE_IMP_COLS + ACTIVE_CLICK_COLS].sum(axis=1) > 0).astype(int)
df["has_passive_comm"] = (df[PASSIVE_IMP_COLS + PASSIVE_CLICK_COLS].sum(axis=1) > 0).astype(int)
df["has_any_order"] = (df[ORDER_COLS].sum(axis=1) > 0).astype(int)
df["order_categories_count"] = (df[ORDER_COLS] > 0).sum(axis=1)
return df
def load_data(path: Path | str = DATA_PATH) -> pd.DataFrame:
df = pd.read_csv(path)
df["business_dt"] = pd.to_datetime(df["business_dt"])
df["gender_cd"] = normalize_gender(df["gender_cd"])
df["device_platform_cd"] = normalize_device(df["device_platform_cd"])
df = add_age_group(df)
df = add_totals(df)
df = add_flags(df)
return df
def describe_zero_share(df: pd.DataFrame, cols: Iterable[str]) -> pd.DataFrame:
stats = []
for col in cols:
series = df[col]
stats.append(
{
"col": col,
"count": series.count(),
"mean": series.mean(),
"median": series.median(),
"std": series.std(),
"min": series.min(),
"q25": series.quantile(0.25),
"q75": series.quantile(0.75),
"max": series.max(),
"share_zero": (series == 0).mean(),
"p95": series.quantile(0.95),
"p99": series.quantile(0.99),
}
)
return pd.DataFrame(stats)
def build_daily(df: pd.DataFrame) -> pd.DataFrame:
agg_cols = ACTIVE_IMP_COLS + PASSIVE_IMP_COLS + ACTIVE_CLICK_COLS + PASSIVE_CLICK_COLS + ORDER_COLS
daily = df.groupby("business_dt")[agg_cols].sum().reset_index()
daily = add_totals(daily)
daily["day_of_week"] = daily["business_dt"].dt.day_name()
return daily
def build_client(df: pd.DataFrame) -> pd.DataFrame:
agg_spec: Dict[str, str] = {col: "sum" for col in ACTIVE_IMP_COLS + PASSIVE_IMP_COLS + ACTIVE_CLICK_COLS + PASSIVE_CLICK_COLS + ORDER_COLS}
meta_spec: Dict[str, str | callable] = {
"age": "median",
"gender_cd": lambda s: s.mode().iat[0] if not s.mode().empty else "UNKNOWN",
"age_group": lambda s: s.mode().iat[0] if not s.mode().empty else np.nan,
"device_platform_cd": lambda s: s.mode().iat[0] if not s.mode().empty else "Other",
}
agg_spec.update(meta_spec)
client = df.groupby("id").agg(agg_spec).reset_index()
contact_days = df.groupby("id")["business_dt"].nunique().rename("contact_days")
imp_day = df.copy()
imp_day["imp_day_total"] = imp_day[ACTIVE_IMP_COLS + PASSIVE_IMP_COLS].sum(axis=1)
max_imp_day = imp_day.groupby("id")["imp_day_total"].max().rename("max_impressions_per_day")
client = add_totals(client)
client = add_flags(client)
client = client.merge(contact_days, on="id", how="left")
client = client.merge(max_imp_day, on="id", how="left")
client = add_contact_density(client)
return client
def add_contact_density(df: pd.DataFrame) -> pd.DataFrame:
# contact_days must already be present
if "contact_days" in df.columns:
df["avg_impressions_per_contact_day"] = safe_divide(df["imp_total"], df["contact_days"])
return df
return df

368
preanalysis/task.md Normal file
View File

@@ -0,0 +1,368 @@
# План полноценного преданализа датасета «Коммуникации в Городе»
Основано на описании датасета: есть ежедневные коммуникации с клиентами в экосистеме «Город Т-Банка», активные/пассивные каналы, показы/клики и заказы по категориям (ent, super, transport, shopping, hotel, avia), а также демография и устройство.
Обозначения:
- `*_imp_*` — показы (impressions) активных/пассивных каналов по категориям (`ent`, `super`, `transport`, `shopping`, `hotel`, `avia`).
- `*_click_*` — клики/касания по тем же категориям.
- `orders_amt_*` — число заказов по категориям.
- `gender_cd`, `age`, `device_platform_cd` — демография и устройство.
---
## 0. Технический скелет проекта
Файлы/ноутбуки:
1. `01_load_and_clean.ipynb` — загрузка, чистка, базовые описания.
2. `02_univariate_bivariate.ipynb` — распределения и связи признаков.
3. `03_time_and_lags.ipynb` — время, лаги, сезонность.
4. `04_clients_segmentation.ipynb` — агрегаты по клиенту, сегменты.
5. `05_exploratory_models.ipynb` — простые модели как часть EDA.
6. `eda_report.md` / `eda_report.ipynb` — итоговый отчёт.
---
## 1. Загрузка и структура данных
### Таблицы/выводы
1. `df.info()` — список столбцов, типы, количество ненулевых.
2. `df.head(5)` — первые строки для визуальной проверки.
3. Размерность:
- `n_rows`, `n_cols`
- `n_unique_clients = df['id'].nunique()`
- диапазон дат: `min(business_dt)`, `max(business_dt)`
4. Проверка ключа:
- таблица: `df.groupby(['id', 'business_dt']).size().value_counts().head()`
(показывает, есть ли дубликаты по ключу)
5. Среднее число дней на клиента:
- `df.groupby('id').size().describe()`
### Графики
1. Количество записей по датам:
- `bar/line`: X = `business_dt`, Y = `count(*)`
- цель: увидеть провалы/пики выгрузки
---
## 2. Качество данных и аномалии
### Таблицы/метрики
1. Пропуски:
- таблица: колонка → количество пропусков → доля пропусков
2. Базовый `describe` по числовым:
- `df[num_cols].describe().T`
3. Доля нулей:
- таблица: колонка → доля нулей → min/max → 95-й, 99-й перцентили
4. Логические проверки:
- все `*_imp_*`, `*_click_*`, `orders_amt_*` должны быть `>= 0`
- поиск отрицательных/странных значений
5. Возраст:
- мин/макс, перцентили (1-й, 99-й), доля мусора (например, `<14` или `>100`)
6. Категориальные:
- уникальные значения `gender_cd`, `device_platform_cd`
- приведение к единому формату (trim, upper, `unknown`)
### Графики
1. Boxplot возраста:
- Y = `age`
- цель: выбросы и мусор
2. Barplot пропусков:
- X = столбец, Y = доля NaN (только где NaN > 0)
---
## 3. Одномерный анализ (univariate)
### 3.1. Числовые признаки (показы/клики/заказы)
#### Таблицы
1. Для каждой группы (`active_imp_*`, `passive_imp_*`, `active_click_*`, `passive_click_*`, `orders_amt_*`):
- `count, mean, median, std, min, q25, q75, max, share_zero, p95, p99`
2. Агрегаты по всем категориям:
- создать `active_imp_total`, `passive_imp_total`, `active_click_total`, `passive_click_total`, `orders_amt_total`
- таблица `describe()` для них
#### Графики
1. Гистограммы (лог-масштаб или `log1p`) для каждой категории и типа:
- `active_imp_ent`, `active_click_ent`, `passive_imp_ent`, `orders_amt_ent`, …
2. Boxplot для агрегатов:
- `active_imp_total`, `passive_imp_total`, `active_click_total`, `passive_click_total`, `orders_amt_total`
### 3.2. Категориальные признаки
#### Таблицы
1. Распределение `gender_cd`: counts, доли, `unknown`
2. Распределение `device_platform_cd`: counts, доли
3. Возрастные группы:
- `<25`, `2534`, `3544`, `4554`, `55+`
- таблица: группа → число клиентов → доля
#### Графики
1. Barplot пола: X = `M/F/Unknown`, Y = доля
2. Barplot платформ: X = platform, Y = доля
3. Гистограмма возраста
---
## 4. Время и сезонность
Создать дневные агрегаты:
- сумма показов/кликов/заказов по дням
- метрики:
- `CTR_active = active_click_total / active_imp_total`
- `CTR_passive = passive_click_total / passive_imp_total`
- `CR_click2order = orders_amt_total / (active_click_total + passive_click_total)`
- `CR_imp2order = orders_amt_total / (active_imp_total + passive_imp_total)`
- день недели: `day_of_week`
### Таблицы
1. `daily.describe()` по дневным агрегатам
2. Таблица по дням недели:
- `day_of_week` → среднее `impressions, clicks, orders, CTR, CR`
### Графики
1. Линейные временные ряды:
- `business_dt` vs total impressions
- `business_dt` vs total clicks
- `business_dt` vs total orders
2. Линии CTR/CR во времени (rolling mean 7 дней по желанию):
- `active_ctr`, `passive_ctr`, `cr_click2order`
3. Сезонность по дням недели:
- barplot для `active_ctr`, `passive_ctr`, `cr_click2order`
4. (Опционально) календарная heatmap заказов/CTR
---
## 5. Парные связи (bivariate)
### Таблицы
1. Корреляции Спирмена (на уровне клиента/дня):
- между всеми числовыми признаками + `age`
2. Для каждой категории:
- биннинг показов по квантилям → средний `imp, click, CTR, orders, CR`
### Графики
1. Scatter/hexbin «показы → клики»:
- `active_imp_*` vs `active_click_*`
- `passive_imp_*` vs `passive_click_*`
2. Scatter «клики → заказы»:
- `*_click_*` vs `orders_amt_*`
3. CTR по бинам показов (линия/бар)
4. CR по бинам кликов (линия/бар)
5. Heatmap корреляций
---
## 6. Демография и устройство vs эффективность
Агрегировать по клиенту:
- суммы показов/кликов/заказов
- CTR/CR на уровне клиента
- добавить `gender_cd`, `age_group`, `device_platform_cd`
### Таблицы
1. По полу:
- средние `impressions, clicks, orders, CTR, CR`
2. По возрастным группам:
- те же метрики
3. По платформам:
- те же метрики
4. Тесты гипотез (MannWhitney / t-test):
- разница CTR/CR между группами
### Графики
1. Barplot CTR/CR по полу
2. Barplot CTR/CR по возрастным группам
3. Barplot CTR/CR по платформам
4. Boxplot заказов по возрастным группам
5. Stacked bar: возраст → доли категорий заказов (наполнение корзины сервисами)
---
## 7. Поведение по клиенту и сегментация
### 7.1. Простые сегменты
Флаги на уровне клиента:
- `has_active_comm`, `has_passive_comm`
- `has_any_order`
- `order_categories_count` (в скольких категориях есть заказ)
#### Таблицы
1. Сегменты каналов:
- `only_active`, `only_passive`, `both`
- доля клиентов, средние заказы, CTR/CR
2. Сегменты мультикатегорийности:
- `1`, `2`, `3+` категорий заказов
- средние коммуникации/заказы, демография
#### Графики
1. Barplot по сегментам каналов:
- средние заказы, CTR/CR
2. Barplot по числу категорий заказов
3. Stacked bar: сегменты → пол/возраст (по желанию)
### 7.2. Кластеризация (расширенный EDA)
1. Вектор фичей:
- суммы по категориям + CTR/CR + доли заказов
2. Нормализация
3. KMeans / GMM, 37 кластеров
#### Таблицы
- кластер → размер → средние фичи → краткая интерпретация
#### Графики
1. Профили кластеров (bar/radar)
2. Scatter PCA/UMAP: цвет = кластер
---
## 8. Воронка: показы → клики → заказы
### Таблицы
1. Общая воронка:
- `channel_type`, `category`, `impressions`, `clicks`, `orders`, `CTR`, `CR_click2order`, `CR_imp2order`
2. Воронка по сегментам:
- пол/возраст/платформа → те же метрики
### Графики
1. Funnel chart active vs passive (общий)
2. Barplot CTR по категориям + сравнение active/passive
3. Barplot CR по категориям + сравнение active/passive
4. Funnel/Bar по возрастным группам
---
## 9. Временные лаги между коммуникациями и заказами
С учётом «поздних покупок» (особенно travel).
### 9.1. Лаги на дневном уровне
#### Таблицы
1. Лаговые признаки `lag1..lag7` для показов/кликов
2. Кросс-корреляция:
- lag → corr(orders*t, impressions*{t-lag})
#### Графики
1. Линия «lag vs корреляция» по:
- `hotel`, `avia` (и др. при желании)
- active vs passive
### 9.2. Лаги на клиентском уровне
#### Таблицы
1. `first_imp_date`, `first_click_date`, `first_order_date`
2. `days_to_order`
3. Квантили `days_to_order` по категориям
#### Графики
1. Гистограмма/ECDF `days_to_order` по категориям
---
## 10. Мультиканальность и «заспамленность»
### Таблицы
1. `contact_days`, `avg_impressions_per_contact_day`, `max_impressions_per_day`
2. Бины по `avg_impressions_per_contact_day` → средний CTR/CR
### Графики
1. Гистограмма `avg_impressions_per_contact_day`
2. Линия/бар: CTR/CR vs уровень спама
---
## 11. Простые модели как часть EDA
### 11.1. Бинарная модель «есть заказ / нет заказа»
Target:
- `has_any_order`
Features:
- суммы показов/кликов по типам и категориям
- CTR/CR
- демография и платформа
#### Таблицы
1. Логистическая регрессия:
- коэффы, p-value, odds ratio
2. Feature importance из дерева/лесов
#### Графики
1. Barplot важностей
2. (Опционально) partial dependence для 23 ключевых фичей
---
## 12. Гипотезы и статтесты
### Примеры гипотез
1. `CTR_active > CTR_passive`
2. CR различается между категориями сервисов
3. CTR/CR различаются по полу/возрасту/платформе
4. «заспамленность» снижает CTR/CR после порога
### Таблица гипотез
- гипотеза, H0/H1, тест, p-value, вывод, бизнес-интерпретация
Графики для поддержки — использовать из разделов 410 (барчики/боксплоты).
---
## 13. Итоговая документация
1. Резюме выводов:
- качество данных
- эффективность каналов/категорий
- сегменты, где коммуникации лучше/хуже работают
- лаги (как быстро покупают после контактов)
- признаки «усталости» от коммуникаций
2. Список проблем данных и принятых решений по чистке
3. Список инсайтов для бизнеса
4. Список фичей для будущих моделей
5. Следующие шаги:
- подготовка ML-пайплайна
- список A/B-гипотез
- какие данные добрать (если нужно)
---