Анализ близости для поиска самого ближайшего бара с использованием Python

Анализ близости с использованием Python для поиска самого ближайшего бара

Несколько слов о обработке пространственных данных

Изображение-превью (от автора)

Отказ от ответственности: в этой статье мы продемонстрируем все подходы с использованием библиотеки с открытым исходным кодом estaty. Эта библиотека появилась из-за желания формализовать алгоритмы, которые мы используем в нашей работе, в инструмент, который будет доступен другим разработчикам. Другими словами, эту статью написал поддерживающий библиотеку.

Сегодня мы хотим продолжить обсуждение темы обработки пространственных данных с использованием Python-библиотеки с открытым исходным кодом. Мы уже говорили о том, как комбинировать данные Open Street Map и открытые данные Landsat для проверки зон зелени рядом с объектами недвижимости.

Теперь давайте рассмотрим другой тип анализа: анализ близости или доступности (или доступности) некоторых полезных объектов, таких как парки, больницы, детские сады и т. д. (Рисунок 1).

Рисунок 1. Схематическое изображение подхода к анализу близости (давайте представим, что здесь речь идет о парикмахерских, например) (изображение от автора)

Немного о анализе близости

Предлагаем начать рассказ с краткого обзора литературы. Мне нравится статья “Use-Cases геопространственного анализа, о которых должен знать каждый дата-саентист” (обратите внимание – эта история доступна только для участников) , которая довольно легко вводит читателя в предмет анализа географической информации.

Статьи “Создание поверхности дистанции стоимости для измерения доступа к паркам” и “Растущий штат Юта: анализ близости жителей Юты к медицинским учреждениям” также показывают довольно ясно, какие характеристики в городах могут вычислить инженеры, чтобы выполнить такие анализы. Тема довольно популярна, и было разработано много как научных статей, так и практических приложений – присоединяйтесь.

Используемая библиотека (that we use)

Теперь перейдем к более конкретному разговору: инструменты, которые можно использовать для анализа близости (в дополнение к географическим информационным системам, таким как QGIS или ArcGIS, и проприетарным сервисам в качестве конечного пользователя продукта).

Мы, в качестве разработчиков, хотели бы анализировать неагрегированные данные, а исходные значения с помощью удобных библиотек или сервисов (желательно с открытым исходным кодом, конечно). Мы не нашли никаких подходящих инструментов для нас, поэтому мы разрабатываем и используем нашу собственную библиотеку с открытым исходным кодом, написанную на Python. Она называется estaty и, если хотите, вы можете посмотреть страницу документации после прочтения статьи:

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

estaty может использовать различные источники данных для выполнения вычислений, но открытые примеры (чтобы они были действительно открытыми) основаны на данных OpenStreetMap. В данной статье мы хотим показать, как мы можем провести достаточно быструю оценку взаимного резерва и в случае необходимости масштабировать подход к новым источникам данных.

Анализ (который мы можем выполнить)

Сначала установите библиотеку с помощью команды

pip install estaty==0.1.0

или, если вы используете poetry:

poetry add estaty==0.1.0

Теперь мы готовы использовать версию 0.1.0

Давайте начнем реализацию. Мы собираемся выполнить близостный анализ для следующего адреса: «Берлин, Нойштедтисхе Кирхштрассе 4–7» — координаты {latitude: 52.5171411, longitude: 13.3857187}. Мы проанализируем окрестности этого объекта в радиусе 2 км.

Чтобы узнать, сколько времени нам потребуется, чтобы добраться до ближайшего бара (согласно OSM — стоит упомянуть здесь), давайте напишем и выполним следующий код:

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

Рисунок 2. Пространственный конвейер обработки данных для расчета расстояний маршрутов до баров с использованием данных OpenStreetMap (изображение от автора)

Давайте подробнее рассмотрим, что происходит «под капотом». Начнем с того, что архитектура estaty построена на 5 абстрактных слоях (в случае с указанным выше конвейером используются только три типа узлов):

  • DataSource — загружает данные из необходимого источника, приводит данные к общему стандарту (вектор и растровый). Независимо от того, каким был источник, выход из этого узла будет только в двух возможных вариантах, что позволяет унифицировать их последующую обработку;
  • Preprocessor — предварительная обработка векторных или растровых данных, например, присвоение новой системы координат (CRS);
  • Merger — объединение данных требуемым способом. Возможные комбинации: векторные данные с векторными данными, растровые данные с растровыми данными и векторные данные с растровыми данными;
  • Analyzer — ядро библиотеки — использует простые атомарные преобразования растровых и векторных объектов для выполнения некоторого анализа, такого как сопоставление площадей или поиск пути;
  • Report — необязательный финальный модуль анализа, который позволяет генерировать отчет в удобочитаемом формате для пользователя.

Комбинируя эти узлы определенным образом, можно составлять различные конвейеры для анализа данных, не изменяя способы обработки исходных данных — только заменой исходных источников или способов анализа (см. Анимацию 1).

Анимация 1. Гибкость библиотеки при подготовке конвейера для анализа (анимация от автора)

Таким образом, на Рисунке 2 показано, что в анализе используются только три узла — это достаточно простой конвейер. В результате его работы мы получим геопандасовскую GeoDataFrame (таблицу с геометрией объектов) с линейными объектами — маршрутами к нашим объектам интереса. Визуализация результата, кстати, будет выглядеть следующим образом:

Рисунок 3. Маршруты к барам с расчетом расстояний по алгоритму (изображение от автора)

У нас есть право делать все, что хотим с полученным набором линейных объектов — например, найти расстояние до ближайшего бара или запросить среднее значение из выборки (что делается в коде):

  • Минимальная длина: 308,93 м
  • Средняя длина: 2306,49 м

Как настоящий эксперт по размещению баров в центрах больших городов, могу сказать, что баров в зоне анализа в реальности может быть намного больше. Но мы договорились с самого начала, что будем анализировать на основе данных OSM, так что…

Сам конвейер независим от источника данных, на основе которых выполняется анализ. Это обеспечивается слабой взаимосвязанностью модулей библиотеки. Начнем с узла DataSource – в нем встроен механизм преобразования данных к одному типу (например, геопандасовские векторные данные здесь будут в формате геофрейма и всегда будут только тремя типами, независимо от того, какой источник мы использовали для этого, все они будут преобразованы в точки, линии или полигоны). Затем идет предварительный процессор, который автоматически определит подходящую метрическую проекцию для наших данных. Единственное, что важно предварительному процессору – это то, что данные должны поступать от источника данных узла, остальное должно быть обработано узлом перед ним. И наша простая конвейерная линия завершается блоком анализа, который будет работать с любыми геометрическими объектами, независимо от того, являются ли они площадной, линейной или точечной формой.

Таким образом, мы можем делать точно такие же вычисления для других категорий данных, которые мы извлечем из OSM (конечно, список не ограничивается только в OSM), например, анализ близости для школ, парков, водных объектов, общественных туалетов, контейнеров для мусора, полицейских участков и т.д. (для типов объектов, которые могут быть определены по тегам, см. страницу википедии особенности карт или в документации estaty о источниках данных):

Рисунок 5. Анализ близости для школ, парков, водных объектов, контейнеров для мусора, полицейских участков (изображение автора)

Как можно масштабировать подход

Мы упомянули, что пространственные данные любой природы могут быть проанализированы: социальные данные, природные объекты или искусственные конструкции. Давайте посмотрим еще глубже в исходный код. Из рисунков видно, что расчет расстояния выполняется на основе маршрутов, построенных с учетом дорог, что разумно, так как обычно мы не летим в бары на вертолетах, а идем пешком. Таким образом, “расстояние” в анализе включает поиск маршрутов на графе дорог, используя граф из OSM. Итак, алгоритм ищет маршрут по следующим абстракциям (мы опирались на следующую статью, которую настоятельно рекомендуем — OSMnx: Python для уличных сетей):

Рисунок 6. Граф пеших дорог, используемый для поиска оптимальных маршрутов (изображение автора)

Граф здесь представляет собой географически определенные узлы и ребра (имеющие атрибуты широты и долготы). Чтобы найти кратчайший маршрут от объекта A к объекту B, необходимо выполнить три шага:

  1. Найти ближайший узел на графе к объекту A (назовем его узел 1)
  2. Найти ближайший узел на графе к объекту B (назовем его узел 2)
  3. С помощью алгоритма обхода графа найти оптимальный маршрут от узла 1 к узлу 2

Если мы хотим получить расстояние, необходимо сложить длину маршрута от узла 1 до узла 2 с расстояниями от объекта A до узла 1 и от объекта B до узла 2. Последние два компонента могут быть рассчитаны на прямой линии, так как сеть с узлами достаточно плотная. Но здесь есть сложность — если объекты A и B являются точками, относительно легко найти ближайшие к ним узлы (вычислить расстояния между точками). Если геометрия объекта A, например, является областью, то нам придется искать “ближайший” узел к полигону. Что делать в случае линейного объекта, также было проблемой для решения

Теперь давайте ответим на вопрос: “Как рассчитать расстояния от узлов графа до векторных объектов разных типов: линий, точек и полигонов единым способом?” Для этой цели библиотека преобразует объекты в тип точек (расстояние между точками очень легко найти) перед началом расчета маршрутов. Мы называем такие преобразования представлениями (Рисунок 7).

Рисунок 7. Некоторые способы представления векторных объектов (изображение автора)

То есть, вместо того, чтобы писать и поддерживать три алгоритма, мы пишем один (поиск маршрутов до точек), а затем сокращаем все исходные типы данных до одного. Как их свести к одному типу – вопрос неоднозначный. Мы могли бы сделать это просто, рассчитав центроиды. Однако, в случае расчета в городских районах и особенно для больших объектов (например, парков), поиск маршрутов до центроидов может быть неуместным – мы считаем, что мы достигли парка не тогда, когда находимся в его центре, а когда делаем первый шаг с асфальтированной дороги в приятную темную чащу парка.

Итак, мы решили использовать подход, при котором мы находим ближайшую точку полигона или линейного объекта к объекту анализа (от которого строятся маршруты). Затем эта точка считается конечной точкой назначения – именно к этой точке будет построен дальнейший маршрут через граф.

Более сложный анализ

Путем маршрутизации до баров никого не удивишь. Давайте усложним задачу. Предположим, что мы хотим провести более сложный анализ, объединяя данные из разных источников. Например, мы хотим рассчитать, сколько в среднем нам нужно идти до парков с дубами (мы любим гулять после баров и любуемся парками с деревьями). Для этого мы объединим данные из двух источников:

  • Загрузим векторные данные из GBIF | Глобальный информационный центр о биоразнообразии (проверьте https://www.gbif.org/, актуально на 20 октября 2023 года)
  • OpenStreetMap – нативная интеграция загрузки данных в библиотеке

Данные GBIF – это векторный слой с точечными геометриями, которые показывают местоположения, где были найдены определенные виды растений и животных (и что-то еще). В данном случае нас будет интересовать Дуб (или Quercus robur по латыни). Вот как будут выглядеть наши данные при наложении слоя точек с дубами на верхнюю часть полигонов парков:

Рисунок 8. Комбинирование данных о парках из OSM и данных GBIF для расчета маршрутов к паркам с дубами (изображение автора)

Код для анализа выглядит следующим образом:

Важно отметить, что точки не обязательно должны находиться внутри полигона парка. Мы объединяем данные из разных источников, и они могут немного отличаться. Поэтому мы устанавливаем буферное расстояние в 10 метров и смотрим на результат (Рисунок 9) – будем считать, что в парке есть дубовое дерево, если полигон парка пересекается с полигоном буфера согласно данным GBIF:

Рисунок 9. Маршруты до парков, где встречаются дубовые деревья, с указанием расстояний (изображение автора)

Немного подробнее о актуальности

Тема анализа является востребованной и очевидной. Следовательно, например, функциональность маршрутизации доступна во многих решениях исходного кода (при подготовке этой статьи я наткнулся на интересную записную книжку “Exploring routing tools for calculating paths for a set of origin-destination pairs”), а также в популярных пользовательских сервисах, таких как Google Maps, и связанных с картами сервисах. Если говорить о анализе близости, то здесь тоже существует множество реализованных решений. К примеру, мы можем упомянуть такой инструмент, как pandana.

Все эти инструменты, конечно, полезны, но для использования их в примерах приложений требуется написание вспомогательного кода. Мы пытаемся создать удобный инструмент для нашей команды и хотим поделиться нашими находками с сообществом. В представленном в этой статье подходе можно упомянуть подход, независимый от источника данных, и гибкий способ совмещения данных. Мы будем рады получить комментарии и предложения.

Полезные ссылки:

  • https://github.com/red5ai/estaty — открытая библиотека на Python для обработки пространственных данных и подготовки прототипных алгоритмов для анализа собственности. Библиотека довольно новая, но мы планируем ее развивать и улучшать;
  • https://estaty.readthedocs.io/en/latest/ — страница документации;
  • https://github.com/red5ai/estaty_examples — открытый репозиторий с примерами запуска для этой статьи

Наборы данных, использованные в этом посте (& лицензии):

  1. Данные карт, защищенные авторским правом участников OpenStreetMap и доступные по адресу https://www.openstreetmap.org Лицензия — Open Data Commons Open Database LicenseСсылки актуальны на 25 октября 2023 года
  2. GBIF.org (20 октября 2023 г.) Загрузка GBIF Occurrence https://doi.org/10.15468/dl.f487j5 Лицензия — Attribution-NonCommercial 4.0 InternationalСсылки актуальны на 25 октября 2023 года

Историю о проведении анализа близости с использованием данных OpenStreetMap и других вам представил Михаил Сарафанов и команда Wiredhut