Найти все координаты в круг географических данных в Python

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

существует наивное O (N^2) решение этой проблемы---просто вычислить расстояние всех пар точек. Однако, поскольку я имею дело с правильной метрикой расстояния (географическое расстояние), должен быть более быстрый способ сделать это.

Я хотел бы сделать это в Python. Один решение, которое приходит на ум, - использовать некоторую базу данных (mySQL с ГИС-расширениями, PostGIS) и надеяться, что такая база данных позаботится об эффективном выполнении описанной выше операции с использованием некоторого индекса. Я бы предпочел что-то более простое, хотя это не требует от меня строить и изучать такие технологии.

пара очков

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

положите в терминах кода python, я хочу что-то вроде:

points = [(lat1, long1), (lat2, long2) ... ] # this list contains millions lat/long tuples
points_index = magical_indexer(points)
neighbors = []
for point in points:
    point_neighbors = points_index.get_points_within(point, 200) # get all points within 200 meters of point
    neighbors.append(point_neighbors) 

2 ответов


опрокинутый Eamon, я придумал простое решение с использованием btrees, реализованных в SciPy.

from scipy.spatial import cKDTree
from scipy import inf

max_distance = 0.0001 # Assuming lats and longs are in decimal degrees, this corresponds to 11.1 meters
points = [(lat1, long1), (lat2, long2) ... ]
tree = cKDTree(points)

point_neighbors_list = [] # Put the neighbors of each point here

for point in points:
    distances, indices = tree.query(point, len(points), p=2, distance_upper_bound=max_distance)
    point_neighbors = []
    for index, distance in zip(indices, distances):
        if distance == inf:
            break
        point_neighbors.append(points[index])
    point_neighbors_list.append(point_neighbors)

scipy

во-первых, во-первых: существуют существующие алгоритмы для выполнения таких вещей, как к-Д Дерево. Scipy имеет реализацию python cKDtree что можно найти все точки в заданном диапазоне.

Бинарный Поиск

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

  1. вычислить PCA набора данных. Вы хотите повернуть набор данных так, чтобы наиболее значительным направлением было первое, а ортогональным (менее большим) вторым направлением было, ну, второе. Вы можете пропустить это и просто выбрать X или Y, но это вычислительно дешево и обычно легко реализовать. Если вы просто выберете X или Y, выберите направление с большей дисперсией.
  2. сортировка точек по основным направление (назовем это направление X).
  3. чтобы найти ближайшего соседа данной точки, найдите индекс ближайшей точки в X с помощью двоичного поиска (если точка уже находится в вашей коллекции, вы можете уже знать этот индекс и не нуждаетесь в поиске). Итеративно посмотрите на следующие и предыдущие точки, сохраняя лучшее совпадение до сих пор и его расстояние от вашей точки поиска. Вы можете перестать смотреть, когда разница в X больше или равна расстоянию до лучшего соответствия, поэтому далеко (на практике, как правило, очень мало точек).
  4. чтобы найти все точки в заданном диапазоне, сделайте то же самое, что и Шаг 3, за исключением того, что не останавливайтесь, пока разница в X не превысит диапазон.

эффективно, вы делаете o(n log(N)) предварительную обработку, и для каждой точки примерно o(sqrt (N)) - или, если распределение ваших очков плохое. Если точки распределены примерно равномерно, то число точек, расположенных ближе к X, чем ближайший сосед, будет равно это менее эффективно, если много точек находятся в пределах вашего диапазона, но никогда не намного хуже, чем грубая сила.

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

Делоне триангуляция

другая идея: a Делоне триангуляция может работать. Для триангуляции Делони задано, что ближайшим соседом любой точки является соседний узел. Интуиция заключается в том, что во время поиска вы можете поддерживать кучу (очередь приоритетов) на основе абсолютного расстояния от точки запроса. Выберите ближайшую точку, проверьте, что она находится в диапазоне, и если да, добавьте всех своих соседей. Я!--23-->подозреваемый что невозможно пропустить какие-либо моменты, как это, но вы должны смотреть на него более внимательно, чтобы быть уверенным...