Выбросы в данных: что удалять, что оставить и почему правило IQR почти всегда врёт на деньгах
Разбор на модельном наборе из 2000 заказов: правило 1,5×IQR помечает 6% заказов, из которых аномальны пять. Как отличить ошибку от редкого клиента и что делать с каждым случаем.
Содержание статьи
Средний чек вырос на треть, и в таблице виден один заказ на два миллиона. Дальше обычно происходит одно из двух: заказ молча удаляют, и метрика становится «нормальной», либо оставляют, и весь отчёт начинает описывать одного клиента. Оба решения принимаются за секунду и оба неверны, потому что вопрос не в том, насколько значение велико, а в том, откуда оно взялось. Ниже — разбор на модельном наборе из 2000 заказов, где заранее известно, какие значения настоящие, а какие сломаны.
Коротко
Выброс — это значение, далёкое от основной массы по выбранному правилу. Ошибка данных — значение, которого в реальности не было. Это пересекающиеся, но разные множества, и путаница между ними стоит дороже всего.
- Сначала происхождение значения, потом статистика. Правило поиска не знает, был ли заказ настоящим.
- Правило 1,5×IQR рассчитано на симметричные распределения; на деньгах оно помечает проценты нормальных заказов.
- Удалять по порогу нельзя: в нашем наборе это стоило бы 59,5% выручки.
- Одно значение может требовать исправления в одной метрике и сохранения в другой — это нормально.
- Тяжёлый хвост бьёт по экспериментам сильнее, чем по отчётам: он раздувает нужный размер выборки в десятки раз.
Один набор данных, четыре разных «средних»
В наборе 2000 заказов интернет-магазина: 1995 обычных покупок, три крупных корпоративных заказа на 418, 611 и 940 тысяч рублей и две записи, где цена попала в базу в копейках вместо рублей — 1,99 и 2,45 миллиона.
Общая выручка — 13,5 млн рублей. Средний чек — 6751 ₽, медиана — 2494 ₽. Среднее почти втрое больше медианы, и это первый признак тяжёлого хвоста: пара значений тянет его вверх, а половина покупателей платит меньше двух с половиной тысяч.
Отсюда простое правило: если среднее и медиана расходятся в разы, среднее нельзя показывать как «типичную покупку». Оно по-прежнему верно для расчёта выручки — просто отвечает на другой вопрос.
| Показатель | Значение | На какой вопрос отвечает |
|---|---|---|
| Среднее | 6751 ₽ | сколько выручки приходится на заказ |
| Медиана | 2494 ₽ | сколько платит середина покупателей |
| p90 | 7432 ₽ | граница дорогих покупок |
| p95 | 9965 ₽ | край массового сегмента |
| p99 | 20 240 ₽ | начало настоящего хвоста |
Посчитайте среднее и медиану рядом. Расхождение в разы означает, что дальше метрику нужно разбирать по квантилям, а не спорить о её значении.
Сначала происхождение, потом статистика
Пять экстремальных значений в наборе делятся на две принципиально разные группы. Две записи по два миллиона — техническая ошибка: цена умножена на сто, потому что источник отдал копейки, а загрузка ждала рубли. Таких заказов не было; их нужно исправить, а не удалить, и заодно починить загрузку.
Три корпоративных заказа на 418, 611 и 940 тысяч — настоящие деньги, которые компания получила. Удалить их означает объявить, что выручки не было. Это не очистка данных, а искажение отчётности.
Разница видна не в распределении, а в источнике: платёж подтверждён в эквайринге, есть отгрузка, есть документы. Поэтому первый шаг после обнаружения кандидата — не выбор порога, а проверка нескольких строк вручную. Пять записей смотрятся за десять минут и обычно сразу показывают механизм.
Цена ошибки в этом наборе заметная: две сломанные записи дают 32,9% всей выручки. Отчёт, в котором их не заметили, ошибается на треть — и никакое робастное правило этого не исправит, потому что данные придётся чинить в источнике.
WITH bounds AS (
SELECT
quantile_cont(amount, 0.25) AS q1,
quantile_cont(amount, 0.75) AS q3
FROM orders
)
SELECT
o.order_id,
o.created_at,
o.customer_id,
o.amount,
o.currency,
o.payment_status,
count(*) OVER (PARTITION BY o.customer_id) AS orders_by_customer
FROM orders o, bounds b
WHERE o.amount > b.q3 + 1.5 * (b.q3 - b.q1)
ORDER BY o.amount DESC;Правило IQR помечает 121 заказ. Аномальны пять
Классическое правило берёт первый и третий квартили и объявляет выбросом всё, что дальше полутора межквартильных размахов. В нашем наборе Q1 = 1386 ₽, Q3 = 4547 ₽, размах 3161 ₽, верхняя граница — 9288 ₽.
Под это правило попадает 121 заказ, шесть процентов всех покупок. Из них сломаны две записи и три — настоящие крупные клиенты. Остальные 116 — обычные дорогие покупки, ничем не примечательные, кроме того, что они выше границы.
Если поступить с ними так, как подсказывает слово «выброс», и удалить, выручка упадёт с 13,5 до 5,5 млн — на 59,5%. Метрика при этом станет гладкой и стабильной, а отчёт — неверным почти вдвое.
Правило не сломано: оно и не обещало отличать ошибку от крупного клиента. Множитель 1,5 выведен для примерно симметричных распределений, где за границей остаётся около 0,7% значений. Деньги, длительности и время отклика так не распределены: у них длинный правый хвост по своей природе, и любой симметричный критерий будет объявлять аномалией нормальную часть бизнеса.
IQR = Q3 − Q1; кандидат, если x < Q1 − 1,5·IQR или x > Q3 + 1,5·IQRЭто способ отобрать строки для проверки. Решение о судьбе строки правило не принимает.
| Решение | Выручка | Средний чек |
|---|---|---|
| Как есть, вместе с ошибками | 13,5 млн ₽ | 6751 ₽ |
| Исправлены две ошибки единиц | 9,1 млн ₽ | 4536 ₽ |
| Удалено всё за границей IQR | 5,5 млн ₽ | 2910 ₽ |
| Ограничение по p99 для расчёта | 7,1 млн ₽ | 3570 ₽ |
Что использовать вместо симметричного порога
Для скошенных величин работают три подхода, и выбирают между ними по тому, что вы собираетесь делать с результатом.
Квантильная граница честнее по смыслу: вы прямо говорите «смотрим верхний процент», а не полагаетесь на множитель, выведенный для другой формы распределения. Логарифм суммы перед проверкой возвращает распределению примерно симметричный вид, и тогда IQR снова осмыслен. Робастный z-счёт на медиане и медианном абсолютном отклонении устойчив к загрязнённому хвосту — обычное стандартное отклонение уже испорчено теми самыми значениями, которые ищет.
И ещё одна поправка, важнее выбора формулы: границы считают внутри сегмента. Корпоративные заказы, оптовые категории, разные валюты и страны имеют разный масштаб, и общий порог по всей таблице обязательно объявит аномалией нормальный для своего сегмента заказ.
- Квантили (p99, p99,5) — когда нужен управляемый объём проверки.
- IQR по логарифму суммы — когда важна форма, а не фиксированная доля.
- Робастный z на медиане и MAD — когда хвост уже загрязнён ошибками.
- Отдельные границы по сегментам, валютам и категориям — почти всегда.
Три решения и как их отражать
После проверки кандидат попадает в одну из трёх корзин, и от неё зависит, что произойдёт с числом в отчёте.
Техническую ошибку исправляют в источнике, а не в витрине. Правка в SQL-запросе отчёта живёт до первого нового отчёта; исправленная загрузка чинит все будущие расчёты сразу.
Настоящее редкое событие оставляют в деньгах и выносят в отдельный сегмент. Корпоративные заказы остаются в выручке, но массовый опыт показывают медианой и p90 — иначе один клиент управляет метрикой, по которой оценивают работу с тысячами покупателей.
Ограничение сверху — не очистка, а параметр конкретного расчёта. Оно уместно там, где дисперсия мешает: в эксперименте, в модели, в прогнозе. Ограниченная величина не может публиковаться как выручка: в нашем наборе p99-ограничение занижает её на четверть.
| Что нашли | Действие | Где отражается |
|---|---|---|
| Ошибка единиц или валюты | исправить загрузку, пересчитать период | журнал качества, changelog витрины |
| Дубликат события | убрать копию, добавить проверку | мониторинг качества |
| Настоящий крупный клиент | оставить в выручке, выделить сегмент | GMV + медиана и p90 отдельно |
| Мешает расчёту эксперимента | ограничить сверху для этого теста | описание метода в отчёте о тесте |
Нижний хвост: нули, возвраты и отрицательные значения
Про большие значения помнят все, потому что они портят среднее заметно. Нижний край обычно не проверяют вовсе, хотя ошибки там встречаются чаще и живут дольше.
Заказ на ноль рублей может быть тестовой покупкой сотрудника, подарочным сертификатом, полной оплатой бонусами или сломанной загрузкой, где сумма не доехала. Четыре разных случая, и правильное действие в каждом своё: тестовые заказы исключают из бизнес-метрик, оплату бонусами оставляют, но выделяют, а недоехавшую сумму чинят в источнике.
Отрицательные значения — отдельная история. Возврат, проведённый как заказ с минусом, уменьшит выручку правильно, но испортит средний чек и распределение: он попадёт в расчёт как ещё одна «покупка». Обычно возвраты нужно хранить отдельным типом операции и вычитать на уровне выручки, а не подмешивать в таблицу заказов.
Отрицательная длительность, отрицательное время между событиями и дата события раньше даты регистрации — почти всегда следствие часовых поясов или сдвинутых часов источника. Такие строки не выбросы, а сигнал, что стыковка таблиц сделана неправильно, и удалять их — значит прятать поломку.
- Нулевые суммы: проверить тип оплаты и признак тестового аккаунта до любого решения.
- Отрицательные суммы: возвраты хранить отдельной сущностью, не как заказ с минусом.
- Отрицательные интервалы: искать причину в часовых поясах, а не фильтровать по «> 0».
- Пустые и подставные значения вроде 1970-01-01 или −1: считать их отдельной категорией, а не числом.
Выброс может быть не в строке, а в дне
Строчная проверка находит одну аномальную покупку. Но чаще ломается не строка, а загрузка: за один день приезжает вдвое больше событий, потому что задание отработало дважды, или заметно меньше, потому что источник молчал час.
Такие сбои не ловятся правилом по сумме: каждая отдельная строка выглядит совершенно нормальной. Их видно только на агрегате по дню — числе событий, числе пользователей, средней сумме. Полезно держать простую ежедневную проверку: сравнение с медианой предыдущих четырёх одинаковых дней недели, чтобы не путать понедельник с воскресеньем.
Отдельный случай — задвоение при повторном запуске загрузки. Оно даёт ровно кратный рост, и это его признак: если метрика за день выросла примерно вдвое, а состав пользователей не изменился, ищите дубли по идентификатору события, а не всплеск спроса.
Проверять стоит и последний день выгрузки. Неполные сутки регулярно попадают в отчёт как провал метрики, и на их разбор уходит больше времени, чем на настоящие аномалии.
WITH daily AS (
SELECT
date_trunc('day', created_at) AS day,
count(*) AS orders,
count(DISTINCT customer_id) AS customers,
count(*) - count(DISTINCT order_id) AS duplicate_ids
FROM orders
GROUP BY 1
)
SELECT
day,
orders,
customers,
duplicate_ids,
round(orders * 1.0 / nullif(median(orders) OVER (
ORDER BY day ROWS BETWEEN 28 PRECEDING AND 1 PRECEDING), 0), 2) AS vs_recent
FROM daily
ORDER BY day DESC;Где хвост бьёт больнее всего: эксперименты
В отчёте выброс виден и обсуждаем. В A/B-тесте он действует тихо: раздувает разброс, а вместе с ним — количество пользователей, нужное чтобы разглядеть эффект.
Возьмём наш набор с исправленными ошибками: среднее 4536 ₽ при стандартном отклонении 26 843 ₽, то есть разброс в шесть раз больше самой величины. Чтобы поймать прирост в 5% с мощностью 80%, потребуется около 220 тысяч пользователей на группу.
Ограничим величину сверху на уровне p99 — 20 240 ₽. Разброс падает до 3357 ₽ при среднем 3553 ₽, и та же задача решается на 5600 пользователях в группе. Разница почти в сорок раз: эксперимент из невозможного становится двухнедельным.
Плата за это названа явно: ограниченная метрика отвечает на вопрос «изменилось ли поведение массового покупателя», а не «выросла ли выручка». Если гипотеза как раз про крупных клиентов, ограничение стирает ровно тот эффект, который вы ищете. Поэтому решение принимают до старта теста и записывают в дизайн вместе с основной метрикой.
| Метрика | Среднее | Стандартное отклонение | Пользователей на группу |
|---|---|---|---|
| Выручка как есть | 4536 ₽ | 26 843 ₽ | ≈ 220 000 |
| Выручка с ограничением по p99 | 3553 ₽ | 3357 ₽ | ≈ 5 600 |
Что писать в отчёте
Фраза «данные очищены от выбросов» не значит ничего: непонятно, что удалили, по какому правилу и сколько денег ушло вместе с ними. Через неделю этот же вопрос придётся разбирать заново, уже в споре.
Рабочая запись называет правило, объём и судьбу каждой группы: «Проверен 121 заказ выше 9288 ₽. Две записи с ценой в копейках исправлены в загрузке, период пересчитан. Три корпоративных заказа подтверждены в эквайринге и остаются в выручке; массовый чек публикуется как медиана 2494 ₽ и p90 7432 ₽. Для расчёта A/B-теста применено ограничение по p99».
Полезно показать и устойчивость вывода. Медиана в нашем наборе почти не заметила исправления ошибок — 2494 ₽ против 2491 ₽, — тогда как среднее изменилось на треть. Если вывод отчёта меняется только после очистки, это само по себе результат, и его стоит показать явно, а не прятать за словом «очищено».
- Правило и порог — числом, а не словом «выбросы».
- Сколько строк проверено и что с каждой группой сделали.
- Что осталось в деньгах и что показано как массовый опыт.
- Где применено ограничение и почему именно там.
- Как менялся вывод до и после — одной строкой.
Материалы по теме
Среднее, медиана и мода: какую «середину» выбрать в аналитике
Разбираем среднее арифметическое, медиану и моду на примерах выручки, времени доставки и размера заказа: когда показатели расходятся и что писать в выводе.
Дисперсия и стандартное отклонение: как измерять разброс в данных
Понятное объяснение дисперсии и стандартного отклонения для аналитика: как читать разброс, сравнивать сегменты и не путать вариативность с ошибкой данных.
Процентили и квантили: как читать p50, p90 и p99 в продуктовой аналитике
Как использовать процентили и квантили для latency, времени доставки, чеков и пользовательского опыта: формулы, примеры, ошибки и правила интерпретации.