Поскольку вы подразумевали, что вам не нужна полная квадратная матрица результатов, отметив, что cdist неудобен, поскольку он рассчитывает попарные расстояния дважды, вы можете использовать Numba, чтобы написать UDF, который рассчитывает только для нижнего или верхнего треугольника квадратная матрица.
Обратите внимание, что при первом запуске это компиляция JIT.
from scipy.spatial import distance
import pandas as pd
from numba import njit, prange
import numpy as np
@njit(parallel=True)
def euclidean_distance(coords1, coords2):
# allocate output array
c1_length, c2_length = len(coords1), len(coords2)
out = np.empty(shape=(c1_length, c2_length), dtype=np.float64)
# fill the lower triangle with euclidean distance formula
# assuming coordiantes are (lat, lon) based on the example https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cdist.html
for lat_ix in prange(c1_length):
for lon_ix in prange(c2_length):
if lat_ix >= lon_ix: # do the reverse for the upper triangle
out[lat_ix, lon_ix] = (
(coords1[lat_ix, 0] - coords2[lon_ix, 0]) ** 2
+ (coords1[lat_ix, 1] - coords2[lon_ix, 1]) ** 2
) ** 0.5
else:
out[lat_ix, lon_ix] = 0
return out
for n in [10, 100, 5000, 20000]:
arr = np.random.normal(0, 100, (n, 2))
print(n, arr.shape)
%time out = euclidean_distance(arr, arr)
%time out_cdist = distance.cdist(arr, arr, 'euclidean')
if n < 1000:
np.testing.assert_array_almost_equal(out, np.tril(out_cdist))
print()
Вывод:
10 (10, 2)
CPU times: user 987 ms, sys: 19.3 ms, total: 1.01 s
Wall time: 1.01 s
CPU times: user 79 µs, sys: 12 µs, total: 91 µs
Wall time: 95.1 µs
100 (100, 2)
CPU times: user 1.05 ms, sys: 404 µs, total: 1.45 ms
Wall time: 1.16 ms
CPU times: user 926 µs, sys: 254 µs, total: 1.18 ms
Wall time: 946 µs
5000 (5000, 2)
CPU times: user 125 ms, sys: 128 ms, total: 253 ms
Wall time: 75 ms
CPU times: user 184 ms, sys: 92.6 ms, total: 277 ms
Wall time: 287 ms
20000 (20000, 2)
CPU times: user 2.21 s, sys: 2.15 s, total: 4.36 s
Wall time: 2.55 s
CPU times: user 3.1 s, sys: 2.71 s, total: 5.81 s
Wall time: 31.9 s
С элементом 20 000 массив, UDF немного быстрее, так как он может сохранить половину вычислений. cdist
кажется особенно / неожиданно медленным для этого специфического c распределения данных в масштабе на моем Macbook Air, но точка зрения сделана независимо.