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

Многие используют кластеризацию или простые scatter plot'ы, но когда точек становятся тысячи, они сливаются в одну черную кляксу. Сегодня мы на конкретном примере разберем, как превратить «кучу точек» в понятную и красивую инвертированную тепловую карту, где сразу видно — где у нас «белые пятна» (низкая конкуренция), а где рынок уже перенасыщен.

Итак, у нас есть датасет all_delivery_points.geojson со всеми пунктами выдачи заказов (ПВЗ). Всего их в России уже более 90 000, причем 66% — партнерские. Это огромная сеть, и вручную анализировать ее бесполезно. Если мы просто нанесем их на карту как маркеры, карта превратится в винегрет. Если используем стандартную тепловую карту, где красным обозначено скопление точек, мы увидим только Москву и пару городов-миллионников, а средние и малые города останутся едва заметным синим туманом.

Наша задача — сделать карту, полезную для бизнеса. Нам нужно ответить на вопросы:

  1. Где ПВЗ так много, что открывать новый — каннибализация?

  2. Где есть спрос, но точек нет («белые пятна»)?

Для этого мы пойдем на хитрость: мы не будем показывать абсолютную плотность, мы будем показывать относительную насыщенность внутри каждого города/кластера.

Агрегация и нормализация данных

Первое, что мы делаем — загружаем данные и переводим их в WGS84 (EPSG:4326), так как Folium работает именно в этой проекции.

gdf = gpd.read_file('all_delivery_points.geojson')
coords = np.array([(geom.x, geom.y) for geom in gdf.to_crs('EPSG:4326').geometry])

Если мы построим тепловую карту по сырым координатам, она будет необъективной. Почему? Потому что в Москве точек 1000, а в Твери — 10. Алгоритм тепловой карты «съест» Тверь фоновым шумом.

Чтобы этого избежать, мы:

  1. Округляем координаты до 3 знака (точность ~100 метров). Это позволяет сгруппировать точки, стоящие в одном здании или ТЦ.

  2. Считаем уникальные локации и частоту (counts).

  3. Делим карту на виртуальные сетки (в примере grid_size = 0.5 градуса ~ 50-55 км). Это нужно, чтобы обрабатывать каждый город отдельно.

# Агрегация дублей по одному адресу
coords_rounded = np.round(coords, decimals=3)
unique_coords, counts = np.unique(coords_rounded, axis=0, return_counts=True)

Чем больше ПВЗ в конкретной точке (внутри города), тем БОЛЬШЕ вес.

Звучит логично? На первый взгляд — да. Но для восприятия это означает, что красным будут гореть места скопления ПВЗ (конкуренты), а синим — места, где их мало.

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

Мы проходим по каждому "квадрату" сетки (городу), находим максимальную концентрацию ПВЗ в одной точке внутри этого города, и делим все значения на этот максимум.

weights = city_counts / max_count  # От 0 до 1

Таким образом, даже если в условном Краснодаре максимум ПВЗ в одной локации = 3, а в Москве = 30, на карте и там, и там самые "красные" точки будут иметь вес 1.0, показывая локальное перенасыщение.

Результат и выводы

Запускаем скрипт и открываем pvz_heatmap_inverted.html.

Что мы видим на примере столицы?

  1. Москва пестрит красными пятнами. Открывать тут новый ПВЗ? Только если вы нашли уникальную нишу или ваш адрес находится в синей зоне внутри Москвы (а таких почти нет).

  2. Подмосковье переливается желто-оранжевым — плотность средняя, но конкуренция уже чувствуется.

  3. Небольшой город N на карте России — мы видим ярко-синюю область. Это сигнал для бизнеса: «Сюда нужно заходить!». В городе есть несколько ПВЗ, но они сконцентрированы в одном ТЦ, а остальные районы — чистое поле.

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

По итогу мы написали скрипт, который из скучного JSON делает интерактивный бизнес-инструмент. Конечно, это базовая версия. В проде можно докрутить следующие вещи:

  • Учитывать не только количество ПВЗ, но и их проходимость (если есть данные).

  • Использовать реальные границы городов вместо сетки 0.5 градуса.

  • Добавить кластеризацию по времени работы или ассортименту.

Но и в текущем виде эта карта отвечает на главный вопрос: «Где нас не хватает?»

Если вы хотите искать места для бургерной или кофейни — принцип тот же, только вместо "ПВЗ" у вас будут "конкуренты". Удачи в анализе!

Весь код доступен по ссылке на нашем сайте. Берите, дорабатывайте, улучшайте.

Комментарии (0)