MBL
Ml / ДЗ2: EDA — три гипотезы про ДТП с нуля (статистика для защиты)
Ml сложный

ДЗ2: EDA — три гипотезы про ДТП с нуля (статистика для защиты)

statisticshypothesis-testingbootstrapchi2itmo

Отдельная статья про статистическую часть ДЗ2 (общий контекст задания и архитектура дашборда — в 02-hw2-eda-dtp-belgorod). Все три проверки реализованы в solution/stats_tests.py, посчитаны один раз по полной очищенной выгрузке (13 371 запись, 0 нераспознанных дат) и закэшированы — не по текущему срезу фильтров дашборда.

Зачем вообще «проверять гипотезу», а не просто посмотреть на график

График показывает разницу в конкретной выборке. Вопрос гипотезы другой: могла бы эта разница получиться просто случайно, даже если на самом деле никакой закономерности нет?

H0 — презумпция невиновности: по умолчанию разницы нет, всё видимое на графике — шум. H1 — то, что подозреваем. Тест не «доказывает» H1: он либо даёт веские основания отвергнуть H0 (тогда данные говорят в пользу H1), либо нет — и тогда мы просто не можем отличить эффект от случайности на этих данных, что не равно «эффекта нет».

Три H0 в этой работе, все три отвергнуты:

# H0 H1 Тест
H1 Доля ДТП с погибшими одинакова днём и ночью Ночью выше χ² на независимость
H2 Доля наездов на пешеходов одинакова зимой и летом Зимой выше Перестановочный тест
H3 Доля ДТП с погибшими одинакова в Белгороде и районах В Белгороде ниже χ² на независимость

p-value — самое неправильно понимаемое число в статистике

p-value отвечает не на вопрос «какова вероятность, что H0 верна», а на вопрос «если бы H0 была верна, насколько странным был бы этот результат». Маленький p-value — не «H0 почти наверняка ложна», а «если бы H0 была верна, результат вроде нашего почти невозможен».

Уровень значимости здесь везде 0.05, выбран заранее, не подгонялся под результат постфактум.

Реальные p-value всех трёх гипотез исчезающе малы: H1 — 2.67·10⁻³⁰, H2 — p<0.0001 (перестановочный тест), H3 — 8.83·10⁻⁴⁸. На выборках в тысячи записей почти любая реальная разница даёт такой p-value — сам факт значимости здесь не главное, интереснее величина эффекта (следующий раздел).

Доверительный интервал — не то, что кажется на первый взгляд

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

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

  • H1: +6.69 п.п., 95% ДИ [5.46; 7.92]
  • H2: +11.66 п.п., 95% bootstrap-ДИ [9.49; 13.70]
  • H3: −9.71 п.п., 95% ДИ [−10.67; −8.75]

Три теста, использованных в этой работе, и почему именно они

χ²-тест — для H1 и H3

Хи-квадрат сравнивает таблицу частот «группа × исход» (например, ночь/день × погиб/не погиб) с тем, какой она была бы при полном отсутствии связи. Условие применимости — ожидаемая частота в каждой ячейке не меньше 5; при выборках в тысячи записей (H1: 4481/8890, H3: 2911/10460) это выполняется с огромным запасом.

Выбран для H1 и H3, потому что оба вопроса — это ровно таблица 2×2 (бинарная группа × бинарный исход «с погибшими / без»), для которой χ² — стандартный и самый быстрый инструмент, не требующий ресурсоёмкого моделирования.

Перестановочный тест и bootstrap — для H2

Для H2 задание прямо требует хотя бы раз применить перестановочный тест или bootstrap — реализовано и то, и другое сразу, для разных задач:

Перестановочный тест (p-value)
# метки "зима"/"лето" 10 000 раз перемешиваются
# случайно между всеми наблюдениями,
# каждый раз пересчитывается разница долей
for _ in range(10_000):
    shuffled = rng.permutation(pooled)
    diff = shuffled[:n_w].mean() - shuffled[n_w:].mean()
# p-value = доля перемешиваний с разницей
# не меньше наблюдаемой (0 из 10 000 здесь)
Bootstrap (доверительный интервал)
# внутри КАЖДОЙ группы отдельно 10 000 раз
# берём случайную подвыборку с возвращением
# и считаем разницу долей на каждой копии
for _ in range(10_000):
    boot_w = rng.choice(winter, size=n_w, replace=True).mean()
    boot_s = rng.choice(summer, size=n_s, replace=True).mean()
# 95% ДИ = 2.5-й и 97.5-й процентили
# разброса этих 10 000 значений

Перестановочный тест отвечает на вопрос «а если бы метка зима/лето на самом деле ничего не значила — какие разницы получались бы от случайного перемешивания?». Ни одна из 10 000 случайных перестановок не дала разницу такую же большую, как наблюдаемая (11.66 п.п.) — отсюда p<0.0001.

Bootstrap — другая задача: не p-value, а доверительный интервал для самого эффекта. Ресэмплинг идёт внутри каждой группы отдельно (не путать с перестановочным тестом, где перемешиваются метки между группами) — так разброс отражает именно неопределённость оценки доли в каждой группе.

Почему не Mann-Whitney, как в гайде по Владимирской области для аналогичной третьей гипотезы? Обе гипотезы (H2 здесь и H3 там) проверяют бинарный исход (доля наезда на пешехода / доля летальных), и для доли перестановочный тест — такой же корректный и даже более прозрачный выбор, чем Mann-Whitney, который создан для сравнения целой порядковой шкалы (как тяжесть ДТП: лёгкий/тяжёлый/погибшие), а не одной бинарной метки. Разные тесты в двух гайдах — это разные, одинаково правильные способы закрыть требование «минимум два разных теста», а не то, что один гайд ошибся с выбором.

Independence — предпосылка, которую легко забыть

Все три теста молчаливо предполагают, что наблюдения независимы друг от друга. Для ДТП это не идеально: одно и то же опасное место может «плодить» похожие по обстоятельствам записи, то есть между строками есть слабая связь через локацию, а часть ДТП теоретически может включать одного и того же водителя повторно за 11-летний период выгрузки. Вывод о направлении эффекта от этого не рушится, но p-value стоит читать не буквально как точную вероятность, а как очень сильный сигнал в нужную сторону — это ограничение прямо прописано в analysis.md.

Effect size — важнее, чем сам факт p < 0.05

Мало сказать «p мал, значит есть эффект». Величина эффекта отвечает на вопрос «а насколько это важно на практике»:

  • H1 (+6.69 п.п.) — заметный, но не огромный эффект;
  • H2 (+11.66 п.п.) — самый крупный из трёх эффектов, почти в два раза выше базовой доли (20.26% → 31.93%);
  • H3 (−9.71 п.п.) — доля в районах (13.56%) почти в 3.5 раза выше, чем в городе (3.85%) — большая разница в относительном выражении, хотя в процентных пунктах сопоставима с H1.

Разница в 6-12 п.п. на выборках в тысячи записей — это большой, практически значимый эффект, а не просто «математически отличается от нуля».

Связь — это не причина

Все три вывода этой работы — наблюдаемые связи, а не доказанные причины. «В районах доля летальных ДТП выше» не означает «район сам по себе убивает» — за этой связью могут стоять скорость на трассах, время прибытия скорой или тип дороги, а выгрузка не содержит данных, чтобы разделить эти причины напрямую (как явно оговорено и в задании, и в разделе «Ограничения» analysis.md).

Отделять наблюдение от предположения о его причине — прямое требование ТЗ, и ровно то место, где на защите легко потерять баллы, сказав «потому что» вместо «есть связь, причина не установлена».

Вопросы, которые могут задать на защите

«А p-value 10⁻⁴⁸ — вы правда это посчитали, не опечатка?» Да, честно посчитано scipy.stats.chi2_contingency; на выборках в десятки тысяч записей почти любая реальная разница даёт исчезающе малый p-value — важна не сама цифра, а величина эффекта рядом с ней.

«Почему для H2 не тот же χ², что для H1 и H3?» Мог бы быть и χ² — H2 тоже сводится к таблице 2×2. Выбран перестановочный тест намеренно, чтобы выполнить требование задания про минимум два разных метода, и заодно показать способ, не опирающийся на приближение хи-квадрат распределением — оба подхода на этих данных дали бы согласованный результат.

«Что если бы p-value оказался больше 0.05 — гипотезу выкинули бы из отчёта?» Нет, задание прямо требует включить результаты всех трёх проверок независимо от значимости — незначимый результат тоже результат, он говорит «на этих данных эффект отличить от случайности не получилось», а не «эффекта точно нет». Первая версия H2 (по среднему injured_count зима/лето) как раз дала слабый эффект — от неё отказались в пользу более содержательного вопроса про долю наездов на пешеходов, а не потому, что первый результат «испортил бы отчёт».

Самопроверка 0 / 8
Могу своими словами сформулировать H0 и H1 для каждой из трёх гипотез
Понимаю, что p-value — это НЕ вероятность того, что H0 верна
Могу объяснить смысл «95% доверительный интервал», не путая его с «95% данных в интервале»
Могу объяснить разницу между перестановочным тестом (для p-value) и bootstrap (для ДИ)
Понимаю, почему H1/H3 проверены хи-квадратом, а H2 — перестановочным тестом, а не наоборот
Могу объяснить, зачем эффект в процентных пунктах важнее самого факта p<0.05
Знаю, какое допущение принято про независимость наблюдений и в чём его слабое место
Могу объяснить разницу «есть связь» / «есть причина» на своих же трёх выводах
Как усвоено?