Сначала вопрос: какое решение изменится от ответа
Продакт написал в чат: «посмотри, пожалуйста, что там с оттоком». Аналитик потратил три дня, собрал витрину, вывел четырнадцать графиков на дашборд и отправил ссылку. Через полгода в логах BI-системы у этого дашборда было одиннадцать открытий, девять из них — самим аналитиком.
Ошибка произошла не на третий день и не в SQL. Она произошла в первую минуту, когда вместо вопроса «какое решение ты примешь, увидев эту цифру» аналитик услышал техническое задание и пошёл его выполнять. Дальше всё было безупречно: корректные джойны, аккуратная витрина, приличная визуализация — безупречная работа над задачей, которой не существовало.
Есть и вторая, более опасная разновидность того же провала. Цифру всё-таки посмотрели, и по ней приняли решение. Отток мобильных пользователей — 12 %, веб — 7 %, вывод: «мобильный онбординг плохой, переделываем». Через квартал переделанный онбординг не изменил ничего, потому что в мобильном приложении просто больше доля пользователей из платного трафика, а платный трафик уходит быстрее везде. Данные не врали. Врал вывод, который они не выдерживали.
Джон Тьюки, статистик, придумавший ящик с усами и термин «разведочный анализ данных», сформулировал это в 1962 году в статье «The Future of Data Analysis»: гораздо лучше приблизительный ответ на правильный вопрос, который часто расплывчат, чем точный ответ на неправильный вопрос, который всегда можно сделать чётким.
Эта глава — про то, как получить правильный вопрос. Не про мотивационное «думайте о бизнесе», а про конкретную процедуру: какие поля должны быть заполнены, какой порог назван, что проверить до того, как открыть редактор SQL, и в какой момент честный ответ звучит «эти данные на такой вопрос не отвечают».
Тест на бесполезный вопрос
Возьмите вопрос, на который собираетесь потратить время, и заполните две строки:
- если ответ окажется A, мы сделаем ______;
- если ответ окажется B, мы сделаем ______.
Если в обеих строках написано одно и то же — считать не нужно. Ответ не изменит поведение, значит, его ценность равна нулю независимо от того, насколько красив будет график.
Это не риторический приём, а рабочая проверка, и она отсекает больше половины входящих запросов. Формально она опирается на понятие ценности информации: информация имеет ценность ровно настолько, насколько она способна изменить выбор действия. Дуглас Хаббард в «How to Measure Anything» строит на этом всю методику: сначала измеряется, какое решение стоит на кону и сколько стоит ошибка в нём, и только потом — сама величина.
Три типичных ответа на тест и что с ними делать.
- «Просто хочу понимать, как у нас дела». Это не решение, а привычка. Иногда законная — мониторинг существует, чтобы заметить аномалию. Тогда вопрос переформулируется в пороговый: «при каком значении мы забьём тревогу и что сделаем». Без порога получится не мониторинг, а обои.
- «Решение уже принято, нужна цифра для презентации». Честный запрос, если назвать вещи своими именами. Опасен тем, что аналитик начинает подбирать разрез, где цифра выглядит нужным образом, — про этот механизм и его последствия подробно в главе про проверку гипотез.
- «От этого зависит, вкладываем ли мы спринт в X». Годный запрос. С него начинается работа.
Ответ → действие, записанное заранее
Развитие теста — маленькая таблица, которую заполняют до запуска запроса. Заказчик заранее фиксирует, что сделает при каждом исходе.
| Ответ | Действие | Кто делает |
|---|---|---|
| Удержание D7 у мобильных ниже веба более чем на 5 п.п. | Спринт команды роста в мобильный онбординг | Продакт мобильного направления |
| Разница в пределах 5 п.п. | Не трогаем онбординг, идём в блок оплаты | Тот же |
| Данных не хватает, чтобы отличить | Ставим счётчики и возвращаемся через две недели | Аналитик |
Ценность таблицы в третьей строке. Она легализует ответ «не знаю» как валидный исход. Без неё аналитик под социальным давлением выдаст точечную оценку без интервала, а заказчик прочитает её как факт. Предварительная фиксация действий — это тот же приём, что предрегистрация в науке, и работает он по той же причине: убирает свободу интерпретации задним числом.
Пять минут перед SQL
Процедура, которую стоит прогонять на каждом запросе. Она короткая, но каждый шаг убивает определённый класс бесполезной работы.
изменится от ответа?"} B -->|никакое| STOP["Не считать.
Записать в бэклог «любопытно»"] B -->|вот это| C{"Кто принимает решение
и к какому сроку?"} C -->|владельца нет или срок вчера| STOP C -->|владелец и срок есть| D{"Где порог: при каком значении
решение меняется?"} D -->|порога нет| E["Вернуться к заказчику:
вопрос ещё не сформулирован"] E --> B D -->|порог назван| F{"Может ли наш способ сбора данных
отличить «выше порога»
от «ниже порога»?"} F -->|нет| G["Менять вопрос или способ сбора:
эксперимент, опрос, ручной разбор"] F -->|да| H["Оценить цену ответа:
выборка, срок, риск ошибки"] H --> I["Писать SQL"]
Обратите внимание на ветку F. Это единственный шаг, который требует технического знания, и именно он чаще всего пропускается. Заказчик спрашивает «почему упали продажи» — вопрос осмысленный, решение от ответа зависит, порог назвать можно. А способ сбора данных на «почему» не отвечает в принципе: в логах нет мира, в котором изменение не произошло. Об этом — глава про причинность, но отсекать такие вопросы нужно здесь, до того как потрачена неделя.
Четыре вида вопросов и почему их путают
Различие не академическое: от вида вопроса зависит, какой способ сбора данных вообще годится.
| Вид | Формулировка | Что нужно от данных | Типичная подмена |
|---|---|---|---|
| Описательный | Сколько, каких, как распределено | Полнота и корректный знаменатель | Среднее вместо распределения |
| Диагностический | Что изменилось и где именно | Разрезы, сопоставимые периоды | «Что изменилось» выдают за «почему» |
| Причинный | Что произойдёт, если мы сделаем X | Вариация, независимая от исхода | Корреляция в наблюдательных данных |
| Прогнозный | Какое значение будет в июле | Устойчивая во времени структура | Экстраполяция тренда через слом |
Практический вывод один: вопрос «почему» почти никогда нельзя закрыть тем же способом сбора, которым закрывают «сколько». Логи продукта отвечают на «сколько» и частично на «что изменилось». На «почему» отвечает либо эксперимент, либо разговор с людьми (см. выявление требований), либо квазиэкспериментальный дизайн — и всё это стоит существенно дороже.
Прогнозные вопросы в этом треке почти не рассматриваются: построение моделей — территория машинного обучения. Здесь речь об описательной и причинной аналитике: понять, что происходит, и понять, что из этого следствие чего.
Лестница от вопроса к цифре
Сквозная тема всего трека: цифра в отчёте описывает не мир, а результат вашего способа сбора данных. Между ними — лестница сужений, и на каждой ступени отсеивается не случайная часть наблюдений, а вполне определённая.
Разберём на конкретном вопросе: «какая доля наших клиентов сталкивается со сбоем оплаты».
- Уровень 1, целевая величина. Все клиенты, у которых оплата не проходит. Именно про них хочет знать заказчик.
- Уровень 2. В телеметрию попадают только те, у кого исполняется наш код. Отваливаются старые версии приложения, пользователи с блокировщиками, корпоративные сети с урезанным трафиком. Отбор неслучайный: у старых версий сбоев оплаты, скорее всего, больше.
- Уровень 3. Счётчик стоит на экране оплаты. Тот, кто не дошёл до экрана из-за подвисшей корзины, в знаменателе не окажется вовсе.
- Уровень 4. Событие должно доехать до сервера. Клиент, у которого приложение упало в момент оплаты, не пришлёт событие об ошибке — то есть самый тяжёлый случай систематически невидим.
- Уровень 5. Фильтры запроса: окно дат обрезает начатые в конце периода попытки, дедупликация схлопывает ретраи, внутренние аккаунты исключены.
- Уровень 6. Одно число: 3,7 %.
Вывод «у 3,7 % клиентов не проходит оплата» — это перенос утверждения с уровня 6 на уровень 1 без всяких оснований. Корректная формулировка: «среди сессий, дошедших до экрана оплаты и приславших телеметрию, доля с ошибкой — 3,7 %; истинная доля по всем клиентам почти наверняка выше, и насколько — из этих данных не установить».
Вторая формулировка длиннее и звучит менее уверенно. Именно поэтому она правильная. Подробный разбор механизмов, порождающих такие сужения, — в главах про источники данных и про качество данных.
Порог решения: сколько точности вам действительно нужно
Самый частый неотвеченный вопрос в аналитическом запросе — где порог. При каком значении решение меняется на противоположное? Без порога любая точность и достаточна, и недостаточна одновременно, а обсуждать размер выборки бессмысленно.
Пусть удержание D7 в когорте измерено как 40 % на выборке в 400 пользователей. Точечная оценка бесполезна сама по себе: важно, отличается ли она от порога. Доверительный интервал Вальда для доли:
$$\hat p \pm z_{1-\alpha/2}\sqrt{\frac{\hat p\,(1-\hat p)}{n}}$$
Подставляем: стандартная ошибка равна $\sqrt{0{,}4 \cdot 0{,}6 / 400} = \sqrt{0{,}0006} \approx 0{,}0245$, полуширина при $z = 1{,}96$ — примерно 4,8 п.п., интервал — примерно от 35,2 % до 44,8 %.
Дальше всё зависит от порога, и только от него:
- порог 30 % — интервал целиком выше, решение принимается уверенно;
- порог 35 % — интервал почти касается порога, решение формально проходит, но запас нулевой;
- порог 38 % — интервал накрывает порог, данные не отличают «выше» от «ниже», и честный ответ здесь «пока не знаю».
Нужный размер выборки выводится из требуемой полуширины $\varepsilon$, а она — из расстояния до порога:
$$n \ge \frac{z^2\,\hat p\,(1-\hat p)}{\varepsilon^2}$$
Чтобы отличить 40 % от порога 38 %, нужна полуширина не больше 2 п.п.: $n \ge 1{,}96^2 \cdot 0{,}24 / 0{,}02^2 \approx 2305$. То есть вместо 400 наблюдений нужно примерно 2300 — и об этом лучше узнать до, а не после того, как отчёт ушёл заказчику.
Тот же расчёт на Python, в форме, которую удобно вставлять в блокнот:
from math import sqrt, ceil
from statistics import NormalDist
Z95 = NormalDist().inv_cdf(0.975) # 1.959963...
def wald_interval(k: int, n: int, z: float = Z95) -> tuple[float, float]:
"""Доверительный интервал Вальда для доли. Годится при k >= 10 и n - k >= 10."""
p = k / n
half = z * sqrt(p * (1 - p) / n)
return p - half, p + half
def verdict(k: int, n: int, threshold: float) -> str:
"""Ответ в терминах решения, а не в терминах цифры."""
lo, hi = wald_interval(k, n)
if lo > threshold:
return f"выше порога (интервал {lo:.3f}..{hi:.3f})"
if hi < threshold:
return f"ниже порога (интервал {lo:.3f}..{hi:.3f})"
need = ceil(Z95 ** 2 * (k / n) * (1 - k / n) / (k / n - threshold) ** 2)
return f"не различаем: интервал {lo:.3f}..{hi:.3f} накрывает {threshold}; нужно n ≈ {need}"
print(verdict(160, 400, 0.30)) # выше порога (интервал 0.352..0.448)
print(verdict(160, 400, 0.38)) # не различаем: ... нужно n ≈ 2305
Сложность здесь никакая — $O(1)$ по времени и памяти; ценность в том, что функция возвращает не число, а формулировку в терминах решения. Аналитик, у которого ответ выглядит так, физически не может отдать голую точечную оценку.
Интервал Вальда при малых k заметно врёт (при k = 0 он вырождается в нулевую ширину), поэтому в проде обычно берут интервал Уилсона:
$$\tilde p = \frac{\hat p + \dfrac{z^2}{2n}}{1 + \dfrac{z^2}{n}},\qquad \text{полуширина} = \frac{z}{1 + \dfrac{z^2}{n}}\sqrt{\frac{\hat p\,(1-\hat p)}{n} + \frac{z^2}{4n^2}}$$
Сравнение методов и границы применимости — в главе про неопределённость и в обзоре на Wikipedia: Binomial proportion confidence interval. Аппарат вероятностей, на который всё это опирается, разобран в математике.
Цена ответа: вопрос стоимостью в три недели
У каждого вопроса есть цена, и её нужно назвать заказчику до начала работы. Для эксперимента цена измеряется не в часах аналитика, а в трафике и календаре.
Размер одной группы для двусторонней проверки разности двух долей при заданной мощности:
$$n_{\text{на группу}} \ge \frac{\left(z_{1-\alpha/2} + z_{1-\beta}\right)^2 \left\lbrack p_1(1-p_1) + p_2(1-p_2) \right\rbrack}{(p_1 - p_2)^2}$$
def n_per_arm(p1: float, mde_abs: float, alpha: float = 0.05, power: float = 0.80) -> int:
"""Сколько наблюдений нужно в КАЖДОЙ группе, чтобы поймать эффект размера mde_abs."""
p2 = p1 + mde_abs
z_a = NormalDist().inv_cdf(1 - alpha / 2) # 1.96 при alpha = 0.05
z_b = NormalDist().inv_cdf(power) # 0.84 при мощности 0.80
var = p1 * (1 - p1) + p2 * (1 - p2)
return ceil((z_a + z_b) ** 2 * var / mde_abs ** 2)
print(n_per_arm(0.10, 0.01)) # 14749 — поймать рост конверсии с 10 % до 11 %
print(n_per_arm(0.10, 0.02)) # 3839 — рост с 10 % до 12 %
Читается это так: чтобы отличить конверсию 11 % от 10 %, нужно около 14 750 пользователей в каждой группе, то есть примерно 29 500 всего. При потоке в 1000 попыток оплаты в день — месяц эксперимента. Уменьшение искомого эффекта вдвое учетверяет требуемую выборку, потому что $n$ обратно пропорционально квадрату эффекта.
Отсюда следует неприятный, но полезный вывод: вопрос «даст ли эта кнопка плюс полпроцента к конверсии» для большинства продуктов не имеет ответа. Не потому что кнопка не работает, а потому что трафика не хватит никогда. Правильная реакция — не запускать тест на три месяца, а сменить вопрос: искать эффекты, которые ваш поток данных способен различить. Механика экспериментов, мощность и подглядывание разобраны в главе про A/B-тесты; продуктовая сторона — в MVP и экспериментах.
Что брать в работу
Два измерения — влияние на решение и цена ответа — дают простую приоритизацию входящей очереди.
Квадрант 4 — самый коварный. Там живут запросы, которые выглядят серьёзной аналитической работой, съедают недели и не меняют ни одного решения: «выясни из логов, почему люди уходят», «сделай сквозную атрибуцию по всем каналам», «посчитай LTV по каждому источнику за три года». Общие принципы приоритизации — в product management.
От расплывчатой просьбы к отвечаемому вопросу
Уточнение — это диалог, а не анкета. Ниже — реальная по структуре последовательность, которая занимает семь минут и экономит три дня.
Ключевых реплик три. Шаг 2 переводит разговор с данных на решение. Шаг 6 вытаскивает порог — заказчик почти никогда не называет его сам, но почти всегда может назвать, если спросить в форме «хватит ли двух пунктов». Шаг 9 — заранее объявленное ограничение: этот способ сбора данных отвечает на «сколько», но не на «почему». Сказанное до работы, оно звучит как профессионализм; сказанное после — как оправдание.
О том, как вести такие разговоры с разными типами заказчиков, — в работе со стейкхолдерами.
Карточка вопроса
Итог диалога полезно свести в короткий артефакт и держать рядом с кодом запроса — тогда через полгода будет понятно, зачем эта витрина вообще существует.
вопрос: "Уходят ли новые пользователи мобильного приложения чаще, чем новые пользователи веба?"
решение: "Вкладывать ли спринт команды роста в мобильный онбординг"
владелец_решения: "Продакт мобильного направления"
срок: "2026-06-20, планирование квартала"
порог: "Разница в удержании D7 больше 5 п.п. — вкладываемся"
единица_наблюдения: "Пользователь, впервые зарегистрировавшийся в окне наблюдения"
метрика: "Доля вернувшихся хотя бы раз на 7-й календарный день после регистрации"
источник: "events.registration, events.session_start; витрина dm_user_activity"
чего_не_узнаем:
- "Почему уходят — причина требует интервью или эксперимента"
- "Пользователей, у которых не работает телеметрия"
- "Тех, кто зарегистрировался на вебе и вернулся в приложении под другим id"
что_дальше: "Если разница есть — A/B нового онбординга"
Поле чего_не_узнаем обязательное. Оно превращает ограничения способа сбора из умолчания в явный текст, который читает заказчик. Это то же самое, что раздел «допущения» в требованиях, и работает по той же причине.
Разбор: «почему конверсия упала на четверть»
Классический входящий запрос. Разберём его целиком — от формулировки до вывода.
Что известно. Конверсия сессии в покупку упала с 4,40 % в мае до 3,40 % в июне. Относительно это минус 23 %, и в чате это уже назвали «падением на четверть».
Что спрашивать первым. Не «почему упало», а «что в знаменателе и одинаково ли он устроен в мае и июне». Разложим по платформам.
| Период | Платформа | Сессий | Покупок | Конверсия | Доля трафика |
|---|---|---|---|---|---|
| Май | Desktop | 8000 | 400 | 5,00 % | 80 % |
| Май | Mobile | 2000 | 40 | 2,00 % | 20 % |
| Май | Итого | 10000 | 440 | 4,40 % | |
| Июнь | Desktop | 4000 | 208 | 5,20 % | 40 % |
| Июнь | Mobile | 6000 | 132 | 2,20 % | 60 % |
| Июнь | Итого | 10000 | 340 | 3,40 % |
Конверсия выросла на обеих платформах — с 5,00 % до 5,20 % и с 2,00 % до 2,20 %. Суммарная при этом упала на целый процентный пункт. Это не ошибка в арифметике, а парадокс Симпсона: изменилась структура трафика, и агрегат поехал за ней, а не за поведением пользователей.
Разложение изменения на вклад структуры и вклад самих ставок — точное тождество:
$$\Delta = \sum_i p_i^{\text{old}}\left(w_i^{\text{new}} - w_i^{\text{old}}\right) + \sum_i w_i^{\text{new}}\left(p_i^{\text{new}} - p_i^{\text{old}}\right)$$
где $p_i$ — конверсия сегмента, $w_i$ — его доля в трафике. Считаем:
- вклад структуры: $0{,}05 \cdot (0{,}4 - 0{,}8) + 0{,}02 \cdot (0{,}6 - 0{,}2) = -0{,}020 + 0{,}008 = -0{,}012$;
- вклад ставок: $0{,}4 \cdot 0{,}002 + 0{,}6 \cdot 0{,}002 = 0{,}0008 + 0{,}0012 = +0{,}002$;
- сумма: $-0{,}012 + 0{,}002 = -0{,}010$, то есть ровно наблюдаемый минус один процентный пункт.
Сто двадцать процентов падения объясняется сдвигом трафика на мобильные, и ещё двадцать процентов противоположного знака добавляет реальное улучшение обеих платформ.
Вывод в терминах решения. Вопрос «почему упала конверсия» задан неверно: конверсия не падала ни в одном сегменте. Правильный следующий вопрос — «почему выросла доля мобильного трафика», и это вопрос к маркетингу, а не к продукту. Спринт по улучшению воронки не изменил бы ничего.
Тот же расчёт в SQL. Схема: таблица sessions(session_id, started_at, platform, purchased).
WITH monthly AS (
SELECT date_trunc('month', started_at)::date AS month,
platform,
count(*) AS sessions,
count(*) FILTER (WHERE purchased) AS purchases
FROM sessions
WHERE started_at >= date '2026-05-01'
AND started_at < date '2026-07-01'
GROUP BY 1, 2
),
shares AS (
SELECT month,
platform,
purchases::numeric / sessions AS p, -- конверсия сегмента
sessions::numeric / sum(sessions) OVER (PARTITION BY month) AS w -- доля трафика
FROM monthly
),
paired AS (
SELECT platform,
max(p) FILTER (WHERE month = date '2026-05-01') AS p_old,
max(w) FILTER (WHERE month = date '2026-05-01') AS w_old,
max(p) FILTER (WHERE month = date '2026-06-01') AS p_new,
max(w) FILTER (WHERE month = date '2026-06-01') AS w_new
FROM shares
GROUP BY platform
)
SELECT platform,
round(p_old * (w_new - w_old), 5) AS mix_effect, -- вклад сдвига структуры
round(w_new * (p_new - p_old), 5) AS rate_effect -- вклад изменения конверсии
FROM paired
ORDER BY platform;
Оконная функция sum(...) OVER (PARTITION BY month) даёт долю сегмента внутри месяца без второго прохода по таблице; max(...) FILTER (...) — стандартный приём разворота двух строк в одну. Подробнее про окна и такие развороты — SQL для анализа.
Правило, которое стоит унести из разбора: прежде чем объяснять движение агрегата, проверьте, не двигался ли состав. Любой агрегат по неоднородной совокупности — это средневзвешенное, и веса меняются сами по себе.
SQL: что именно вы посчитали
Между вопросом и запросом есть зазор, в который проваливаются даже опытные люди. Три ошибки дают львиную долю неверных цифр.
Ошибка первая: считать события вместо людей.
-- Так спрашивают чаще всего — и почти всегда получают не то
SELECT (count(*) FILTER (WHERE event_name = 'payment_success'))::numeric
/ nullif(count(*) FILTER (WHERE event_name = 'checkout_open'), 0) AS conversion
FROM events
WHERE occurred_at >= date '2026-06-01'
AND occurred_at < date '2026-07-01';
Запрос синтаксически корректен и семантически бессмысленен. Пользователь, открывший оплату пять раз и купивший один, даёт 1/5. Пользователь, открывший один раз и не купивший, даёт 0/1. Итоговая дробь — не «доля людей, которые купили», а отношение двух счётчиков событий, у которого нет содержательной интерпретации. Плюс краевой эффект: покупка 30 июня в 23:50 по попытке, начатой 30 июня в 23:40, попадёт в числитель, а начатая 30 июня в 23:59 и завершённая 1 июля — нет.
Как правильно: зафиксировать единицу наблюдения (пользователь), взять первую попытку в окне и дать ей фиксированное окно на завершение.
WITH starts AS ( -- единица наблюдения: пользователь, первая попытка в окне
SELECT user_id, min(occurred_at) AS started_at
FROM events
WHERE event_name = 'checkout_open'
AND occurred_at >= date '2026-06-01'
AND occurred_at < date '2026-07-01'
GROUP BY user_id
),
wins AS ( -- успех засчитываем в пределах 24 часов от попытки
SELECT s.user_id
FROM starts s
JOIN events e
ON e.user_id = s.user_id
AND e.event_name = 'payment_success'
AND e.occurred_at >= s.started_at
AND e.occurred_at < s.started_at + interval '24 hours'
GROUP BY s.user_id
)
SELECT count(*) AS n, -- знаменатель
count(w.user_id) AS k, -- числитель
round(count(w.user_id)::numeric / count(*), 4) AS p_hat
FROM starts s
LEFT JOIN wins w ON w.user_id = s.user_id;
GROUP BY в wins нужен, чтобы несколько успешных оплат не размножили строки при джойне, — иначе count(w.user_id) окажется больше числа пользователей и конверсия превысит единицу. Это вторая классическая ошибка: джойн, размножающий строки. Проверка простая: после каждого джойна убедитесь, что число строк не выросло, либо явной агрегацией, либо сравнением count(*) до и после.
Ошибка третья: цифра без знаменателя и без разброса. Любое число в отчёте должно сопровождаться n, иначе его нельзя нести в решение. Достаточно вынести предыдущий запрос в CTE с именем agg и обернуть его:
-- agg — предыдущий запрос целиком, вынесенный в CTE: колонки n, k, p_hat
SELECT n, k, p_hat,
round(p_hat - 1.96 * sqrt(p_hat * (1 - p_hat) / n), 4) AS ci_lo,
round(p_hat + 1.96 * sqrt(p_hat * (1 - p_hat) / n), 4) AS ci_hi
FROM agg;
Правило для отчётов, которое стоит сделать обязательным в команде: колонка с долей всегда идёт вместе с колонкой n. На витринах OLAP это ничего не стоит по производительности — count(*) считается тем же сканом (см. ClickHouse и OLAP), — зато отсекает целый класс выводов по трём наблюдениям.
Отдельно: определения событий в вашем трекере — часть контракта, а не деталь реализации. Если checkout_open шлётся при открытии модалки, а не при показе формы, все конверсии смещены, и никакой SQL это не чинит. Договорённости о смысле событий и их качество — источники данных и качество данных, а на стороне пайплайнов — data quality и governance.
Жизненный цикл вопроса
Вопрос — не разовое сообщение в чате, а сущность с состояниями. Понимание этого цикла избавляет от главного организационного невроза аналитика: ощущения, что нужно ответить на всё и немедленно.
Два перехода в этой схеме обычно отсутствуют в реальных командах, и именно их отсутствие портит работу.
Первый — answered → more. Без него ответ «интервал накрывает порог» не имеет легального исхода, и аналитик под давлением выдаёт точечную оценку как факт. Второй — infeasible → draft. Без него запрос, на который данные не отвечают, всё равно уходит в работу и возвращается красивой картинкой с ложной уверенностью.
Полезная практика: каждое decided записывать в журнал решений — какой вопрос, какая оценка, какое решение приняли, что ожидали увидеть дальше. Через полгода это единственный способ узнать, помогала ли аналитика хоть чему-нибудь. Смежная тема со стороны продукта — аналитика и решения.
Почему дашборды никто не смотрит
Теперь можно назвать причину прямо. Большинство дашбордов не открывают не потому, что они плохо свёрстаны, а потому, что ни один график на них не отвечает на вопрос «какое решение изменится от этой цифры». Дашборд собирают из того, что есть в витрине, а не из решений, которые кто-то регулярно принимает.
Диагностика занимает пять минут: возьмите любой график с вашего дашборда и спросите автора, при каком значении этой линии кто-то что-то сделает. Если ответа нет — график декоративный. Обычно декоративны от 80 до 100 % площади.
Обратное устройство даёт живой инструмент: под каждое регулярное решение — свой блок, в котором есть текущее значение, порог, интервал и подпись, что делать при пересечении. Такой дашборд выглядит скучнее, зато его открывают. Подробно — в главе про дашборды; визуальная сторона, включая честные способы соврать графиком, — в главе про визуализацию; метрики интерфейса — в ux-design.
И сразу предупреждение на будущее: как только цифра становится целевой, она начинает менять поведение людей, которые на неё влияют, и перестаёт измерять то, что измеряла. Это закон Гудхарта, ему посвящены глава трека и материал про локальную оптимизацию. Поэтому вопрос «какое решение изменится» имеет продолжение: «и как изменится поведение, когда все узнают, что мы смотрим на эту цифру».
Антипаттерны постановки
- «Дай выгрузку». Просьба о данных вместо вопроса. Ответ: «конечно, под какое решение». Выгрузка без вопроса почти всегда порождает вторую выгрузку.
- Вопрос без порога. «Какое у нас удержание?» — ответ невозможно оценить как хороший или плохой, значит, он не изменит поведение.
- Вопрос без срока. Ответ через три недели на решение, принятое во вторник, — потраченное время, а не аналитика.
- Вопрос «почему» к наблюдательным данным. Логи фиксируют то, что случилось, а не то, что случилось бы. Либо эксперимент, либо квазиэксперимент, либо честное «причину отсюда не установить» — см. причинность.
- Вопрос, подогнанный под найденный результат. Сначала посмотрели двадцать разрезов, потом сформулировали гипотезу про тот, где получилось красиво. Механику разбирают Гельман и Локен в «The Statistical Crisis in Science»: даже без сознательного жульничества свобода выбора разреза после просмотра данных обесценивает любые p-значения.
- Метрика вместо вопроса. «Нам нужен NPS» — это ответ на невысказанный вопрос. Полезнее спросить, какое решение принимается по NPS и почему именно он.
- Точность ради точности. Три знака после запятой при выборке в 40 наблюдений — сигнал, что автор не считал интервал.
- Одно число там, где нужно распределение. Среднее время ответа 200 мс при p99 в 4 секунды описывает несуществующего пользователя. Про это — описательная статистика и распределения.
Мини-итог
- Вопрос считается поставленным, когда названы решение, владелец, срок и порог. Отсутствие любого из четырёх — повод вернуться к заказчику, а не открывать редактор SQL.
- Нужная точность не выбирается по вкусу: она выводится из расстояния до порога. Отсюда же берётся требуемый размер выборки, а значит — цена ответа в трафике и календаре.
- Цифра описывает не мир, а результат вашего способа сбора данных. Между целевой величиной и числом в отчёте — лестница неслучайных сужений; переносить вывод с нижней ступени на верхнюю нельзя.
- «Не различаем, нужно больше данных» — валидный ответ, и он должен быть заранее легализован в таблице «ответ → действие». Иначе его место займёт точечная оценка без интервала.
- Прежде чем объяснять движение агрегата, проверьте, не менялся ли состав совокупности: разложение на вклад структуры и вклад ставок — точное тождество и делается одним запросом.
- Каждое число в отчёте идёт вместе со своим
n. Дашборд, где ни один график не привязан к порогу и решению, закономерно никто не открывает.
Материалы
- John W. Tukey. The Future of Data Analysis (1962) — источник тезиса про приблизительный ответ на правильный вопрос.
- Douglas W. Hubbard. How to Measure Anything — измерение как снижение неопределённости и ценность информации через решение.
- Ronny Kohavi, Diane Tang, Ya Xu. Trustworthy Online Controlled Experiments — глава про постановку метрик и то, во что обходятся плохо заданные вопросы.
- Andrew Gelman, Eric Loken. The Statistical Crisis in Science — «сад расходящихся тропок»: почему вопрос, сформулированный после просмотра данных, не считается.
- Cassie Kozyrkov. Публикации по decision intelligence — практичный взгляд на аналитику как на обслуживание решений.
- Richard Hamming. You and Your Research — классический текст про выбор задачи; применим к выбору вопроса напрямую.
- Simpson’s Paradox, Stanford Encyclopedia of Philosophy — формальный разбор эффекта из примера с конверсией.
Что дальше
Вопрос поставлен, порог назван, цена ответа оценена. Дальше нужно понять, откуда физически берутся данные, которыми вы собираетесь отвечать, и какие искажения встроены в сам способ их получения — от определения события до выжившего источника, который виден только потому, что уцелел.