Открытых данных по загруженности парковок в мире на удивление мало. Самый известный публичный набор — UCI Parking Birmingham: 30 парковок, октябрь–декабрь 2016 года, чуть больше трёх месяцев. Именно на нём до сих пор стоит заметная часть опубликованных работ по предсказанию занятости. Данным девять лет, и они охватывают один квартал.
Мы полтора года опрашиваем открытый API Департамента транспорта Москвы каждые полчаса и складываем результат в TimescaleDB. Накопилось столько, что грех не поделиться.
4 684 084 замера. 210 муниципальных парковок. С 31 марта 2025 года, непрерывно, шаг 30 минут.
GitHub: matrosovcmtn/moscow-parking-occupancy — канонический источник
Зеркала: Kaggle и Hugging Face
Лицензия: CC BY 4.0 на данные, MIT на код
Что внутри
Два файла в parquet, разложенные по месяцам.
occupancy — сами замеры, первичный ключ (time, parking_id):
Колонка |
Что это |
|---|---|
|
момент замера, UTC, шаг 30 минут ± минута джиттера |
|
ссылка на |
|
свободных мест в обычном (не инвалидном) пуле |
|
свободных мест для инвалидов |
|
процент занятости обычных мест |
parking_spots — паспорта 210 парковок: названия и адреса на русском и английском, ближайшее метро, координаты WGS-84, вместимость с разбивкой на обычные и инвалидные места. Плюс два поля, про которые ниже.
Старт за тридцать секунд:
import pandas as pd, glob occ = pd.concat( pd.read_parquet(f) for f in sorted(glob.glob("data/occupancy_*.parquet")) ) spots = pd.read_parquet("data/parking_spots.parquet") df = occ.merge(spots[["id", "name_ru", "subway_ru"]], left_on="parking_id", right_on="id")
Или через DuckDB, не поднимая всё в память:
SELECT date_trunc('hour', time) AS h, avg(occupancy_rate) FROM 'data/occupancy_*.parquet' GROUP BY 1 ORDER BY 1;
Прочитайте это, прежде чем обучать модель
Тут главное, ради чего стоит дочитать анонс. Данные наблюдательные и не почищены — а значит, в них есть вещи, которые тихо испортят вашу модель, если про них не знать.
39 парковок из 210 — не то, чем кажутся
19% московских парковок отдают постоянный ноль свободных мест. Выглядит как «забита под завязку». На самом деле город их зарегистрировал, но занятость по ним не публикует, либо датчик умер.
Девятнадцать из них не показали ни одного свободного места за всю историю. Ещё двадцать показали однажды и замолчали: парковка на 257 мест последний раз отдавала свободное место в мае 2026-го, парковка на 143 места у ВДНХ — в июле 2025-го. Парковка на 606 мест не бывает полной пять месяцев подряд.
Мы их не удалили — знание о том, какие фиды мёртвые, само по себе полезно. Но occupancy_rate у них это вечная сотня, которой никогда не было, и модель на таких данных учится предсказывать константу.
Поэтому в паспортах есть два поля:
spots = pd.read_parquet("data/parking_spots.parquet") live = spots.loc[~spots.feed_silent, "id"] occ = occ[occ.parking_id.isin(live)] # отсечёт ~4,8% строк
feed_silent — не отдавала свободных мест 30 дней. last_free_at — когда отдавала в последний раз, NULL если никогда. Второе поле важнее первого: по нему вы соберёте пометку на нужную вам дату, а не будете верить нашей.
Разница ощутимая: средняя занятость по городу со всеми парковками — 55,2%, без молчащих — 51,1%.
Цензурирование на границах
25,3% всех строк — ровно 100, и 7,4% — ровно 0. Большая часть этого правда: парковки действительно заполняются и действительно пустеют. Но обычная регрессия на таких данных будет смещена на краях, целевую переменную надо считать ограниченной.
Три странности в самих числах
Отдельный раздел README посвящён дефектам, которые мы нашли в собственных данных, когда готовили публикацию, — и оставили, документировав:
occupancy_rateуходит ниже нуля в 19 792 строках (0,42%), вплоть до −1000. Все они лежат в интервале с 18 мая по 9 декабря 2025 года: до января 2026-го коллектор считал процент, не ограничивая число свободных мест объявленной вместимостью. После — ни одной такой строки. Фильтруйтеoccupancy_rate >= 0, если нужна чистая шкала.occupancy_rateнельзя пересчитать изparking_spots. Знаменатель — вместимость, объявленная на момент замера, а в паспорте лежит сегодняшняя. 74 парковки из 190 её переобъявляли. Если знаменатель нужен, доставайте его из самой строки:free_spaces / (1 - occupancy_rate / 100).Счётчик инвалидных мест врёт. Превышает паспортную вместимость в 2,0% строк, максимум — 9979 свободных мест при объявленных двух. И это независимый счётчик, а не часть
free_spaces: в 6,8% строк он больше.
Мы намеренно не стали ничего из этого «чинить». Наблюдательный архив ценен тем, что показывает, как ведёт себя настоящий городской источник данных, — со сбоями, переобъявлениями и мёртвыми датчиками. Почищенный датасет выглядел бы солиднее и был бы менее честным.
Как это собирается
открытый API Департамента транспорта Москвы │ опрос раз в 30 минут ▼ parking-data-collector (FastAPI + APScheduler) │ bulk insert ▼ гипертаблица TimescaleDB parking_occupancy │ экспорт с разбивкой по месяцам ▼ data/occupancy_YYYY-MM.parquet
Скрипт экспорта лежит в репозитории — пайплайн воспроизводим целиком. Обновление еженедельное, по крону.
Честно про пропуски: они бывают, когда лежит источник или наш коллектор. Самый заметный — в апреле 2026-го, но там ломался бэкап, а сбор шёл нормально.
Чего в датасете нет
Только муниципальные парковки. Торговые центры, частные стоянки и бесплатные места вдоль улиц в открытый API Москвы не попадают.
Никаких предсказаний. Это чистая наблюдаемая история. Модели, которые крутятся у нас в проде, в датасет не входят.
Никаких персональных данных. Агрегированные счётчики мест, ни машин, ни номеров, ни водителей.
Зачем это может пригодиться
Полтора года получасовых замеров по двум сотням точек — это довольно универсальная песочница: предсказание временных рядов с явной суточной и недельной сезонностью, кластеризация точек по профилю поведения, пространственный анализ по координатам и метро, детекция аномалий и деградации датчиков. Плюс редкая возможность посмотреть, как выглядит городской источник данных изнутри, со всеми его переобъявлениями вместимости и сходящими с ума счётчиками.
Мы сами используем эти данные на parkout.ru — карта загруженности с теплокартой и прогнозом.
Если возьмёте датасет в работу — напишите, что получилось. Issue и PR в репозитории приветствуются, особенно если найдёте в данных то, что мы проглядели. Один раз мы уже это проглядели.
Это третья статья по следам одного проекта. Первая — про чёрную карту без единой ошибки в консоли. Вторая — про те самые −1000% занятости, которые нашлись в этом датасете уже после публикации.
Tomasina
Какой критерий занятости парковочного места? Счетчик [Всего мест] - [въехали] + [заехали]? Видеокамера с распознаванием? Факт оплаты парковки (что не означает что оно уже занято)?
cmtn Автор
Источник каждые 3-10 минут публикует обновленное состояние каждой парковки, а именно количество занятых мест. То есть информации по отдельно взятому парковочному месту нет.
cmtn Автор
Все парковки, которые есть в датасете, закрытого типа (ГПЗТ). Въезд туда только через шлагбаум, на таких парковках количество занятых мест обновляется с каждой въехавшей машиной. Придорожные парковочные зоны пока не транслируются Мос. паркингом