计算多个向量的成对平方差

时间:2018-10-24 09:28:56

标签: python pandas numpy pairwise

我正在寻找最快的方法来计算两个向量($(function() { $("#answer").autocomplete({ minLength: 2, source: function(request, response) { var display = []; $.each(array, function(k, v) { if (v.capital.toLowerCase().indexOf(request.term.toLowerCase()) == 0) { display.push({ "label": v.capital }); return; } }); response(display); }, )之间的平方差,但是要成对(所有组合或仅上三角)。

(x1-x2)**2

预期输出:

x1 = [1,3,5,6,8]
x2 = [3,6,7,9,12]

array([[   4.,   25.,   36.,   64.,  121.],
       [   0.,    9.,   16.,   36.,   81.],
       [   4.,    1.,    4.,   16.,   49.],
       [   9.,    0.,    1.,    9.,   36.],
       [  25.,    4.,    1.,    1.,   16.]])

甚至(如果更快):

array([[   4.,   25.,   36.,   64.,  121.],
       [   0.,    9.,   16.,   36.,   81.],
       [   0.,    0.,    4.,   16.,   49.],
       [   0.,    0.,    0.,    9.,   36.],
       [   0.,    0.,    0.,    0.,    16.]])

2 个答案:

答案 0 :(得分:4)

这里有一个broadcastingmasking来获得上三角三角形,然后仅对那些进行平方以提高性能效率-

def pairwise_squared_diff(x1, x2):
    x1 = np.asarray(x1)
    x2 = np.asarray(x2)
    diffs = x1[:,None] - x2
    mask = np.arange(len(x1))[:,None] <= np.arange(len(x2))
    return (diffs[mask])**2

样品运行-

In [85]: x1
Out[85]: array([1, 3, 5, 6, 8])

In [86]: x2
Out[86]: array([ 3,  6,  7,  9, 12])

In [87]: pairwise_squared_diff(x1, x2)
Out[87]: 
array([  4,  25,  36,  64, 121,   9,  16,  36,  81,   4,  16,  49,   9,
        36,  16])

可能的改进

改进#1:

我们还可以使用np.tri生成mask-

mask = ~np.tri(len(x1),len(x2),dtype=bool,k=-1)

改进2:

如果我们可以将2D的输出与下面的三角形设置为0s一起使用,那么简单地将元素与mask相乘也可以解决该问题,以获得最终输出-

(diffs*mask)**2

这对于numexpr module可以很好地处理大数据并获得内存效率和性能。

改进#3:

我们还可以使用numexpr计算差异,因此也可以使用相同的evaulate方法计算屏蔽输出,从而为自己提供一个新的解决方案-

def pairwise_squared_diff_numexpr(x1, x2):
    x1 = np.asarray(x1)
    x2 = np.asarray(x2)
    mask = ~np.tri(len(x1),len(x2),dtype=bool,k=-1)
    return ne.evaluate('mask*((x1D-x2)**2)',{'x1D':x1[:,None]})

改进的时间

让我们研究这些有关大型阵列性能的建议-

设置:

In [136]: x1 = np.random.randint(0,9,(1000))

In [137]: x2 = np.random.randint(0,9,(1000))

改进1:

In [138]: %timeit np.arange(len(x1))[:,None] <= np.arange(len(x2))
1000 loops, best of 3: 772 µs per loop

In [139]: %timeit ~np.tri(len(x1),len(x2),dtype=bool,k=-1)
1000 loops, best of 3: 243 µs per loop

改进2:

In [140]: import numexpr as ne

In [141]: diffs = x1[:,None] - x2
     ...: mask = np.arange(len(x1))[:,None] <= np.arange(len(x2))

In [142]: %timeit (diffs[mask])**2
1000 loops, best of 3: 1.46 ms per loop

In [143]: %timeit ne.evaluate('(diffs*mask)**2')
1000 loops, best of 3: 1.05 ms per loop

在完整解决方案上进行了改进#3:

In [170]: %timeit pairwise_squared_diff(x1, x2)
100 loops, best of 3: 3.66 ms per loop

In [171]: %timeit pairwise_squared_diff_numexpr(x1, x2)
1000 loops, best of 3: 1.54 ms per loop

一个人

出于完整性考虑,由于内存效率高,这是一个循环式的循环,利用slicing的性能要比单纯的broadcasting更好-

def pairwise_squared_diff_loopy(x1,x2):
    n = len(x2)
    idx = np.concatenate(( [0], np.arange(n,0,-1).cumsum() ))
    start, stop = idx[:-1], idx[1:]
    L = n*(n+1)//2
    out = np.empty(L,dtype=np.result_type(x1,x2))
    for i,(s0,s1) in enumerate(zip(start,stop)):
        out[s0:s1] = x1[i] - x2[i:]
    return out**2

时间-

In [300]: x1 = np.random.randint(0,9,(1000))
     ...: x2 = np.random.randint(0,9,(1000))

In [301]: %timeit pairwise_squared_diff(x1, x2)
100 loops, best of 3: 3.44 ms per loop

In [302]: %timeit pairwise_squared_diff_loopy(x1, x2)
100 loops, best of 3: 2.73 ms per loop

答案 1 :(得分:1)

您可以使用广播:

x1 = np.asarray([1,3,5,6,8]).reshape(-1, 1)
x2 = np.asarray([3,6,7,9,12]).reshape(1, -1)
(x1 - x2)**2

输出:

array([[  4,  25,  36,  64, 121],
       [  0,   9,  16,  36,  81],
       [  4,   1,   4,  16,  49],
       [  9,   0,   1,   9,  36],
       [ 25,   4,   1,   1,  16]])

这很容易编写代码,但是可以计算所有值,因此可以对其进行优化以仅计算上三角。