Привет! Меня зовут Черняховский Денис, и я Data Engineer. Я достаточно продолжительное время работаю с данными и увлекаюсь математической статистикой. Совсем недавно решил поискать в интернете, как другие опытные дата-инженеры исследуют качество данных при помощи статистики, и обнаружил, что никак… пум-пум-пум. А далее обнаружил, что проблема уходит корнями гораздо глубже, чем может показаться.

В этой статье я постараюсь рассказать:

  • почему дата-инженерам необходимо использовать статистику и почему её не используют

  • проведём тесты на реальных примерах данных

  • разберём проблему межпрофессионального разрыва компетенций между дата-инженерами и аналитиками

Почему инженеру данных стоит использовать статистику?

Разберём, какой базовый набор проверок/валидаций использует типичный дата-инженер и инженер качества данных.

Типичный чек-лист на проде:

  • NOT NULL

  • UNIQUE

  • REFERENTIAL INTEGRITY

  • row_count_today >= row_count_yesterday

  • max(updated_at) >= now() – 1h

  • revenue > 0

  • И какие-то прочие атомарные проверки

Это бинарные правила: либо сломалось, либо нет. Те же, кто работает с качеством данных, ежедневно сталкиваются с проблемой, когда бинарные проверки не показывают проблем «Тихие ошибки» (Silent Failures), но аналитики и заказчик прибегают с горящими глазами и кричат, что всё сломано.

А статистика — это вероятностное мышление. Статистика всегда покажет проблему и покажет её первой, если данная проблема имеет место быть.

Почему инженеры не используют статистику в валидации данных?

Статистика «не орёт», когда что-то пошло не так

Пример:

  • COUNT(*) = 0 — АЛЕРТ

  • mean + 3σ уехало — «Ну… вроде странно, но не факт»

В проде любят чёткие сигналы, а не «подозрения». Хотя, если подумать, мы можем превратить статистику в бинарный алерт через Thresholds (пороги).

  • Пример: Если abs(mean - median) > 20%, то мы не просто пишем в лог, а останавливаем пайплайн или кидаем Critical Alert.

Нет готовых «коробочных» паттернов

Для схем и пайплайнов есть:

  • Airflow

  • dbt tests

  • Great Expectations

  • Soda

А вот для стат методов:

  • drift distribution

  • stability index

  • autocorrelation

  • quantile tracking

— почти нет стандартов.

Поэтому большинство просто говорят:
«Не трогаем, потому что непонятно, как поддерживать».


Разберём простейшее, но мощное применение статистики в отслеживании выбросов (ошибочные данные, которые сильно отличаются от допустимых), такое как среднее и медианное значение, их сравнение и интерпретацию.

Да, вы можете жёстко задать валидацию по допустимым диапазонам, например a < x ≤ b. Но очень часто вы не располагаете информацией о допустимом диапазоне в силу большой дисперсии данных и сезонности отрасли.

Перейдём к делу!

1. Что такое среднее в статистике?

как видим, среднее может быть сильно смещено из за наличия хвоста
как видим, среднее может быть сильно смещено из за наличия хвоста

Среднее (арифметическое) в статистике — это мера центральной тенденции, рассчитываемая как сумма всех числовых значений в выборке, делённая на их общее количество.

Например, вам необходимо узнать среднюю зарплату по отделу, тогда:
(10 + 8 + 7 + 5) / 4 = 7,5.

Получается, средняя зарплата по отделу — 7,5 чего-то там. И это выглядит корректно, если предоставить такую статистику заказчику.

Но что, если в данных будет выброс?

(1000 + 8 + 7 + 5) / 4 = 255.

Получается, что средняя зарплата по отделу = 255, и не важно, что вы получаете 5–7 арбузиков за рабочий день, а ваш начальник — целую фуру арбузиков. Что-то здесь не так, даже интуитивно, правда?

2. Что такое медиана?

медиана всегда более устойчива к выбросам чем арифметическое среднее и всегда более приближена к середине графика
медиана всегда более устойчива к выбросам чем арифметическое среднее и всегда более приближена к середине графика

Медиана в статистике — это серединное значение упорядоченного набора данных, которое делит его ровно пополам: 50 % значений находятся ниже медианы и 50 % — выше.
Для нечётного числа данных медиана — это число в середине ряда; для чётного — среднее арифметическое двух центральных чисел. Это устойчивая мера центральной тенденции, менее чувствительная к «выбросам» (экстремальным значениям), чем среднее арифметическое.

Как считать медиану:

  1. Упорядочить элементы в списке по возрастанию.

  2. Посчитать количество элементов в списке.

  3. а) Если число элементов нечётное — найти число, стоящее посередине.
    б) Если число элементов чётное — найти два числа, которые находятся посередине, сложить их и результат разделить пополам.

Попробуем посчитать медиану зарплаты по отделу с жадным начальником:

4, 5, 7, 8, 1000

Делаем всё как в алгоритме выше и получаем медиану = 7.
Замечаете? Значение медианы очень близко к нашему среднему в первом рассмотренном случае.

Посчитав разницу между средним и медианой, можно понять, доверять ли тем данным, которые мы загружаем в наш DM-слой, или стоит начинать копать.


Но давайте перейдём к реальным примерам. Немного «потрогаем» статистику в валидации качества данных.

У нас есть таблица заказов orders и таблица услуг с ценами order_items.

create table dq_demo.order_items
(
    dt         Date,
    order_id   UInt64,
    item_id    UInt64,
    qty        UInt16,
    price      Float64,
    created_at DateTime
)
    engine = MergeTree PARTITION BY dt
        ORDER BY (dt, order_id, item_id)
        SETTINGS index_granularity = 8192;


create table dq_demo.orders
(
    dt         Date,
    order_id   UInt64,
    user_id    UInt64,
    source     LowCardinality(String),
    city       LowCardinality(String),
    revenue    Float64,
    created_at DateTime
)
    engine = MergeTree PARTITION BY dt
        ORDER BY (dt, order_id)
        SETTINGS index_granularity = 8192;

Наполним эти таблицы данными 

INSERT INTO dq_demo.orders
SELECT
  toDate(now()) - (15 - d) AS dt,
  (d * 1000000) + number AS order_id,
  1 + (number % 20000) AS user_id,
  multiIf(number % 3 = 0, 'web', number % 3 = 1, 'ios', 'android') AS source,
  multiIf(number % 4 = 0, 'Moscow', number % 4 = 1, 'SPB', number % 4 = 2, 'Kazan', 'Other') AS city,
  round(200 + (randNormal(0, 1) * 40) + (number % 10) * 3, 2) AS revenue,
  now() - toIntervalDay(15 - d) - toIntervalSecond(number % 86400) AS created_at
FROM numbers(50000)     -- 50k заказов в день
CROSS JOIN (SELECT arrayJoin(range(1, 15)) AS d);


INSERT INTO dq_demo.order_items
SELECT
  o.dt,
  o.order_id,
  (o.order_id * 10) + item_idx AS item_id,
  1 + (rand() % 3) AS qty,
  round(exp(4.2 + randNormal(0, 1) * 0.35), 2) AS price,
  o.created_at + toIntervalSecond(item_idx) AS created_at
FROM dq_demo.orders o
CROSS JOIN (SELECT arrayJoin(range(1, 1 + (rand() % 4))) AS item_idx)
WHERE o.dt < toDate(now());

И посчитаем при помощи скл запроса среднее, медиану и разницу между ними

Как будто всё отлично, придраться не к чему — разница между медианой и средним минимальная.

Но давайте попробуем сделать выброс: вставим число побольше в наше значение revenue = 21400, когда среднее по данному заказу около 214.

Делаем insert выброса
Делаем insert выброса

Выполняем запрос, который описали выше, и видим…

разница между среднем арифметическим и медианой
разница между среднем арифметическим и медианой

Видим, что разница между средним и медианой в нашем случае огромна. Я бы не стал отдавать такие данные заказчику как минимум, а перепроверил бы слой raw.


Этот случай с разницей между средним арифметическим и медианой примитивен. На данный момент существует огромное количество статистических методов, которые можно применить к вашим данным и более точечно искать проблемы. Но данный пример показывает, как полезна статистика даже в её самом примитивном варианте, а также показывает, что статистические методы наиболее практично применять вместе, а не по отдельности.


Почему этим на практике почти никто не занимается?

Потому что это зона между ролями:

  • слишком «математично» для DE

  • слишком «инфраструктурно» для аналитика

И именно здесь появляется Data Quality Engineer / Analytics Engineer нового поколения.


Изначально я хотел написать одну статью, но, начав её, понял, что одной статьёй здесь не обойтись. Поэтому эта статья — лишь вводная в мир анализа качества данных при помощи статистики. Будут ещё статьи про:

  • квантили и хвосты распределений

  • drift и PSI

  • корреляции и бизнес-инварианты

где я постараюсь наиболее полезно и практично показать, что дата-инженерия и статистика — неотделимые сферы деятельности и что дата-инженерам пора брать её на вооружение, чтобы не остаться на уровне валидаций данных 2010 года.

Если мы хотим делать устойчивые DWH и витрины, статистика — не опция, а инструмент первой необходимости.

А какие методы статистики вы используете в своих пайплайнах? Поделитесь вашими решениями!