Самая быстрая попарная метрика расстояния в python

У меня есть 1D массив чисел, и я хочу вычислить все попарные евклидовы расстояния. У меня есть метод (благодаря SO) делать это с трансляцией, но он неэффективен, потому что он вычисляет каждое расстояние дважды. И это не хорошо.

вот пример, который дает мне то, что я хочу с массив из 1000 чисел.

import numpy as np
import random
r = np.array([random.randrange(1, 1000) for _ in range(0, 1000)])
dists = np.abs(r - r[:, None])

Какова самая быстрая реализация в scipy / numpy/scikit-узнайте, что я могу использовать для этого, учитывая, что она должна масштабироваться до ситуации, когда массив 1D имеет значения >10k.

Примечание: матрица симметрична, поэтому я предполагаю, что можно получить по крайней мере 2x ускорение, обратившись к этому, я просто не знаю, как.

3 ответов


ни один из других ответов вполне ответили на вопрос - 1 был на Cython, один был медленнее. Но оба дали очень полезные советы. Наблюдение за ними предполагает, что scipy.spatial.distance.pdist - это путь.

вот код:

import numpy as np
import random
import sklearn.metrics.pairwise
import scipy.spatial.distance

r = np.array([random.randrange(1, 1000) for _ in range(0, 1000)])
c = r[:, None]

def option1(r):
    dists = np.abs(r - r[:, None])

def option2(r):
    dists = scipy.spatial.distance.pdist(r, 'cityblock')

def option3(r):
    dists = sklearn.metrics.pairwise.manhattan_distances(r)

синхронизация с IPython:

In [36]: timeit option1(r)
100 loops, best of 3: 5.31 ms per loop

In [37]: timeit option2(c)
1000 loops, best of 3: 1.84 ms per loop

In [38]: timeit option3(c)
100 loops, best of 3: 11.5 ms per loop

Я не пробовал реализацию Cython (я не могу использовать ее для этого проекта), но сравнивая мои результаты с другим ответом, который сделал, похоже,scipy.spatial.distance.pdist примерно на треть медленнее, чем Реализация Cython (с учетом различных машин путем бенчмаркинга на НП.решение АБС).


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

Я знаю, что вы просили что-то внутри scipy / numpy / scikit-learn, но, возможно, это откроет для вас новые возможности:

my_cython.pyx:
import numpy as np
cimport numpy as np
import cython

cdef extern from "math.h":
    double abs(double t)

@cython.wraparound(False)
@cython.boundscheck(False)
def pairwise_distance(np.ndarray[np.double_t, ndim=1] r):
    cdef int i, j, c, size
    cdef np.ndarray[np.double_t, ndim=1] ans
    size = sum(range(1, r.shape[0]+1))
    ans = np.empty(size, dtype=r.dtype)
    c = -1
    for i in range(r.shape[0]):
        for j in range(i, r.shape[0]):
            c += 1
            ans[c] = abs(r[i] - r[j])
    return ans

ответ-это 1-D массив, содержащий все не повторяющиеся оценки.

для импорта в Python:

import numpy as np
import random

import pyximport; pyximport.install()
from my_cython import pairwise_distance

r = np.array([random.randrange(1, 1000) for _ in range(0, 1000)], dtype=float)

def solOP(r):
    return np.abs(r - r[:, None])

синхронизация с IPython:

In [2]: timeit solOP(r)
100 loops, best of 3: 7.38 ms per loop

In [3]: timeit pairwise_distance(r)
1000 loops, best of 3: 1.77 ms per loop

через половину памяти, но в 6 раз медленнее, чем np.abs(r - r[:, None]):

triu = np.triu_indices(r.shape[0],1)
dists2 = abs(r[triu[1]]-r[triu[0]])