Условие
Doctolib продаёт SaaS-подписку врачам за 129€/мес. На конец 2016 — 300 000 проспектов, база не пополнялась год. Data-команда сделала скор от 0 (низкий) до 100 (высокий), на основе бизнес-правил, заданных Sales Manager интуитивно по каждому критерию. Скор не обновлялся.
Sales имеет доступ к проспектам и скорам и может приоритизировать звонки.
Определения:
- Prospect — кого может звонить Sales.
- Client — проспект с подписанным контрактом (signature_date).
- Bucket — диапазон скоров.
- Meeting Rate (MR) = #Prospects with meeting / #Prospects.
- Transformation Rate (TR) = #Clients / #Prospects with meeting.
- Signature delay = days(signature - meeting_start).
В январе 2018 Head of Sales спрашивает, как идёт инициатива.
Вопросы:
- Работает ли скор? (а) Эффективность модели в предсказании конверсии. (б) Глубокий анализ по medical_specialty, age, location.
- Реально ли его используют продажники? (а) Используют ли скор? Меняется ли использование во времени? (б) Корреляция индивидуальной перформанс продажника с использованием скора. (в) Любые другие insights.
- Потенциальный impact от увеличения использования скора: методология + численная оценка + 1 слайд аргументов.
Таблицы: business_dev, prospects, scores, meetings, contract_signature_dates.
Решение
Q1. Работает ли скор?
(а) Общая эффективность.
Превратите задачу в бинарную: y = «стал клиентом за 2017». Скор — feature.
- ROC-AUC на (score, y) для проспектов, у которых был meeting (т.к. сигнал измеряется на тех, кому позвонили).
- Calibration plot: разбейте скор на бакеты (0–10, 10–20, ... 90–100), на каждом посчитайте MR и TR, и
Conversion = MR × TR. Хорошая модель → монотонный рост. Плохая — плато или инверсия в верхних бакетах. - Lift @ top decile: TR в top 10% / TR в среднем.
WITH p AS (
SELECT pr.prospect_id,
s.score,
CASE WHEN c.signature_date IS NOT NULL THEN 1 ELSE 0 END AS is_client,
CASE WHEN m.meeting_id IS NOT NULL THEN 1 ELSE 0 END AS had_meeting
FROM prospects pr
LEFT JOIN scores s USING (prospect_id)
LEFT JOIN meetings m USING (prospect_id)
LEFT JOIN contract_signature_dates c USING (prospect_id)
)
SELECT FLOOR(score/10)*10 AS bucket,
COUNT(*) AS n_prospects,
AVG(had_meeting) AS meeting_rate,
SUM(is_client)*1.0/NULLIF(SUM(had_meeting),0) AS transform_rate,
SUM(is_client)*1.0/COUNT(*) AS conv_rate
FROM p
GROUP BY 1 ORDER BY 1;(б) Глубокий анализ по dimensions.
Для каждого критерия (specialty, age, location):
- Сравните
actual conversion rate в рамках dimension valuevsсредний весовой вклад в score этого dimension. Если есть рассинхрон — баллы плохо откалиброваны. - Для категориальных (specialty, location): сделайте WoE / Information Value на (dimension_value, y) и отсортируйте — увидите, какие специальности/города реально различают.
- Возраст: разбейте на бакеты, посчитайте conversion. Если модель даёт линейные баллы по возрасту, а реально — non-monotonic (например, пик в 35–50, спад в 60+), баллы неверные.
Q2. Используется ли скор?
«Использование» косвенно: продажник, использующий скор, должен в первую очередь обзванивать высокий скор. Прокси:
- Скор у contacted vs un-contacted: средний/распределение. Если скор не используется, распределения совпадают.
- «Доля времени на топ-decile»: какая доля meetings относится к top-decile проспектов.
- Spearman corr между «прозвонил» (бинарка) и скором.
- Делать срез по неделям/месяцам — увидеть тренд.
SELECT DATE_TRUNC('month', m.meeting_date) AS mth,
AVG(s.score) AS avg_score_contacted,
(SELECT AVG(score) FROM scores) AS avg_score_pool
FROM meetings m JOIN scores s USING (prospect_id)
GROUP BY 1 ORDER BY 1;Если avg_score_contacted стабильно ≈ avg_score_pool → продажники не используют скор.
(б) Performance vs использование.
Для каждого business_dev посчитайте:
personal_score_correlation= corr между скором и тем, обзвонили ли проспекта.personal_TR= clients/meetings.
Сделайте scatter/quartile-таблицу: высокая корреляция → выше TR? Если да — это аргумент для презентации.
Q3. Impact от usage
Методология (counterfactual):
- Возьмите top-X% по скору. Если бы продажники звонили только им (или сначала им), сколько клиентов получили бы?
- Грубая оценка:
- Кол-во meetings = M (фактическое).
- Если бы те же M meetings распределились по top-X% (вместо реального распределения), TR заменилось бы средним
TR_top_Xиз бакетов. - Δ Clients = M × (TR_top_X − TR_actual).
- Δ Revenue/year = Δ Clients × 129€ × 12 × avg_lifetime.
- Контр-аргументы: ресурсы продажников ограничены, top-X% может быть быстро исчерпан, нужна модель «refresh» базы.
Слайд аргументов (1 слайд):
- Если AUC > 0.7 — скор информативен.
- Использование на момент анализа низкое (X%).
- Симуляция: рост клиентов на ~Δ%, дополнительно ARR ~Δ€.
- Дополнительные effort: нет — скор уже есть.
Подводные камни
- Selection bias: TR измеряется только на contacted. Если sales уже отбирали top-score, оценка lift искажена.
- Score not refreshed: модели полтора года, рынок меняется → calibration drift.
- Meetings ≠ contacted: проспект мог быть обзвонен, но meeting не назначен. Обзвон = более широкая воронка.
- Confounding
business_devperf и usage: senior продажники могут одновременно лучше продавать и больше использовать инструмент — корреляция ≠ каузация. - Time lag: signature_delay может быть большой → 2017-конверсия для late-2017 meetings ещё не проявилась.
Эталонный ответ
- Эффективность скора — через AUC, calibration по бакетам, IV/lift по dimensions.
- Использование — через корреляцию «обзвонили vs скор», тренд по времени, разрез по продажникам.
- Impact — counterfactual «что было бы, если контактировали top-X%», из дельты TR умножать на ARR.