Felicuss/urban-ridership-forecast

Прогноз пассажиропотока и инструменты диспетчера городского транспорта.

HTML

0

215 commits

updated Sep 27, 2026

See the code

README

Час пик: прогноз загрузки трамвайных маршрутов

Прогнозируем почасовые посадки на 10 трамвайных маршрутах Москвы на ноябрь–декабрь 2025 года по валидациям за январь–октябрь 2025 года. Проект включает модель прогноза и сервис для диспетчера.

Постановка, критерии и сроки лежат в docs/. Анализ данных, внешние факторы и эксперименты описаны в docs/analysis/README.md. Область применимости модели: docs/applicability.md, ограничения и план развития: docs/limitations_and_roadmap.md.

Быстрый старт

Нужен только Docker. Из корня репозитория:

docker compose up -d --build

После сборки (у нас без кеша 3 минуты) интерфейс открывается на http://localhost:8080, вход: логин dispatcher, пароль chaspik. Документация API: http://localhost:8080/swagger-ui.html, кнопка «Authorize» принимает тот же логин и пароль. Помощнику диспетчера нужен ключ LLM_API_KEY в .env (см. .env.example), без него работает всё остальное.

КритерийГде смотреть
Качество прогнозасабмиты в forecasts/, подтверждённые результаты со SHA-256 в реестре, подключённый прогноз - ниже
Внешние данныессылки, лицензии и способ выгрузки - external/README.md, эффект каждого источника - docs/analysis/README.md, в интерфейсе вкладка «Факторы»
Область применимости и поправкиdocs/applicability.md, ползунки с живым пересчётом - вкладка «Сценарий», точность - вкладка «Точность»
Архитектура и производительностьсхема ниже, замеры k6 - backend/README.md: одна реплика на 2 vCPU держит 4000 запросов/с при p95 < 1 мс
Функциональностьгоризонты сутки, неделя, месяц и год; маршрут, остановка, участок и сеть; карта; выгрузка CSV и XLSX кнопкой в верхней строке
Польза для диспетчерараздел ниже, вкладка «Смена» и режим табло
Дополнительные возможностисписок ниже

Что уже сделано

  • Сырые валидации (62.4 млн строк) переведены в parquet. Целевая величина воспроизводится из сырья точно во всех 72 960 ячейках.
  • Подключены и проверены внешние источники: производственный календарь, почасовая погода Open-Meteo, помесячная статистика пассажиропотока и загруженность дорог data.mos.ru, события сети по постам Дептранса, геометрия маршрутов из OpenStreetMap. Ссылки и способ выгрузки: external/README.md.
  • На первом этапе бэктест на 4 фолдах сравнивал baseline организаторов (0.469), медианные профили (до 0.845 в среднем, 0.904 на октябре), LightGBM и foundation-модели Chronos-2, t0-beta, TimesFM 3.0. Лучше всего на том этапе (0.870) работала смесь профиля с городским сезонным уровнем из data.mos.ru и Chronos-2 + t0-beta на дневных суммах.
  • К сервису подключён файл v25, построенный поверх v21 с обновлённым согласованием по результатам платформы. На последнем скриншоте есть скор 0.91274, предположительно для v25; порядок двух последних загрузок ещё не подтверждён. Описание v25/v26, передача ML команде, реестр подтверждённых результатов.
  • Эксперимент v8: обученный на Mac JointDayNet + Chronos-2, дневные суммы v7 сохранены. На шести периодах разработки средний прирост +0.001782; скор платформы 0.90555, на 0.00015 ниже v7. Лучшее решение сохраняем. Обучение, проверки и воспроизведение.
  • v9: графовая модель режимов + STL/ETS и согласование групповых сумм. Скор 0.90543, на 0.00027 ниже v7; исторический прирост не подтвердился. Архитектура, все проверки и ограничения.
  • s85: прозрачная модель уровень × форма с режимами маршрутов из каталога событий. На шести фолдах s85 выше v11 (0.8818 против 0.8658), на табло 0.89761: выигрыш лета–осени зимой не подтвердился. Отчёт.
  • Сервис на Java 25 и Spring Boot 4.1 (WebFlux) отдаёт прогноз из artifacts/ по маршруту, остановке, участку и сети на горизонтах день, месяц и год, пересчитывает его по ползункам и событиям, строит кадры тепловой карты и выгружает CSV и XLSX. Одна реплика на 2 vCPU держит 4000 запросов/с при p95 < 1 мс. Запуск, API и замеры: backend/README.md.
  • Интерфейс диспетчера на React: карта Москвы с тепловой картой посадок по часам, вагонами по расписанию transport.mos.ru (на крупном плане объёмными), метро, 3D-домами и осадками там, где они идут. Любая дата с января 2025 по декабрь 2027 с пометкой «факт», «прогноз» или «оценка», проигрывание времени до ×900, поездка трамвая с посадками на каждой остановке, прогноз по сети, маршруту, остановке и участку, сравнение двух дат на одном графике, советы по интервалам, ползунки сценариев, внешние факторы и качество модели на одном экране. Выгрузка в CSV и XLSX с выбором объекта, периода и шага, фильтр маршрутов и спутниковая подложка. Подробности: frontend/README.md.
  • Вкладка «Смена» для диспетчера: сводка смены одной кнопкой, оповещения на завтра по маршруту или участку, узкие места на неделю вперёд и калькулятор выпуска вагонов. Режим табло на большой экран диспетчерской.
  • Сбои из оперативного канала Дептранса превращаются в события сценария: сервис сам читает t.me/DtOperativno, находит пары «задерживаются трамваи» и «движение восстановлено» и считает потерю посадок по часам. Сбой можно примерить на любой день прогноза.
  • После встречи с организаторами 26.09: вход в сервис по логину и паролю; горизонт «неделя» и пиковый час каждого дня у недели и месяца; маршрут по станциям и дням одной клавишей (S) и номера маршрутов с клавиатуры; сплит и панели виджетов, которые диспетчер расставляет сам; валидации вне часов работы маршрутов (проверки оборудования) отброшены из факта; пропуски в данных 2025 года найдены, у каждого названа причина по постам Дептранса и восстановлены посадки по прошлым неделям; №5 по умолчанию скрыт. Подробности: frontend/README.md, backend/README.md.
  • План и факт по прошедшим дням: на 1 февраля - 31 октября 2025 сервис хранит прогноз, который диспетчер получил бы вечером накануне, и показывает его пунктиром рядом с фактом. Точность плана 0.8912 по часам и 0.9249 по суткам. Как собран план: analysis/export_plan.py.
  • Тур по разделам при первом входе: 11 шагов с подсветкой блоков, повторить можно кнопкой со знаком вопроса в верхней строке.
  • Помощник диспетчера: ReAct-агент на Java в плашке верхней строки, вопросы текстом или голосом. Модель gpt-oss:120b через Ollama Cloud, 15 инструментов MCP-сервера на Python (SDK mcp 2.2.0), история диалога и кэш ответов в Redis. Агент считает, показывает на карте и открывает нужную панель. Подробности: mcp-server/README.md, backend/README.md.
  • Презентация для жюри: presentation/chaspik.html, один HTML-файл на 10 слайдов, открывается без сети. Это снимок прежней версии v11; текущий сервис и его оценка описаны выше. Сборка и данные: presentation/README.md.

Сервис по умолчанию отдаёт прогноз v25 до ячейки: формула профиля домножается на поправку до v25 в каждой ячейке, поэтому ползунки и события работают поверх v25. Как это устроено: artifacts/README.md.

Структура

analysis/           скрипты анализа, экспорта и экспериментов; ML-раунды s41…s83
artifacts/          артефакты модели для сервиса: компоненты прогноза, ползунки, остановки, сеть (s40)
backend/            сервис прогноза на Java: API, сценарии, выгрузка, тесты
frontend/           интерфейс диспетчера на React: карта, время, сценарии, факторы
mcp-server/         MCP-сервер на Python для агента: инструменты поверх API и команды интерфейсу
presentation/       презентация на Remotion: один HTML-файл, исходники и скрипты данных
dataset/            данные организаторов
  README.md         описание датасета
  labels/           почасовые посадки route;date;hour;boardings
  spravochniki/     справочники маршрутов и остановок (xlsx)
  test_submission.csv   шаблон сабмита с baseline
docs/               постановка, критерии, анализ, обзоры
  analysis/         отчёт, графики (figures/) и таблицы результатов (tables/)
  research/         обзоры моделей, похожих решений, внешних источников, событий сети
external/           внешние данные: погода, календарь, data.mos.ru, OSM, события
forecasts/          прогнозы на ноябрь-декабрь в формате сабмита и их коэффициенты
deploy/             nginx перед репликами сервиса и сценарий нагрузочного замера k6
tests/              эталонные тесты артефактов на Python

Архитектура

Модель считается на Python офлайн и отдаёт сервису готовые артефакты. Сервис на Java модель не запускает: при старте он сверяет артефакты с manifest.json по sha256 и пересчитывает прогноз по той же формуле, что и экспорт. Если результат разойдётся с эталоном, сервис не стартует.

flowchart LR
  subgraph PY["Python, офлайн: analysis/"]
    A["Приём и нормализация<br/>s00: 10,4 ГБ CSV в parquet<br/>s03: проверка labels"]
    B["Внешние данные<br/>s01, s08, s33, s41, s42, s43: Open-Meteo,<br/>data.mos.ru, transport.mos.ru, OSM,<br/>посты Дептранса"]
    C["Геопривязка<br/>export_network: остановки справочника<br/>и OSM ближе 40 м, доли посадок"]
    D["Прогноз v25<br/>s41…s121: профили и LightGBM,<br/>оценка неопределённости,<br/>согласование с агрегатами"]
    E["Экспорт s40<br/>artifacts/ и manifest с sha256"]
  end
  subgraph JV["Сервис: backend/, Java 25 + Spring WebFlux"]
    F["infrastructure<br/>загрузка и сверка артефактов"]
    G["domain<br/>формула прогноза, агрегация,<br/>шкала факт, прогноз, оценка"]
    H["application<br/>запросы, сценарии, кэш"]
    I["api<br/>REST /api/v1, OpenAPI,<br/>ошибки RFC 9457, CSV и XLSX"]
  end
  subgraph WEB["Интерфейс: frontend/, React 19"]
    J["карта MapLibre, время,<br/>прогноз, смена, сценарии, табло"]
  end
  subgraph AG["Агент"]
    K["mcp-server/, Python<br/>15 инструментов MCP"]
    L["Ollama Cloud<br/>gpt-oss:120b"]
    R["Redis<br/>история и кэш"]
  end
  T["t.me/DtOperativno<br/>сбои движения"]
  A --> C --> D --> E
  B --> C
  B --> D
  E --> F --> G --> H --> I -->|nginx| J
  B -. погода по дате .-> J
  T -. раз в 10 минут .-> F
  H <-->|ReAct| L
  H <--> K
  K -->|REST| I
  H <--> R

Границы слоёв проверяет ArchitectureTest: domain не зависит от Spring, Reactor, Jackson и других слоёв, application вызывается только из api, к api и infrastructure не обращается ни один слой. Подробности по сервису и замеры нагрузки: backend/README.md.

Польза для диспетчера

Прогноз нужен, чтобы заранее решить, сколько вагонов выпускать на линию и в какие часы. Пример из интерфейса: маршрут 17, пятница 14 ноября 2025. В 8:00 v25 прогнозирует 5 261 посадку при интервале 7 минут по расписанию transport.mos.ru — это около 307 посадок на рейс. «Витязь-М» везёт 185 человек при 5 чел/м² (pk-ts.org).

  • Карточка «Посадок на рейс» делит прогноз маршрута на число рейсов по расписанию и подсвечивает часы выше порога. При пороге 185 сервис советует интервал 4 минуты вместо 6 в 7-10 ч и 4 минуты вместо 7 в 16-20 ч. Порог диспетчер задаёт ползунком.
  • Часы, где на рейс меньше 15 посадок, отмечены отдельно. Там интервал можно увеличить, а вагоны перевести на пиковые часы.
  • Перекрытие, мероприятие или снегопад задаются событием на вкладке «Сценарий», прогноз по сети и по дням пересчитывается сразу.
  • Участок между двумя остановками показывает, где именно на маршруте растёт поток, и выгружается в CSV или XLSX отдельно.

Вкладка «Смена» собирает это в рабочие инструменты:

  • Сводка смены на выбранный день: погода, посадки и пиковый час, разница с тем же днём неделей раньше, три самых загруженных маршрута, тесные часы с советом по интервалу, события дня. Кнопка копирует текст для рабочего чата, вторая просит агента пересказать его.
  • Оповещения: подписка на маршрут или участок с порогом посадок на рейс. Если завтра поток выше порога, подписка краснеет, а на колокольчике в верхней строке появляется число.
  • Узкие места на 7 дней: маршрут, день и часы, где на рейс входит больше людей, чем помещается в вагон, от самых острых, с остановкой, где входят больше всего.
  • Калькулятор выпуска: интервал в выбранные часы превращается в число вагонов на линии, посадки на рейс и вагоно-часы. Рядом те же числа по расписанию.
  • Табло на большой экран: крупные числа по одному маршруту, маршруты сменяются сами каждые 8 секунд, внизу все маршруты сразу, бегущая строка с оповещениями и сбоями.

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

Дополнительные возможности

Сверх обязательного (горизонты день, месяц и год, агрегация по маршруту, остановке, участку и интервалу, карта, выгрузка CSV и XLSX, внешние факторы):

ВозможностьГде в интерфейсе
Сводка смены, оповещения на завтра с порогом посадок на рейс, узкие места на неделю, калькулятор выпуска вагоноввкладка «Смена», колокольчик в верхней строке
Табло на большой экран диспетчерскойкнопка с монитором в верхней строке
Помощник диспетчера: вопросы текстом или голосом, агент считает, показывает на карте и открывает панельплашка «Спросить» в верхней строке, нужен ключ LLM_API_KEY
Сбои из канала «Дептранс. Оперативно» как события сценария, их можно примерить на любой деньвкладка «Сценарий»
Из чего сложился прогноз: вклад каждого шага формулы за сутки, от профиля истории до итога v25карточка на вкладке «Прогноз»
План и факт по прошедшим днямграфик на вкладке «Прогноз» для дат до 31 октября 2025
Сравнение двух дат на одном графике«Сравнить с» под графиком прогноза
Горизонт «неделя», пиковый час каждого дня, самые напряжённые дни месяцавкладка «Прогноз»
Помесячная оценка на 2026–2027 годы с коридоромлюбая дата до 31.12.2027
Поездка трамвая с посадками на каждой остановке«Пустить трамвай» в карточке маршрута
Маршрут по станциям и дням: матрица остановок против дней недели или месяцакнопка в карточке маршрута или клавиша S
Пропуски в данных 2025 года с причиной по постам Дептранса и восстановленными посадкамивкладка «Точность»
Погода на карте там, где идут осадки, свет по времени суток, метро, объёмные дома, спутниккнопки слоёв над картой
Раскладки «сплит» и «панели» с виджетами, которые диспетчер расставляет самзначки в верхней строке или клавиша V
Горячие клавиши: номер маршрута цифрами, день и час стрелкамисписок в настройках по клавише ?
Вход по логину и паролю, Swagger с кнопкой «Authorize»экран входа, /swagger-ui.html
Тур по разделам при первом входекнопка со знаком вопроса в верхней строке

Запуск

Нужен uv. Сырые dataset/train.csv (8.2 ГБ) и dataset/test.csv (2.2 ГБ) в репозиторий не входят. Их нужно скачать архивом dataset.zip по ссылке https://disk.yandex.ru/d/DiFwlfMOauxjBg и положить в dataset/.

uv sync                  # анализ и классические модели
uv sync --extra fm       # + torch 2.14 (CUDA 13.0), Chronos-2, TimesFM 3.0, t0-beta
cd analysis
uv run python s00_prepare_parquet.py
uv run python s10_forecast.py

Полный порядок запуска всех шагов приведён в docs/analysis/README.md.

Подключённый прогноз v25

Готовый CSV v25 воспроизводится сервисом по умолчанию во всех 14 640 ячейках. Описание сборки и ограничений отделяет конкурсный результат с пока не подтверждённой привязкой к файлу от проверки сервиса. Для запуска Docker обучение и сырые 10 ГБ валидаций не нужны.

Предыдущий прогноз v11

Готовый CSV переобучения не требует: SHA-256 и параметры лежат в JSON рядом. v11 собирается цепочкой раундов. Каждый раунд читает прогноз предыдущего и сам строит свои кэши в data/, сырые 10 ГБ не нужны: раунды берут метки из dataset/labels/.

ШагСкрипты analysis/Результат в forecasts/СкорРасхождение на Python 3.14
Адаптивные профили, LightGBM, сбои Дептрансаs42-s46submission_kaggle_v1_weather.csv0.902360.15 %
Единый ансамбльs47submission_kaggle_unified_v2.csv0.903580.14 %
Дневной уровень, маршрут 5s52-s59submission_daily_route5_v4.csv0.904180.15 %
Почасовые доли, городской трамвайs62-s64submission_shape_facts_v6.csv0.905530.10 %
Форма суток, данные для следующих раундовs66submission_shape50_v7.csv0.905700.04 %
Сезонная модель почасовых долейs80, s81submission_seasonal_v10.csv0.907310.05 %
Распределение объёма между днямиs82, s83submission_seasonal_daily_v11.csv0.907410.04 %

Раунды запускаются в общем окружении uv на Python 3.14: uv sync --extra fm (torch нужен шагам s80-s83), на Windows с режимом UTF-8 PYTHONUTF8=1. Последняя колонка - проверка 26.09.2026 на чистом клоне: каждый шаг запускался от закоммиченного входа, и его файл отличается от оценённого на столько процентов суммы модулей. Шаги v6-v11 сохраняют суммы входа, у них общая сумма совпала точно, у v1-v4 сдвиг не больше 0.04 %. Новые LightGBM и numpy дают немного другие числа, поэтому s47, s59 и s64 на актуальных версиях останавливаются на своих проверках точного совпадения с оценёнными файлами. Побайтно эти файлы повторяются только в окружении, где раунды считались: Python 3.9 и analysis/requirements-round4-py39.txt. Команды, тесты и проверки каждого шага описаны в отчётах раундов, ссылки собраны в docs/ml_handoff.md. Пересчёт v11 под суммы проб уровня на табло собирает analysis/s84_block_probes.py apply в окружении uv, тест tests/test_block_probes.py пересобирает загруженный файл из журнала проб (отчёт).

Сервис

Сервис с интерфейсом, MCP-сервером и Redis запускается из корня одной командой, сырые данные и Python ему не нужны:

cp .env.example .env            # для агента впишите LLM_API_KEY Ollama Cloud; без ключа работает всё, кроме агента
docker compose up -d --build    # интерфейс http://localhost:8080, документация API: /swagger-ui.html

Вход в интерфейс: логин dispatcher, пароль chaspik. Свои учётные записи задаёт AUTH_USERS в .env, подробности в backend/README.md.

Порт интерфейса меняется переменной API_PORT, например API_PORT=8090 docker compose up -d.

Проверка на чистом клоне 26.09.2026 (Windows 11, Docker 28, Intel Core Ultra 5 125H, 32 ГБ), выполненная ещё с v11: сборка всех образов без кеша слоёв заняла 155 с, запуск 26 с. Контейнер api после старта занимал 474 МБ из лимита 2 ГБ. Для v25 27.09.2026 проверены сборка в Podman, запуск API, совпадение 72 живых почасовых ответов с CSV и тесты артефактов.

Расширение шкалы до 31.12.2027: метод, ограничения и проверки.

Felicuss/urban-ridership-forecast

Прогноз пассажиропотока и инструменты диспетчера городского транспорта.

HTML

0

215 commits

updated Sep 27, 2026

See the code

README

Час пик: прогноз загрузки трамвайных маршрутов

Прогнозируем почасовые посадки на 10 трамвайных маршрутах Москвы на ноябрь–декабрь 2025 года по валидациям за январь–октябрь 2025 года. Проект включает модель прогноза и сервис для диспетчера.

Постановка, критерии и сроки лежат в docs/. Анализ данных, внешние факторы и эксперименты описаны в docs/analysis/README.md. Область применимости модели: docs/applicability.md, ограничения и план развития: docs/limitations_and_roadmap.md.

Быстрый старт

Нужен только Docker. Из корня репозитория:

docker compose up -d --build

После сборки (у нас без кеша 3 минуты) интерфейс открывается на http://localhost:8080, вход: логин dispatcher, пароль chaspik. Документация API: http://localhost:8080/swagger-ui.html, кнопка «Authorize» принимает тот же логин и пароль. Помощнику диспетчера нужен ключ LLM_API_KEY в .env (см. .env.example), без него работает всё остальное.

КритерийГде смотреть
Качество прогнозасабмиты в forecasts/, подтверждённые результаты со SHA-256 в реестре, подключённый прогноз - ниже
Внешние данныессылки, лицензии и способ выгрузки - external/README.md, эффект каждого источника - docs/analysis/README.md, в интерфейсе вкладка «Факторы»
Область применимости и поправкиdocs/applicability.md, ползунки с живым пересчётом - вкладка «Сценарий», точность - вкладка «Точность»
Архитектура и производительностьсхема ниже, замеры k6 - backend/README.md: одна реплика на 2 vCPU держит 4000 запросов/с при p95 < 1 мс
Функциональностьгоризонты сутки, неделя, месяц и год; маршрут, остановка, участок и сеть; карта; выгрузка CSV и XLSX кнопкой в верхней строке
Польза для диспетчерараздел ниже, вкладка «Смена» и режим табло
Дополнительные возможностисписок ниже

Что уже сделано

  • Сырые валидации (62.4 млн строк) переведены в parquet. Целевая величина воспроизводится из сырья точно во всех 72 960 ячейках.
  • Подключены и проверены внешние источники: производственный календарь, почасовая погода Open-Meteo, помесячная статистика пассажиропотока и загруженность дорог data.mos.ru, события сети по постам Дептранса, геометрия маршрутов из OpenStreetMap. Ссылки и способ выгрузки: external/README.md.
  • На первом этапе бэктест на 4 фолдах сравнивал baseline организаторов (0.469), медианные профили (до 0.845 в среднем, 0.904 на октябре), LightGBM и foundation-модели Chronos-2, t0-beta, TimesFM 3.0. Лучше всего на том этапе (0.870) работала смесь профиля с городским сезонным уровнем из data.mos.ru и Chronos-2 + t0-beta на дневных суммах.
  • К сервису подключён файл v25, построенный поверх v21 с обновлённым согласованием по результатам платформы. На последнем скриншоте есть скор 0.91274, предположительно для v25; порядок двух последних загрузок ещё не подтверждён. Описание v25/v26, передача ML команде, реестр подтверждённых результатов.
  • Эксперимент v8: обученный на Mac JointDayNet + Chronos-2, дневные суммы v7 сохранены. На шести периодах разработки средний прирост +0.001782; скор платформы 0.90555, на 0.00015 ниже v7. Лучшее решение сохраняем. Обучение, проверки и воспроизведение.
  • v9: графовая модель режимов + STL/ETS и согласование групповых сумм. Скор 0.90543, на 0.00027 ниже v7; исторический прирост не подтвердился. Архитектура, все проверки и ограничения.
  • s85: прозрачная модель уровень × форма с режимами маршрутов из каталога событий. На шести фолдах s85 выше v11 (0.8818 против 0.8658), на табло 0.89761: выигрыш лета–осени зимой не подтвердился. Отчёт.
  • Сервис на Java 25 и Spring Boot 4.1 (WebFlux) отдаёт прогноз из artifacts/ по маршруту, остановке, участку и сети на горизонтах день, месяц и год, пересчитывает его по ползункам и событиям, строит кадры тепловой карты и выгружает CSV и XLSX. Одна реплика на 2 vCPU держит 4000 запросов/с при p95 < 1 мс. Запуск, API и замеры: backend/README.md.
  • Интерфейс диспетчера на React: карта Москвы с тепловой картой посадок по часам, вагонами по расписанию transport.mos.ru (на крупном плане объёмными), метро, 3D-домами и осадками там, где они идут. Любая дата с января 2025 по декабрь 2027 с пометкой «факт», «прогноз» или «оценка», проигрывание времени до ×900, поездка трамвая с посадками на каждой остановке, прогноз по сети, маршруту, остановке и участку, сравнение двух дат на одном графике, советы по интервалам, ползунки сценариев, внешние факторы и качество модели на одном экране. Выгрузка в CSV и XLSX с выбором объекта, периода и шага, фильтр маршрутов и спутниковая подложка. Подробности: frontend/README.md.
  • Вкладка «Смена» для диспетчера: сводка смены одной кнопкой, оповещения на завтра по маршруту или участку, узкие места на неделю вперёд и калькулятор выпуска вагонов. Режим табло на большой экран диспетчерской.
  • Сбои из оперативного канала Дептранса превращаются в события сценария: сервис сам читает t.me/DtOperativno, находит пары «задерживаются трамваи» и «движение восстановлено» и считает потерю посадок по часам. Сбой можно примерить на любой день прогноза.
  • После встречи с организаторами 26.09: вход в сервис по логину и паролю; горизонт «неделя» и пиковый час каждого дня у недели и месяца; маршрут по станциям и дням одной клавишей (S) и номера маршрутов с клавиатуры; сплит и панели виджетов, которые диспетчер расставляет сам; валидации вне часов работы маршрутов (проверки оборудования) отброшены из факта; пропуски в данных 2025 года найдены, у каждого названа причина по постам Дептранса и восстановлены посадки по прошлым неделям; №5 по умолчанию скрыт. Подробности: frontend/README.md, backend/README.md.
  • План и факт по прошедшим дням: на 1 февраля - 31 октября 2025 сервис хранит прогноз, который диспетчер получил бы вечером накануне, и показывает его пунктиром рядом с фактом. Точность плана 0.8912 по часам и 0.9249 по суткам. Как собран план: analysis/export_plan.py.
  • Тур по разделам при первом входе: 11 шагов с подсветкой блоков, повторить можно кнопкой со знаком вопроса в верхней строке.
  • Помощник диспетчера: ReAct-агент на Java в плашке верхней строки, вопросы текстом или голосом. Модель gpt-oss:120b через Ollama Cloud, 15 инструментов MCP-сервера на Python (SDK mcp 2.2.0), история диалога и кэш ответов в Redis. Агент считает, показывает на карте и открывает нужную панель. Подробности: mcp-server/README.md, backend/README.md.
  • Презентация для жюри: presentation/chaspik.html, один HTML-файл на 10 слайдов, открывается без сети. Это снимок прежней версии v11; текущий сервис и его оценка описаны выше. Сборка и данные: presentation/README.md.

Сервис по умолчанию отдаёт прогноз v25 до ячейки: формула профиля домножается на поправку до v25 в каждой ячейке, поэтому ползунки и события работают поверх v25. Как это устроено: artifacts/README.md.

Структура

analysis/           скрипты анализа, экспорта и экспериментов; ML-раунды s41…s83
artifacts/          артефакты модели для сервиса: компоненты прогноза, ползунки, остановки, сеть (s40)
backend/            сервис прогноза на Java: API, сценарии, выгрузка, тесты
frontend/           интерфейс диспетчера на React: карта, время, сценарии, факторы
mcp-server/         MCP-сервер на Python для агента: инструменты поверх API и команды интерфейсу
presentation/       презентация на Remotion: один HTML-файл, исходники и скрипты данных
dataset/            данные организаторов
  README.md         описание датасета
  labels/           почасовые посадки route;date;hour;boardings
  spravochniki/     справочники маршрутов и остановок (xlsx)
  test_submission.csv   шаблон сабмита с baseline
docs/               постановка, критерии, анализ, обзоры
  analysis/         отчёт, графики (figures/) и таблицы результатов (tables/)
  research/         обзоры моделей, похожих решений, внешних источников, событий сети
external/           внешние данные: погода, календарь, data.mos.ru, OSM, события
forecasts/          прогнозы на ноябрь-декабрь в формате сабмита и их коэффициенты
deploy/             nginx перед репликами сервиса и сценарий нагрузочного замера k6
tests/              эталонные тесты артефактов на Python

Архитектура

Модель считается на Python офлайн и отдаёт сервису готовые артефакты. Сервис на Java модель не запускает: при старте он сверяет артефакты с manifest.json по sha256 и пересчитывает прогноз по той же формуле, что и экспорт. Если результат разойдётся с эталоном, сервис не стартует.

flowchart LR
  subgraph PY["Python, офлайн: analysis/"]
    A["Приём и нормализация<br/>s00: 10,4 ГБ CSV в parquet<br/>s03: проверка labels"]
    B["Внешние данные<br/>s01, s08, s33, s41, s42, s43: Open-Meteo,<br/>data.mos.ru, transport.mos.ru, OSM,<br/>посты Дептранса"]
    C["Геопривязка<br/>export_network: остановки справочника<br/>и OSM ближе 40 м, доли посадок"]
    D["Прогноз v25<br/>s41…s121: профили и LightGBM,<br/>оценка неопределённости,<br/>согласование с агрегатами"]
    E["Экспорт s40<br/>artifacts/ и manifest с sha256"]
  end
  subgraph JV["Сервис: backend/, Java 25 + Spring WebFlux"]
    F["infrastructure<br/>загрузка и сверка артефактов"]
    G["domain<br/>формула прогноза, агрегация,<br/>шкала факт, прогноз, оценка"]
    H["application<br/>запросы, сценарии, кэш"]
    I["api<br/>REST /api/v1, OpenAPI,<br/>ошибки RFC 9457, CSV и XLSX"]
  end
  subgraph WEB["Интерфейс: frontend/, React 19"]
    J["карта MapLibre, время,<br/>прогноз, смена, сценарии, табло"]
  end
  subgraph AG["Агент"]
    K["mcp-server/, Python<br/>15 инструментов MCP"]
    L["Ollama Cloud<br/>gpt-oss:120b"]
    R["Redis<br/>история и кэш"]
  end
  T["t.me/DtOperativno<br/>сбои движения"]
  A --> C --> D --> E
  B --> C
  B --> D
  E --> F --> G --> H --> I -->|nginx| J
  B -. погода по дате .-> J
  T -. раз в 10 минут .-> F
  H <-->|ReAct| L
  H <--> K
  K -->|REST| I
  H <--> R

Границы слоёв проверяет ArchitectureTest: domain не зависит от Spring, Reactor, Jackson и других слоёв, application вызывается только из api, к api и infrastructure не обращается ни один слой. Подробности по сервису и замеры нагрузки: backend/README.md.

Польза для диспетчера

Прогноз нужен, чтобы заранее решить, сколько вагонов выпускать на линию и в какие часы. Пример из интерфейса: маршрут 17, пятница 14 ноября 2025. В 8:00 v25 прогнозирует 5 261 посадку при интервале 7 минут по расписанию transport.mos.ru — это около 307 посадок на рейс. «Витязь-М» везёт 185 человек при 5 чел/м² (pk-ts.org).

  • Карточка «Посадок на рейс» делит прогноз маршрута на число рейсов по расписанию и подсвечивает часы выше порога. При пороге 185 сервис советует интервал 4 минуты вместо 6 в 7-10 ч и 4 минуты вместо 7 в 16-20 ч. Порог диспетчер задаёт ползунком.
  • Часы, где на рейс меньше 15 посадок, отмечены отдельно. Там интервал можно увеличить, а вагоны перевести на пиковые часы.
  • Перекрытие, мероприятие или снегопад задаются событием на вкладке «Сценарий», прогноз по сети и по дням пересчитывается сразу.
  • Участок между двумя остановками показывает, где именно на маршруте растёт поток, и выгружается в CSV или XLSX отдельно.

Вкладка «Смена» собирает это в рабочие инструменты:

  • Сводка смены на выбранный день: погода, посадки и пиковый час, разница с тем же днём неделей раньше, три самых загруженных маршрута, тесные часы с советом по интервалу, события дня. Кнопка копирует текст для рабочего чата, вторая просит агента пересказать его.
  • Оповещения: подписка на маршрут или участок с порогом посадок на рейс. Если завтра поток выше порога, подписка краснеет, а на колокольчике в верхней строке появляется число.
  • Узкие места на 7 дней: маршрут, день и часы, где на рейс входит больше людей, чем помещается в вагон, от самых острых, с остановкой, где входят больше всего.
  • Калькулятор выпуска: интервал в выбранные часы превращается в число вагонов на линии, посадки на рейс и вагоно-часы. Рядом те же числа по расписанию.
  • Табло на большой экран: крупные числа по одному маршруту, маршруты сменяются сами каждые 8 секунд, внизу все маршруты сразу, бегущая строка с оповещениями и сбоями.

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

Дополнительные возможности

Сверх обязательного (горизонты день, месяц и год, агрегация по маршруту, остановке, участку и интервалу, карта, выгрузка CSV и XLSX, внешние факторы):

ВозможностьГде в интерфейсе
Сводка смены, оповещения на завтра с порогом посадок на рейс, узкие места на неделю, калькулятор выпуска вагоноввкладка «Смена», колокольчик в верхней строке
Табло на большой экран диспетчерскойкнопка с монитором в верхней строке
Помощник диспетчера: вопросы текстом или голосом, агент считает, показывает на карте и открывает панельплашка «Спросить» в верхней строке, нужен ключ LLM_API_KEY
Сбои из канала «Дептранс. Оперативно» как события сценария, их можно примерить на любой деньвкладка «Сценарий»
Из чего сложился прогноз: вклад каждого шага формулы за сутки, от профиля истории до итога v25карточка на вкладке «Прогноз»
План и факт по прошедшим днямграфик на вкладке «Прогноз» для дат до 31 октября 2025
Сравнение двух дат на одном графике«Сравнить с» под графиком прогноза
Горизонт «неделя», пиковый час каждого дня, самые напряжённые дни месяцавкладка «Прогноз»
Помесячная оценка на 2026–2027 годы с коридоромлюбая дата до 31.12.2027
Поездка трамвая с посадками на каждой остановке«Пустить трамвай» в карточке маршрута
Маршрут по станциям и дням: матрица остановок против дней недели или месяцакнопка в карточке маршрута или клавиша S
Пропуски в данных 2025 года с причиной по постам Дептранса и восстановленными посадкамивкладка «Точность»
Погода на карте там, где идут осадки, свет по времени суток, метро, объёмные дома, спутниккнопки слоёв над картой
Раскладки «сплит» и «панели» с виджетами, которые диспетчер расставляет самзначки в верхней строке или клавиша V
Горячие клавиши: номер маршрута цифрами, день и час стрелкамисписок в настройках по клавише ?
Вход по логину и паролю, Swagger с кнопкой «Authorize»экран входа, /swagger-ui.html
Тур по разделам при первом входекнопка со знаком вопроса в верхней строке

Запуск

Нужен uv. Сырые dataset/train.csv (8.2 ГБ) и dataset/test.csv (2.2 ГБ) в репозиторий не входят. Их нужно скачать архивом dataset.zip по ссылке https://disk.yandex.ru/d/DiFwlfMOauxjBg и положить в dataset/.

uv sync                  # анализ и классические модели
uv sync --extra fm       # + torch 2.14 (CUDA 13.0), Chronos-2, TimesFM 3.0, t0-beta
cd analysis
uv run python s00_prepare_parquet.py
uv run python s10_forecast.py

Полный порядок запуска всех шагов приведён в docs/analysis/README.md.

Подключённый прогноз v25

Готовый CSV v25 воспроизводится сервисом по умолчанию во всех 14 640 ячейках. Описание сборки и ограничений отделяет конкурсный результат с пока не подтверждённой привязкой к файлу от проверки сервиса. Для запуска Docker обучение и сырые 10 ГБ валидаций не нужны.

Предыдущий прогноз v11

Готовый CSV переобучения не требует: SHA-256 и параметры лежат в JSON рядом. v11 собирается цепочкой раундов. Каждый раунд читает прогноз предыдущего и сам строит свои кэши в data/, сырые 10 ГБ не нужны: раунды берут метки из dataset/labels/.

ШагСкрипты analysis/Результат в forecasts/СкорРасхождение на Python 3.14
Адаптивные профили, LightGBM, сбои Дептрансаs42-s46submission_kaggle_v1_weather.csv0.902360.15 %
Единый ансамбльs47submission_kaggle_unified_v2.csv0.903580.14 %
Дневной уровень, маршрут 5s52-s59submission_daily_route5_v4.csv0.904180.15 %
Почасовые доли, городской трамвайs62-s64submission_shape_facts_v6.csv0.905530.10 %
Форма суток, данные для следующих раундовs66submission_shape50_v7.csv0.905700.04 %
Сезонная модель почасовых долейs80, s81submission_seasonal_v10.csv0.907310.05 %
Распределение объёма между днямиs82, s83submission_seasonal_daily_v11.csv0.907410.04 %

Раунды запускаются в общем окружении uv на Python 3.14: uv sync --extra fm (torch нужен шагам s80-s83), на Windows с режимом UTF-8 PYTHONUTF8=1. Последняя колонка - проверка 26.09.2026 на чистом клоне: каждый шаг запускался от закоммиченного входа, и его файл отличается от оценённого на столько процентов суммы модулей. Шаги v6-v11 сохраняют суммы входа, у них общая сумма совпала точно, у v1-v4 сдвиг не больше 0.04 %. Новые LightGBM и numpy дают немного другие числа, поэтому s47, s59 и s64 на актуальных версиях останавливаются на своих проверках точного совпадения с оценёнными файлами. Побайтно эти файлы повторяются только в окружении, где раунды считались: Python 3.9 и analysis/requirements-round4-py39.txt. Команды, тесты и проверки каждого шага описаны в отчётах раундов, ссылки собраны в docs/ml_handoff.md. Пересчёт v11 под суммы проб уровня на табло собирает analysis/s84_block_probes.py apply в окружении uv, тест tests/test_block_probes.py пересобирает загруженный файл из журнала проб (отчёт).

Сервис

Сервис с интерфейсом, MCP-сервером и Redis запускается из корня одной командой, сырые данные и Python ему не нужны:

cp .env.example .env            # для агента впишите LLM_API_KEY Ollama Cloud; без ключа работает всё, кроме агента
docker compose up -d --build    # интерфейс http://localhost:8080, документация API: /swagger-ui.html

Вход в интерфейс: логин dispatcher, пароль chaspik. Свои учётные записи задаёт AUTH_USERS в .env, подробности в backend/README.md.

Порт интерфейса меняется переменной API_PORT, например API_PORT=8090 docker compose up -d.

Проверка на чистом клоне 26.09.2026 (Windows 11, Docker 28, Intel Core Ultra 5 125H, 32 ГБ), выполненная ещё с v11: сборка всех образов без кеша слоёв заняла 155 с, запуск 26 с. Контейнер api после старта занимал 474 МБ из лимита 2 ГБ. Для v25 27.09.2026 проверены сборка в Podman, запуск API, совпадение 72 живых почасовых ответов с CSV и тесты артефактов.

Расширение шкалы до 31.12.2027: метод, ограничения и проверки.