MBL
Ml / ДЗ2: EDA — PCA и t-SNE по профилям районов (не по отдельным ДТП)
Ml сложный

ДЗ2: EDA — PCA и t-SNE по профилям районов (не по отдельным ДТП)

pcatsnedimensionality-reductionitmo

Отдельная статья про раздел PCA/t-SNE в ДЗ2 (архитектура и дашборд — в 02-hw2-eda-dtp-belgorod, статистика гипотез — в 03-hw2-eda-statistics-belgorod). Код — solution/projections.py, вход для него собирает solution/features.py::district_profiles.

Ключевое решение: объект — район, а не отдельное ДТП

В гайде по Владимирской области PCA/t-SNE строятся на отдельных ДТП (тысячи точек, признаки — one-hot по категориям и обстоятельствам конкретной аварии). Здесь выбран другой объект: 22 района Белгородской области, а признаки — не сама авария, а числовой профиль района в целом.

district_profiles собирает на район: доля каждой категории ДТП, доля каждой тяжести, доля ночных/дневных ДТП, средние participants_count/injured_count/dead_count — 28 числовых признаков на 22 объекта. Задание прямо допускает оба варианта («можно работать с отдельными ДТП или составить профили территорий») — выбор профиля районов сделан ради интерпретируемости: 22 подписанных точки на графике читаются глазами и на защите, в отличие от тысяч перекрывающихся точек отдельных ДТП, где имеет смысл разве что общая форма облака, а не конкретные объекты.

Цена этого выбора — меньше статистической мощности (22 наблюдения вместо тысяч) и более грубый, агрегированный сигнал: PCA/t-SNE здесь показывают структуру между районами, а не между отдельными типами ДТП внутри одного района.

Зачем стандартизация перед PCA/t-SNE

StandardScaler приводит каждый из 28 признаков к среднему 0 и дисперсии 1 перед PCA/TSNE (projections.py::_feature_matrix). Без этого признак с большим числовым разбросом — например, mean_dead_count (десятичная дробь около 0.1-0.2) против cat_Столкновение (доля от 0 до ~0.5) — не обязательно доминировал бы сам по себе, но масштаб признаков в принципе разный по происхождению (доли 0-1 против средних абсолютных величин), и без стандартизации PCA ловил бы отчасти разницу единиц измерения, а не только реальную структуру данных.

PCA: 37.9% дисперсии — и почему это нормально

PCA строит новые оси как линейные комбинации 28 исходных признаков. Здесь сохранено 25.7% + 12.1% = 37.9% дисперсии в первых двух компонентах — заметно больше, чем 19.7% в гайде по Владимирской области, что ожидаемо: там 30+ one-hot признаков на тысячи объектов (дисперсия размазана очень широко), здесь всего 22 объекта и признаки — не бинарные one-hot, а доли (более гладкие, скоррелированные между собой величины), поэтому структуре проще сконцентрироваться в паре осей.

Как читать нагрузки (pca.components_). У каждой оси есть вклад каждого исходного признака. Топ-5 по модулю для PC1: sev_С погибшими (0.355), mean_dead_count (0.351), cat_Съезд с дороги (0.297), cat_Наезд на пешехода (0.285), cat_Опрокидывание (0.278). Это ось «доля тяжёлых/смертельных исходов» — районы с высокой долей погибших и характерными для этого категориями (съезд с дороги, опрокидывание — типично загородные высокоскоростные сценарии) лежат на одном полюсе.

На практике PC1 явно разводит крупные города (Белгород, Старооскольский, Губкинский, Белгородский район — отрицательный полюс, низкая доля погибших) и малые сельские районы (Краснояружский, Ровеньский, Чернянский, Вейделевский — положительный полюс). Это прямое визуальное подтверждение гипотезы H3 (доля ДТП с погибшими в городе ниже) — тот случай, когда PCA и формальный статистический тест независимо указывают в одну сторону.

PC2 сильнее всего связан с sev_Тяжёлый (0.407) и долей дневных/ночных ДТП (0.360 — обе стороны бинарного признака дают одинаковый по модулю вклад, это ожидаемо для взаимодополняющей пары долей) — вторая ось про долю умеренно тяжёлых исходов и структуру освещённости, отдельно от смертности.

Тяжесть и число погибших сознательно оставлены в признаках, в отличие от гайда по Владимирской области, где severity намеренно исключена. Разница в цели: там PCA/t-SNE строились по отдельным ДТП, и включение severity сделало бы разбиение тривиальным вокруг самой тяжести одной аварии.

Здесь единица — район, и вопрос ровно противоположный: не «разделяется ли тяжесть сама по себе», а «какие районы похожи по характеру аварийности в целом, включая тяжесть как одну из его сторон» — profile-based подход, где тяжесть законная часть профиля территории, а не то, что нужно исключать, чтобы не упрощать задачу.

t-SNE: два запуска, разный perplexity

t-SNE, в отличие от PCA, не пытается сохранить общее направление разброса — старается сохранить, какие точки были соседями в исходном 28-мерном пространстве. Ключевой параметр — perplexity.

При всего 22 объектах perplexity держится маленьким (общее правило — заметно меньше числа объектов, ориентир n/3): здесь два запуска с perplexity 5 и 15, random_state=42, init="pca" — оба на одних и тех же 22 профилях, seed зафиксирован, иначе результат было бы не с чем сравнивать (t-SNE стохастичен без фиксированного seed).

init="pca" — тоже не случайный выбор: инициализация координатами PCA вместо случайных точек делает результат воспроизводимее и обычно быстрее сходится, чем со случайного старта, особенно при таком маленьком числе объектов.

Оба запуска стабильно держат крупные города обособленно от малых районов — согласуется с PCA. Взаимное расположение мелких районов между собой заметно меняется между perplexity=5 и perplexity=15 — это ожидаемо, не баг: t-SNE не сохраняет глобальный масштаб, и при 22 точках чувствительность к perplexity выше, чем на тысячах наблюдений.

Частая ошибка новичков — сравнивать размер и расстояния между пятнами/точками на t-SNE. Размер и межкластерные расстояния там ничего не значат сами по себе, важен только состав соседей внутри локальной группы. Устойчиво «слипшиеся» в обоих запусках районы стоит считать более надёжной находкой, чем пару, сблизившуюся только в одном из двух прогонов.

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

«Почему PCA на районах, а не на отдельных ДТП, как в других работах по этому же ДЗ?» Оба варианта прямо разрешены заданием. Выбор в пользу профилей районов — ради интерпретируемости: 22 подписанных точки читаются на графике и на защите без дополнительных пояснений, тысячи точек отдельных ДТП требовали бы дополнительной работы, чтобы превратить облако в конкретный вывод.

«37.9% дисперсии — это мало, весь анализ ненадёжен?» Нет: это доля, сохранённая именно в первых ДВУХ компонентах ради визуализации на плоскости — у PCA есть и остальные компоненты, просто их не рисуют. Важнее, что первая компонента интерпретируется осмысленно (совпадает с независимо найденным эффектом H3), чем сам процент.

«Почему не убрали severity/dead_count из признаков, как в примере с ДТП-объектами?» Потому что единица анализа другая — район, а не одна авария. У ДТП-профиля убирать severity нужно, чтобы не сделать задачу тривиальной; у профиля района severity — законная часть общего портрета территории, ровно то, что и хотелось увидеть.

«Как понять, что кластер малых районов на t-SNE — не просто шум?» Смотреть, устойчив ли он между двумя запусками с разным perplexity, а не доверять картинке с одним запуском — если группа держится вместе и при 5, и при 15, это куда более надёжный сигнал, чем случайное совпадение одного прогона.

Самопроверка 0 / 7
Могу объяснить, почему объект PCA здесь — район, а не отдельное ДТП
Понимаю, зачем признаки стандартизуются перед PCA/t-SNE
Могу объяснить, что значит «37.9% объяснённой дисперсии» и почему это не провал
Могу прочитать нагрузки PC1 и объяснить, что это за ось по смыслу
Могу объяснить, что такое perplexity и зачем сделаны два запуска с разными значениями
Знаю, почему нельзя сравнивать размер и расстояния между пятнами на t-SNE
Могу объяснить разницу между подходом «профиль района» и «профиль отдельного ДТП»
Как усвоено?