Разработка транспортных систем. Ч1 (GTFS, остановки)
Предуведомление
Когда к нам приходят стажеры и хотят стать backendом, я сначала даю им книгу «Phoenix in Action» (мы пишем на Elixir), чтобы они узнали основы языка и фреймворка, затем я показываю то что будет далее в статье (цикле статей). Хороший стажер проходит обучение от двух до четырех недель. Проходит обучение = ему можно дать простую задачу из реального проекта, и он хотя бы поймет что от него хотят.
Несмотря на то, что речь будет идти про программирование на Elixir, решать те же задачи можно на любом другом языке, или даже без программирования вообще, например с использованием любой подходящей софтины.
Проектным менеджерам, бизнес-аналитикам, дизайнерам и тд тоже будет полезно, тк во многом это обучение не программированию, а системному подходу.
GTFS
В Санкт-Петербурге каждый день около 3:00 тут, выкладывают маршрутную сеть в формате GTFS. GTFS это zip архив с набором файлов в формате csv. (Кроме gtfs фида там можно получить текущие позиции транспортных средств (далее ТС), но этим будем заниматься сильно позднее)
При разархивации видим такой набор файлов:

Мы будем разрабатывать систему, оперирующую втч этими данными, для начала нужно эти данные проанализировать.
Самый интуитивно понятный файл — stops.txt:

Формат csv (сomma separated values) — в первой строке через запятую наименования атрибутов, в последующих строках через запятую значения атрибутов каждой остановки.
Одна строка = одна остановка, если значение атрибута содержит запятую, то значение целиком экранируется двойными кавычками (Например …,"ПР. АВИАКОНСТРУКТОРОВ, 49», …)
Если значение атрибута содержит двойные кавычки, то каждая двойная кавычка заменяется на две (Например …,"Ст. метро ""Приморская""", …)
Первая остановка выглядит так:

По этим двум картинкам можно заметить некоторые моменты:
- stop_id это как-будто то же самое что stop_code
- stop_name где-то капсом, а где-то нет
- не понятно что такое location_type и wheelchair_boarding
Вопросы
Посмотрев файл остановок сразу возникают следующие вопросы:
- Сколько всего остановок?
- Везде ли stop_id уникален?
- Везде ли stop_id == stop_code?
- Везде ли stop_name уникален?
- Сколько будет уникальных stop_name если привести к регистру?
- Везде ли координаты уникальны?
- Что такое location_type и какие значение он принимает?
- Что такое wheelchair_boarding и какие значение он принимает?
- Какие бывают transport_type и как часто они встречаются?
- Неплохо было бы посмотреть остановки на карте, мб будут какие-то аномалии/доп соображения.
Чтобы ответить на эти вопросы, я воспользуюсь эликсиром (но можно использовать python, excel, tableau и тд)
Во первых я прочитаю файл и сложу остановки в список объектов:

Код выше простейший, но есть несколько нюансов:
- заголовки в файле имеют префикс stop_ (stop_name, stop_code и тд), я вместо этого использую просто name, code и тд, тк объект уже является остановкой (stop), мусорный префикс осложнит читаемость
- в строках 26-27 есть перенос скобки на следующую строку, это не по стандарту форматирования, но так проще вставлять в консоль, без этого выполнение будет построчно, а не целым блоком
- координаты складываю сразу в один атрибут, вместо отдельных как в исходном файле, причем сначала долготу, потом широту, согласно стандарту GeoJSON
- на 27 строке CommaParser это модуль, объявленный через библиотеку nimble_csv, вместо например библиотеки csv, nimble лучше
Далее выполняю в консоли код для ответа на первые 5 вопросов:

- Остановок всего 8 648
- stop_id везде уникален, а значит в будущем может быть использован как идентификатор
- stop_id везде равен stop_code, а значит можно оставить что-то одно, а второй атрибут выкинуть
- stop_name не уникален, уникальных наименований всего 4 319, а всего остановок 8 648
- Если привести к нижнему регистру, то уникальных наименований станет еще меньше — 3 678
После ответа на пятый вопрос возникают дополнительные:
5.1. Дублирующиеся наименования остановок дублируются парами? или тройками? или все 3600 уникальных, а какое-то одно значение дублируется 5000 раз?
Отвечаем:

Самый частый дубль — Проспект Луначарского, 22 раза, а «Дунайский» и «универсам» дублируются например по 17 раз. Возникает доп вопрос:
5.2. Как распределены частоты дублирования? Например как много остановок имеет больше десяти дублей? а больше двух?
Отвечаем:

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

Две остановки в разных направлениях, обе называются Рижский проспект. Проспект луначарского и его 22 дубля на яндексе выглядит так:

На карте 7 остановок, но яндекс объединяет автобусные, тролейбусные и тд остановки в одну.
Ответ на шестой вопрос:

Координаты в высокой степени уникальны, всего 17 дублей (8648 — 8631), и все 17 раз это дубли парами: автобусная + троллейбусная в одном месте.
Ответы на вопросы 7, 8, 9:

Ответ на седьмой вопрос — location_type везде принимает значение 0, а значит без разницы что это такое, это мусорный атрибут который можно забыть.
Ответ на восьмой — wheelchair_boarding почти принимает значение 2, ровно одно исключение — автобусная остановка «школа № 478». Если найти ее на карте и посмотреть панораму, то никаких отличий нет, видимо атрибут такой-же мусорный как и location_type.
Ответ на девятый — остановки бывают автобусные, троллейбусные и трамвайные, их соответственно 6 361, 1 372 и 915. В данном случае это не слишком важно, но в целом стоит учитывать, что люди плохо воспринимают соотношения величин смотря на цифры, поэтому тут не помешает простенькая визуализация:

Для десятого тезиса («посмотреть остановки на карте»), есть довольно неэффективный, но самый доступный для стажера способ — сервис geojson.io. Ставим точку в случайное место, и смотрим какой формат текста для этого требуется:

Генерируем такой текст:

Вставляем на сайте, получаем интерактивную карту:

Способ неэффективный, карта сильно лагает даже на моем маке, правильные инструменты анализа пространственных данных это тема отдельной статьи. В нашем случае каких-то очевидных аномалий на карте нет (например нет остановок на воде/вне дорог, или отдельно стоящей трамвайной остановки и т.д).
Итоги
Выше пример анализа одного из десятка файлов, представляющих собой маршрутную сеть общественного транспорта спб. Далее обычно я даю стажеру задачу поставить самому себе вопросы по файлам routes, trips и stoptimes, а потом ответить на них. Основное обучение происходит когда стажер показывает вопросы, которые он придумал, и как он на них ответил, и получает правки о том какие еще вопросы должны быть, и как эффективнее получать ответы.