Как использовать векторизацию с массивами NumPy для расчета геодезического расстояния с использованием библиотеки Geopy для большого набора данных? - PullRequest
0 голосов
/ 10 мая 2018

Я пытаюсь вычислить геодезическое расстояние от кадра данных, который состоит из четырех столбцов данных широты и долготы с приблизительно 3 миллионами строк. Для этого я применил лямбда-метод apply, но это заняло 18 минут. Есть ли способ использовать векторизацию с массивами NumPy для ускорения расчетов? Спасибо за ответ.

Мой код с использованием метода apply и lambda:

from geopy import distance

df['geo_dist'] = df.apply(lambda x: distance.distance(
                              (x['start_latitude'], x['start_longitude']),
                              (x['end_latitude'], x['end_longitude'])).miles, axis=1)

Обновление:

Я пытаюсь этот код, но он дает мне ошибку: ValueError: Значение истинности массива с более чем одним элементом неоднозначно. Используйте a.any () или a.all (). Ценю, если кто-нибудь может помочь.

df['geo_dist'] = distance.distance(
                          (df['start_latitude'].values, df['start_longitude'].values),
                          (df['end_latitude'].values, df['end_longitude'].values)).miles

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

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

В частности, оно имеетметод для вычисления расстояния между наборами точек в GeoSeries, который может быть столбцом GeoDataFrame. Я вполне уверен, что этот метод использует numexpr для векторизации.

Это должно выглядеть примерно так: вы конвертируете свой фрейм данных в GeoDataFrame с (как минимум) двумя столбцами GeoSeries, которые можно использовать для исходного и конечного пунктов назначения. Это должно вернуть объект GeoSeries:

import pandas as pd
import geopandas as gpd
from shapely.geometry import Point

geometry = [Point(xy) for xy in zip(df.longitude, df.latitude)]
gdf = gpd.GeoDataFrame(df, crs={'init': 'epsg:4326'}, geometry=geometry)

distances = gdf.geometry.distance(gdf.destination_geometry)
0 голосов
/ 11 мая 2018

Ответ на ваш вопрос: Вы не можете делать то, что хотите делать с geopy. Я не знаком с этим пакетом, но трассировка ошибок показывает, что эта функция и, возможно, все другие функции в этом пакете не были написаны / разработаны с учетом векторизованных вычислений.

Теперь, если вы можете делать с расстояниями большого круга, я бы посоветовал вам поэкспериментировать с пакетом astropy.coordinates, чтобы можно было вычислять separations между точками векторным способом.

Вот пример, основанный на моем ответе на другой вопрос: Поиск ближайшей точки :

from astropy.units import Quantity
from astropy.coordinates import SkyCoord, EarthLocation
from astropy.constants import R_earth
import numpy as np

lon1 = Quantity([-71.312796, -87.645307, -87.640426, -87.635513,
                 -87.630629, -87.625793 ], unit='deg')
lat1 = Quantity([41.49008, 41.894577, 41.894647, 41.894713,
                 41.894768, 41.894830], unit='deg')
lon2 = Quantity([-81.695391, -87.645307 + 0.5, -87.640426, -87.635513 - 0.5,
                 -87.630629 + 1.0, -87.625793 - 1.0], unit='deg')
lat2 = Quantity([41.499498, 41.894577 - 0.5, 41.894647, 41.894713 - 0.5,
                 41.894768 - 1.0, 41.894830 + 1.0], unit='deg')

pts1 = SkyCoord(EarthLocation.from_geodetic(lon1, lat1, height=R_earth).itrs, frame='itrs')
pts2 = SkyCoord(EarthLocation.from_geodetic(lon2, lat2, height=R_earth).itrs, frame='itrs')

Тогда расстояния между двумя наборами точек можно вычислить как:

>>> dist = pts2.separation(pts1)
>>> print(dist)
<Angle [ 7.78350849, 0.62435354, 0., 0.62435308, 1.25039805, 1.24353876] deg>

Приблизительное преобразование в расстояние:

>>> np.deg2rad(pts2.separation(pts1)) * R_earth / u.rad
<Quantity [ 866451.17527216,  69502.31527953,      0.        ,
             69502.26348614, 139192.86680148, 138429.29874024] m>

Сравните первое значение с тем, что вы получите из примера geopy:

>>> distance.distance((41.49008, -71.312796), (41.499498, -81.695391)).meters
866455.4329098687

РЕДАКТИРОВАТЬ: На самом деле, вполне возможно, что это может дать вам геодезическое расстояние, что вы после, но обязательно проверьте описание EarthLocation.

...