Numpy:逐行减去2个numpy数组

3

我有两个numpy数组a和b,如下:

a = np.random.randint(0,10,(3,2))
Out[124]: 
array([[0, 2],
       [6, 8],
       [0, 4]])
b = np.random.randint(0,10,(2,2))
Out[125]: 
array([[5, 9],
       [2, 4]])

我希望从a中的每一行中减去b中的每一行,期望输出为形状(3,2,2):

array([[[-5, -7],        [-2, -2]],

       [[ 1, -1],        [ 4,  4]],

       [[-5, -5],        [-2,  0]]])

我可以使用以下方式实现这一点:
print(np.c_[(a - b[0]),(a - b[1])].reshape(3,2,2))

但我需要一个完全矢量化的解决方案或内置的numpy函数来完成这个任务。

我所说的完全向量化解决方案(factorized是之前的一个打字错误)是指我不想通过索引引用数组b,例如b[i],因为该数组中的行数可能会发生变化,我希望有一个解决方案,它始终会输出一个形状为(3,len(b),2)的数组。 - Allen Qin
4个回答

5

只需使用np.newaxis(其实只是None的别名)来为a添加一个单例维度,让广播完成剩下的工作:

In [45]: a[:, np.newaxis] - b
Out[45]: 
array([[[-5, -7],
        [-2, -2]],

       [[ 1, -1],
        [ 4,  4]],

       [[-5, -5],
        [-2,  0]]])

这比我的最佳解决方案快了约40%。你能更好地解释一下这里发生了什么吗?有点抽象。 - litepresence
1
这对大型数组非常不利于内存;我想从一个500 x 3078的数组中减去一个5000 x 3078的数组,这将需要500 * 3072 * 5000 * 8 / 1e9 = 61.44吉字节。 - jyn
这真是个巧妙的解决方案。我们甚至可以用标量列表减去向量,得到矩阵:a = np.array([1,2,3,4,5,6])b = np.array([1,2,3])a[:, np.newaxis]-b。结果形状为(6,3) - Muhammad Yasirroni

1
你可以使用np.subtract()稍微节省一些时间,使用np.concatenate()可以节省更多时间。
import numpy as np
import time

start = time.time()
for i in range(100000):

    a = np.random.randint(0,10,(3,2))
    b = np.random.randint(0,10,(2,2))
    c = np.c_[(a - b[0]),(a - b[1])].reshape(3,2,2)

print time.time() - start

start = time.time()
for i in range(100000):

    a = np.random.randint(0,10,(3,2))
    b = np.random.randint(0,10,(2,2))
    #c = np.c_[(a - b[0]),(a - b[1])].reshape(3,2,2)
    c = np.c_[np.subtract(a,b[0]),np.subtract(a,b[1])].reshape(3,2,2)

print time.time() - start

start = time.time()
for i in range(100000):

    a = np.random.randint(0,10,(3,2))
    b = np.random.randint(0,10,(2,2))
    #c = np.c_[(a - b[0]),(a - b[1])].reshape(3,2,2)
    c = np.concatenate([np.subtract(a,b[0]),np.subtract(a,b[1])],axis=1).reshape(3,2,2)

print time.time() - start

>>>

3.14023900032
3.00368094444
1.16146492958

参考资料:

对numpy.c_文档和示例代码感到困惑

np.c_是另一种进行数组连接的方法。


谢谢您的回答,但请查看我在问题中的评论。 - Allen Qin
啊,我明白了,这增加了一些复杂性。我不确定你在原始操作中所说的因式分解是什么意思,我会考虑一下;看看我能否想出什么。尽管如此,减少 CPU 负载总是有益的。 - litepresence

1

我不确定完全分解解决方案的含义,但也许这会有所帮助:

np.append(a, a, axis=1).reshape(3, 2, 2) - b

谢谢您的回答,但请查看我在问题中的评论。 - Allen Qin

1
从关于“广播”的文件中阅读,它说:

When operating on two arrays, NumPy compares their shapes element-wise. It starts with the trailing dimensions, and works its way forward. Two dimensions are compatible when

they are equal, or
one of them is 1
回到你的情况,你希望结果的形状为(3, 2, 2),根据以下规则,你需要调整维度。 下面是完成此操作的代码:
In [1]: a_ = np.expand_dims(a, axis=0)

In [2]: b_ = np.expand_dims(b, axis=1)

In [3]: c = a_ - b_

In [4]: c
Out[4]: 
array([[[-5, -7],
        [ 1, -1],
        [-5, -5]],

       [[-2, -2],
        [ 4,  4],
        [-2,  0]]])

In [5]: result = c.swapaxes(1, 0)

In [6]: result
Out[6]: 
array([[[-5, -7],
        [-2, -2]],

       [[ 1, -1],
        [ 4,  4]],

       [[-5, -5],
        [-2,  0]]])

In [7]: result.shape
Out[7]: (3, 2, 2)

如果A和B有超过两列,这个答案还适用吗? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接