Условие
Дан CSV dataset_ymarket_offers.csv со статистикой по магазинам и товарам в Яндекс.Маркете за месяц:
store_id, offer_id, clicks, cost, orders_number, revenue_value, orders_number_conf, revenue_value_conf, profit.
«Conf» = выкупленные заказы. Берём только позиции с clicks > 3 и revenue_value > 1000.
Задачи:
- Топ-10 прибыльных
offer_id— распределение расходов и доходов. - Посчитать ДРР (доля рекламных расходов), ROAS, какие ещё показатели добавили бы.
- Найти товары, где ДРР по размещённому заказу хороший (≤10%), а по выкупу — плохой (>10%).
- Найти товары, эффективные по соотношению прибыли и расходов.
- Найти товары, отстающие по выкупаемости от остальных.
- Предложить правило увеличения/уменьшения бюджета на эффективные/неэффективные товары.
Решение
Подход
Метрики:
- ДРР =
cost / revenue × 100%(доля рекламных расходов в доходе). - ROAS =
revenue / cost(обратная ДРР, безразмерная). - Выкупаемость =
orders_number_conf / orders_number. - CPO =
cost / orders_number(cost per order). - AOV =
revenue / orders_number(средний чек). - Margin / CAC ratio =
profit / cost.
Реализация
import pandas as pd
import numpy as np
df = pd.read_csv("dataset_ymarket_offers.csv")
# Полезная выборка
df = df[(df["clicks"] > 3) & (df["revenue_value"] > 1000)].copy()
# Метрики
df["drr_placed"] = df["cost"] / df["revenue_value"] * 100
df["drr_conf"] = df["cost"] / df["revenue_value_conf"].replace(0, np.nan) * 100
df["roas_placed"] = df["revenue_value"] / df["cost"]
df["roas_conf"] = df["revenue_value_conf"] / df["cost"]
df["redemption_rate"] = df["orders_number_conf"] / df["orders_number"].replace(0, np.nan)
df["cpo"] = df["cost"] / df["orders_number"].replace(0, np.nan)
df["margin_per_cost"] = df["profit"] / df["cost"]
# 1. Топ-10 прибыльных
top10 = (df.groupby("offer_id", as_index=False)
.agg(profit=("profit","sum"), cost=("cost","sum"),
revenue=("revenue_value_conf","sum"))
.sort_values("profit", ascending=False)
.head(10))
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10,5))
top10.plot(x="offer_id", y=["cost","revenue"], kind="bar", ax=ax)
ax.set_title("Top-10 profitable offers: cost vs revenue (confirmed)")
plt.tight_layout()
# 3. ДРР по размещённому хороший, по выкупу плохой
problem = df[(df["drr_placed"] <= 10) & (df["drr_conf"] > 10)]
print(problem[["store_id","offer_id","drr_placed","drr_conf","redemption_rate"]].head(20))
# 4. Эффективные по profit/cost
df["score"] = df["profit"] / df["cost"]
top_efficient = df.sort_values("score", ascending=False).head(20)
# 5. Низкая выкупаемость относительно остальных
median_redem = df["redemption_rate"].median()
laggards = df[df["redemption_rate"] < median_redem * 0.7]
print(laggards.shape, "товаров с низкой выкупаемостью")Бюджетная логика
Простое правило:
def budget_action(row, drr_target=10):
if row["drr_conf"] is np.nan:
return "stop" # нет выкупа
if row["drr_conf"] <= drr_target * 0.7:
return "increase"
elif row["drr_conf"] <= drr_target * 1.0:
return "keep"
elif row["drr_conf"] <= drr_target * 1.5:
return "decrease"
else:
return "stop"
df["action"] = df.apply(budget_action, axis=1)
print(df["action"].value_counts())Более «умная» формула — мультипликативный коэффициент к текущему бюджету:
Cap нужен, чтобы не дёргать бюджет в 10 раз за итерацию (волатильность).
Дополнительные метрики
- Profit per click —
profit / clicks. Защищает от ложных «эффективных», у которых случайно один большой заказ. - CR =
orders / clicks— насколько эффективен сам товар при клике. - Доля выкупа в категории — товар может казаться плохим, но это норма для категории (например, одежда — 60%, электроника — 90%).
- Доверительные интервалы на ДРР через bootstrap — бюджет надо менять с учётом неопределённости.
Подводные камни
- Деление на ноль. Если
clicks = 0илиcost = 0, ROAS уходит вinf. Заранееreplace(0, np.nan). - Маленькая выборка. Пара заказов на товар = огромная дисперсия. Условие
clicks > 3фильтрует, но 4 кликов всё равно мало для уверенного решения. Накладывайте дополнительный порогrevenue_value > Xили используйте байесовский смуш. - Сезонность. Месячная выгрузка может попасть на распродажи, новогоднюю активность. Сравнивайте товары между собой, а не с собой во времени.
- «Хороший по placed, плохой по conf» = высокая возвратность. Это структурная проблема: либо товар реально не нравится, либо самовывоз/доставка ломаются.
- Категория. Без категории-нормы ДРР, ROAS-сравнение между товарами некорректно. Минимум — нормировать на медиану по категории.
- Бюджет ≠ перформанс монотонно. При увеличении ставки CPC растёт, ROAS падает. Линейная формула «k = drr_target/drr_actual» — упрощение.
Эталонный ответ
Считаем ДРР, ROAS, выкупаемость, CPO, AOV, profit/cost. Эффективные товары — высокий profit/cost при достаточном объёме. Проблемные — drr_placed ≤ 10%, drr_conf > 10% (высокая возвратность). Бюджетное правило: k = drr_target/drr_actual с capping [0.5, 2.0] и stop при низком объёме. Учитываем категорию и неопределённость через bootstrap-CI.