成对点之间的成对位移矢量

时间:2014-03-13 20:40:41

标签: python numpy scipy

我在N维度d中有一个(N, d)点数组,我想为每对{{1}创建一个包含所有位移向量的新数组}。如果我只想要这些向量的大小,我可以使用pdist中的scipy.spatial.distance

如果我能做的话会很棒

(N choose 2, d)

pdist(points, lambda u, v: u - v) 函数必须返回标量(metric


我的解决方案是使用ValueError: setting an array element with a sequence.

np.triu_indices

这比使用i, j = np.triu_indices(len(points), 1) displacements = points[i] - points[j] 慢大约20-30倍(我通过考虑pdist的大小进行比较,虽然这不是耗时的部分,我认为这实际上是上三角和运行花式索引)。

2 个答案:

答案 0 :(得分:2)

直接前进

dis_vectors = [l - r for l, r in itertools.combinations(points, 2)]

但我怀疑它很快。实际上%timeit说:

获得3分:

list : 13 us
pdist: 24 us

但已经有27分了:

list : 798 us
pdist: 35.2 us

我们在这里谈论了多少分?

另一种可能性如

import numpy
from operator import mul
from fractions import Fraction

def binomial_coefficient(n,k):
    # credit to http://stackoverflow.com/users/226086/nas-banov
    return int( reduce(mul, (Fraction(n-i, i+1) for i in range(k)), 1) )

def pairwise_displacements(a):
    n = a.shape[0]
    d = a.shape[1]
    c = binomial_coefficient(n, 2)

    out = numpy.zeros( (c, d) )

    l = 0
    r = l + n - 1
    for sl in range(1, n): # no point1 - point1!
        out[l:r] = a[:n-sl] - a[sl:]
        l = r
        r += n - (sl + 1)
    return out

这简单地在所有维度上“滑动”数组,并在每个步骤中执行(可广播)减法。注意,不考虑重复,也没有相等的对(例如,point1 - point1)。

此功能在使用31.3ms的1000点范围内仍然表现良好,而pdist20.7 ms时仍然更快,而列表理解在1.23 s中排在第三位。

答案 1 :(得分:1)

如果计算差异的完整笛卡尔积,展平生成的2D数组,并创建自己的索引以提取上三角形,则可以将其设置为“仅”#34;比pdist慢6倍:

In [39]: points = np.random.rand(1000, 2)

In [40]: %timeit pdist(points)
100 loops, best of 3: 5.81 ms per loop

In [41]: %%timeit
    ...: n = len(points)
    ...: rng = np.arange(1, n)
    ...: idx = np.arange(n *(n-1) // 2) + np.repeat(np.cumsum(rng), rng[::-1])
    ...: np.take((points[:, None] - points).reshape(-1, 2), idx, axis=0)
    ...: 
10 loops, best of 3: 33.9 ms per loop

您还可以加快解决方案速度,自己创建索引,并使用take而不是花哨的索引:

In [75]: %%timeit
    ...: n = len(points)
    ...: rng = np.arange(1, n)
    ...: idx1 = np.repeat(rng - 1, rng[::-1])
    ...: idx2 = np.arange(n*(n-1)//2) + np.repeat(n - np.cumsum(rng[::-1]), rng[::-1])
    ...: np.take(points, idx1, axis=0) - np.take(points, idx2, axis=0)
    ...: 
10 loops, best of 3: 38.8 ms per loop