在numpy中执行交叉差分

4 投票
3 回答
1010 浏览
提问于 2025-04-18 11:54

我想要进行一个叫做“交叉差”的操作,具体是这样的:

cross_diff ( [a,b], [c,d] ) = [ [ a - c, a - d], [ b - c, b - d] ]

我在Python中有一个实现这个操作的方法,如下所示:

def crossdiff(a,b):
    c = []
    for a1 in range(len(a)):
        for b1 in range(len(b)):
            c.append (a[a1]-b[b1])
    x = numpy.array(c)
    x.reshape(len(a),len(b))
    return x

问题是,我需要先创建一个Python数组,把结果放进去,然后再转换回numpy数组。我希望能够直接用numpy的向量a和b,得到一个包含所有差值的numpy数组c,因为上面的代码在处理大向量时性能很差。

有没有办法用“纯”numpy操作来完成这个计算呢?

编辑以测试结果:

我在Python性能分析工具中运行了这个线程中列出的四个实现,以便进行比较。我必须在工作站上运行它们,因为最初的实现使用了大约4GB的内存,处理了10,000个元素。

import numpy
import cProfile

def cross_diff(A, B):
    return A[:,None] - B[None,:]

def crossdiff2 (a,b):
    ap = numpy.tile (a, (numpy.shape(b)[0],1))
    bp = numpy.tile (b, (numpy.shape(a)[0],1))

    return ap - bp.transpose()

def crossdiff(a,b):
    c = []
    for a1 in range(len(a)):
        for b1 in range(len(b)):
            c.append (a[a1]-b[b1])
    x = numpy.array(c)
    x.reshape(len(a),len(b))
    return x

a = numpy.array(range(10000))
b = numpy.array(range(10000))

cProfile.run('crossdiff (a,b)')
cProfile.run('crossdiff2 (a,b)')
cProfile.run('cross_diff (a,b)')
cProfile.run('numpy.subtract.outer (a,b)')

结果是:原始的Python实现需要74.147秒,我的版本只需1.656秒,第三个实现是0.296秒,第四个实现是0.288秒。

3 个回答

0

根据@DrV提到的技术,我想出了这个方法:

def crossdiff2 (a,b):
    ap = numpy.tile (a, (numpy.shape(b)[0],1))
    bp = numpy.tile (b, (numpy.shape(a)[0],1))

    print ap.transpose()

    return ap - bp.transpose()

这个方法得出的结果和上面的一样,但所有的数据处理都在numpy里完成。

5

你可以通过使用ufuncs的.outer方法来得到相同的结果,而不需要明确地添加维度来进行广播。比如说:

>>> np.subtract.outer([1, 2], [3, 4, 5])
array([[-2, -3, -4],
       [-1, -2, -3]])

这个方法还有一个好处,就是它可以接受任何可迭代的对象作为输入,你不需要先把它们转换成数组。

4

试试这个:

import numpy as np

np.array([a,b])[:,None] - np.array([c,d,e])[None,:]

稍微解释一下:索引中的 None 会根据需要扩展维度。所以,实际上计算会是:

a a a     c d e     a-c  a-d  a-e
       -         =  
b b b     c d e     b-c  b-d  b-e

索引中的 None 真的很有用。

再来一个例子:

import numpy as np

def cross_diff(A, B):
    return A[:,None] - B[None,:]

vec_a = np.array([1,2,3,4])
vec_b = np.array([3,2,1])

print cross_diff(vec_a, vec_b)

撰写回答