Необходимо вычислить расстояния между каждой из 60 000 координат

Я работаю над корреляционным исследованием на Python, для которого требуется матрица расстояний между каждой парой координат в наборе данных с 60 000 точками данных. Я попытался векторизовать и использовать геопанды, но проблема с геопандами заключается в том, что для запуска функции расстояния мне нужны данные x и y в повторяющихся списках (данные x повторяют набор из 60 000 башен, а данные y повторяют каждую координату 60 000 раз). подряд), делая каждый список длиной 3,6e9 значений, и на моем компьютере заканчивается память до того, как это будет завершено, или когда я пытаюсь запустить его на удаленном рабочем столе в моей школе, это занимает больше получаса, и у меня нет Мне не удалось его успешно запустить. Вот код, который я запускаю:

#Florida Tower Matrix 
#take coordinates of Florida towers
#CHECK THE LAT/LONG order 
import geojson
import pandas as pd
import geopandas as gpd
from shapely.geometry import Point
features = []
with open("/Users/katcn/Desktop/Spring 2024/Research/PleaseWork.geojson") as f:
   gj = geojson.load(f)

for i in range(59629):
   features.append(gj['features'][i]["geometry"]['coordinates'])


#OR make the X matrix all in one column 
#make the Y matrix repeat each value 59000 times 
longitude = []
latitude = []
for i in range(len(features)):
   for j in range(len(features)):
       longitude.append(features[j][0])
   for k in range(len(features)):
       latitude.append(features[i][0])


dict = {"longitude" : longitude, "latitude" : latitude}
df = pd.DataFrame(dict)
dict2 = {"longitude" : longitude, "latitude" : latitude}
df2 = pd.DataFrame(dict2)
#calculate distance between two towers 
geometry = [Point(xy) for xy in zip(df.longitude, df.latitude)]
gdf = gpd.GeoDataFrame(df, crs = {'init': 'epsg:4326'}, geometry=geometry)

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

distances = gdf.geometry.distance(gdf2.geometry)

print(distances)

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

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
110
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Для этого вам на самом деле не нужно использовать функцию расстояния geopandas. По сути, единственное, что вам нужно, это scipy. Начните с помещения ваших координат в массив:

import numpy as np
import pandas as pd
import geojson
from scipy.spatial import distance

with open("/Users/katcn/Desktop/Spring 2024/Research/PleaseWork.geojson") as f:
    gj = geojson.load(f)

coords = np.array([feature["geometry"]["coordinates"] for feature in gj['features']])
dist_matrix = distance.cdist(coords, coords, 'euclidean')
dist_df = pd.DataFrame(dist_matrix)
print(dist_df)

Поскольку вы не предоставили данные, я создал образец набора данных с точками континентальной части США (здесь 60 000 точек от KKey West, Флорида до канадской границы и от Западного побережья до Восточного побережья):

import numpy as np
import pandas as pd
from scipy.spatial import distance
import time

np.random.seed(42)


latitudes = np.random.uniform(low=25.0, high=49.0, size=60000)  
longitudes = np.random.uniform(low=-125.0, high=-66.0, size=60000) 
coords = np.column_stack((latitudes, longitudes))

start_time = time.time()

dist_matrix = distance.cdist(coords, coords, 'euclidean')

end_time = time.time()
elapsed_time = end_time - start_time

print("Distance matrix computed in {:.2f} seconds".format(elapsed_time))
print("Shape of the distance matrix:", dist_matrix.shape)

Какие взять

Distance matrix computed in 185.64 seconds
Shape of the distance matrix: (60000, 60000)

Итак, примерно 3 минуты.

ОБНОВЛЕНИЕ: ИСПОЛЬЗОВАНИЕ ГЕОДЕЗИЧЕСКОГО РАССТОЯНИЯ

Это работает, но требует времени:

import numpy as np
import pandas as pd
from geopy.distance import geodesic
import time

np.random.seed(42)

latitudes = np.random.uniform(low=25.0, high=49.0, size=60000)
longitudes = np.random.uniform(low=-125.0, high=-66.0, size=60000)

coords = list(zip(latitudes, longitudes))

def geodesic_distance_matrix(coords):
    n = len(coords)
    dist_matrix = np.zeros((n, n))
    for i in range(n):
        for j in range(i+1, n): 
            dist_matrix[i, j] = geodesic(coords[i], coords[j]).meters
            dist_matrix[j, i] = dist_matrix[i, j]
    return dist_matrix

start_time = time.time()

dist_matrix = geodesic_distance_matrix(coords)

end_time = time.time()
elapsed_time = end_time - start_time

print("Distance matrix computed in {:.2f} seconds".format(elapsed_time))
print("Shape of the distance matrix:", dist_matrix.shape)

Другой подход — использовать haversine следующим образом:

import numpy as np
import pandas as pd
import time

def haversine(lat1, lon1, lat2, lon2):
    lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2])

    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2
    c = 2 * np.arcsin(np.sqrt(a)) 
    r = 6371  
    return c * r

np.random.seed(42)
latitudes = np.random.uniform(low=25.0, high=49.0, size=60000)
longitudes = np.random.uniform(low=-125.0, high=-66.0, size=60000)
coords = np.column_stack((latitudes, longitudes))

start_time = time.time()

n = coords.shape[0]
dist_matrix = np.zeros((n, n))
for i in range(n):
    dist_matrix[i, :] = haversine(coords[i, 0], coords[i, 1], coords[:, 0], coords[:, 1])

end_time = time.time()
elapsed_time = end_time - start_time

print("Distance matrix computed in {:.2f} seconds".format(elapsed_time))
print("Shape of the distance matrix:", dist_matrix.shape)

который возвращает

Distance matrix computed in 463.75 seconds
Shape of the distance matrix: (60000, 60000)

то есть более чем в два раза больше времени евклидова расстояния.

array([[   0.        , 1683.78776508, 1717.22879531, ..., 1706.77761313,
        3857.52001651, 1579.36733204],
       [1683.78776508,    0.        , 2008.58039812, ..., 2813.5398641 ,
        4379.29512353, 2340.45973263],
       [1717.22879531, 2008.58039812,    0.        , ..., 1151.61134582,
        2399.40193517,  560.14519709],
       ...,
       [1706.77761313, 2813.5398641 , 1151.61134582, ...,    0.        ,
        2244.42005933,  593.632848  ],
       [3857.52001651, 4379.29512353, 2399.40193517, ..., 2244.42005933,
           0.        , 2291.13872501],
       [1579.36733204, 2340.45973263,  560.14519709, ...,  593.632848  ,
        2291.13872501,    0.        ]])

здесь размерность составляет километры. Если вам нужны мили, вам придется поменять

 r = 6371 

к

r = 3956

Это прекрасно работает и у меня работает примерно за минуту. Мне интересно, поскольку мне нужны выходные данные в метрах, а координаты - широта/длина, следует ли мне использовать различные расчеты расстояний, которые предлагает Scipy, для получения более точного результата, чем евклидово? Знаете ли вы, каковы единицы евклидова результата?

Kat Neumann 14.04.2024 20:05

Вы имеете в виду геодезическое расстояние?

Serge de Gosson de Varennes 14.04.2024 20:10

Это вполне возможно, но сама природа геодезического расстояния означает, что при наличии 60 000 точек вам придется сделать 3,6 миллиарда сравнений. Позвольте мне рассмотреть другой способ.

Serge de Gosson de Varennes 14.04.2024 20:19

Да, я имею в виду геодезическое расстояние, извините, поскольку координаты относятся к опорам ЛЭП во Флориде, я думаю, что геодезические будут лучше, чем евклидовы.

Kat Neumann 14.04.2024 20:20

@KatNeumann Проверьте обновления.

Serge de Gosson de Varennes 14.04.2024 20:36

Если это то, на что вы надеялись, не забудьте пометить ответ как принятый, чтобы он был удален из списка вопросов без ответов.

Serge de Gosson de Varennes 14.04.2024 20:54

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

Kat Neumann 15.04.2024 01:00

@KatNeumann .csv не лучший тип файла для такого объема данных, попробуйте .parquet или просто numpy .npz

dankal444 15.04.2024 11:43

@dankal444 Хорошо, я провел последние два дня, пробуя паркет, npz и коврик, потому что мне нужно, чтобы это можно было импортировать в Matlab, но любой формат файла, который я использую, не работает. Я использую код VS, тестирую функции записи в файл и могу записать небольшие матрицы, но матрица расстояний ломается, ничего не записывая в файлы. Я не уверен, почему это происходит, поскольку мой отладчик не выдает ошибку, запуск файла просто прекращается. Единственное, что мне удалось написать, это в CSV, но на прогон трети матрицы ушло около 5 часов. Знаете ли вы какие-либо способы ускорить запись в CSV?

Kat Neumann 17.04.2024 21:10

@KatNeumann Может быть, некоторые из ваших проблем были связаны с размером файла/ОЗУ, и все, что вам нужно, это разделить сохраненные данные на части? polars пакет работает быстро, когда дело доходит до чтения/записи CSV, стоит попробовать. Matlab должен читать файлы паркета, вы платите за это программное обеспечение, чтобы вы могли связаться с авторами, почему оно не работает. Я думаю, файл слишком большой

dankal444 17.04.2024 21:24

@KatNeumann Какие данные вы пытаетесь передать в CSV? Какие преобразования вы делаете с матрицей расстояний, прежде чем попытаться отправить ее в CSV?

Serge de Gosson de Varennes 18.04.2024 06:07

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

Serge de Gosson de Varennes 18.04.2024 06:54
Ответ принят как подходящий

Этот ответ предназначен для решения следующей проблемы, а именно сохранения полученных результатов в CSV-файл. Учитывая объем данных, pd.to_csv() — не лучший вариант.

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

Я создаю фрейм данных, добавляя точки (lat, long). Затем, используя polar, я преобразую фрейм данных pandas в полярный фрейм данных и, наконец, сохраняю его:

import numpy as np
import pandas as pd
import time

def haversine(lat1, lon1, lat2, lon2):
    lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2])
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2
    c = 2 * np.arcsin(np.sqrt(a)) 
    r = 6371  
    return c * r

np.random.seed(42)
latitudes = np.random.uniform(low=25.0, high=49.0, size=10000)
longitudes = np.random.uniform(low=-125.0, high=-66.0, size=10000)
coords = np.column_stack((latitudes, longitudes))

start_time = time.time()

n = coords.shape[0]
dist_matrix = np.zeros((n, n))
for i in range(n):
    dist_matrix[i, :] = haversine(coords[i, 0], coords[i, 1], coords[:, 0], coords[:, 1])

end_time = time.time()
elapsed_time = end_time - start_time
print("Distance matrix computed in {:.2f} seconds".format(elapsed_time))
print("Shape of the distance matrix:", dist_matrix.shape)

point_labels = [f"Point {i}" for i in range(n)]
dist_df = pd.DataFrame(dist_matrix, index=point_labels, columns=point_labels)


distance = dist_df.loc['Point 0', 'Point 1']
print(f"Distance from Point 0 to Point 1: {distance} km")

lat_lon_index = pd.MultiIndex.from_arrays([latitudes, longitudes], names=['Latitude', 'Longitude'])
dist_df.index = lat_lon_index
dist_df.columns = lat_lon_index

lat_from, lon_from = latitudes[0], longitudes[0]  
lat_to, lon_to = latitudes[1], longitudes[1]
distance = dist_df.loc[(lat_from, lon_from), (lat_to, lon_to)]
print(f"Distance from ({lat_from}, {lon_from}) to ({lat_to}, {lon_to}): {distance} km")

Теперь dist_df выглядит так

Чтобы сохранить его в CSV, просто сделайте следующее:

dist_df_reset = dist_df.reset_index()

polars_df = pl.from_pandas(dist_df_reset)

polars_df.write_csv('polars_distance_matrix.csv')

Файл будет:

ВАЖНОЕ ПРИМЕЧАНИЕ: Это будет намного быстрее, но вы не можете надеяться, что это займет очень мало времени с таким количеством точек.

Хорошо, я попробовал это на своем рабочем столе Windows, и у меня проблемы с памятью, так как я всего лишь пользователь, а выделение недостаточно велико для переиндексации кадра данных (хотя я проверил на нем около 33 ГБ. Я избавился от переиндексации, чтобы посмотреть, сохранится ли CSV, и у него закончилась память malloc, которая, как я полагаю, является памятью модуля Polars. Я попытался затем запустить его на своем ПК, но возникла проблема с Mac OS, работающей на Python под зависимостями Rosetta. для пакета Polars, который я могу попытаться исправить.

Kat Neumann 18.04.2024 15:32

Вы можете сохранить его как файл данных. Почему вы хотите сохранить его как CSV-файл?

Serge de Gosson de Varennes 18.04.2024 18:48

Мне просто нужно это в любом формате файла, который я могу импортировать в MATLAB. Но любой формат файла, кроме CSV, автоматически прекращает работу, ничего не выполняя. Я попытался просто запустить файл Python через MATLAB, чтобы экспорт не потребовался, но мне не удалось заставить MATLAB запустить numpy, который используется для создания матрицы расстояний.

Kat Neumann 19.04.2024 22:55

Другие вопросы по теме