沿特定维度从ndarray减去矩阵而不进行整形

时间:2018-10-06 14:36:27

标签: python numpy

假设我有一个名为ahat的2 x 2 x 100 ndarray和一个名为A的2 x 2矩阵。从尺寸为100的尺寸中减去2 x 2矩阵而不进行循环或整形的最有效方法是什么?

for k in range(ahat.shape[2]):
    ahat[:,:,k] = ahat[:,:, k] - A

我尝试使用np.apply_over_axes,但无法正常工作。更一般而言,如果我有两个兼容形状的ndarray,例如上面的示例,那么沿特定尺寸进行操作的首选方式是什么。例如,我可能想将ahat中的每个2x2矩阵与A相乘,或者我想沿尺寸100的尺寸应用np.linalg.inv

2 个答案:

答案 0 :(得分:2)

A升级为形状(2,2,1)的数组,然后NumPy broadcasting将完成其余工作:

ahat -= A[..., None]

A[..., None]等效于A[..., np.newaxis]。它将长度为1的新轴添加到数组。由于ahat的形状为(2,2,100),而A[..., None]的形状为(2,2,1),因此NumPy广播会将两个数组都提升为兼容的形状(2,2,100)(但在内存中)有效的方式,实际上将值从A复制到更大的数组)。

更一般而言,请注意,NumPy广播会自动将新轴添加到任何NumPy数组形状的左侧。因此,举例来说,对于任何基本的NumPy算术运算(例如加法或乘法),如果算术运算中涉及的其他数组是3维的,A会自动广播为(1,2,2)这样的形状, (如果另一个数组是4维的,则甚至是(1,1,2,2))。在上方,我们需要A[..., None]来显式添加新轴,因为我们希望新轴位于形状的右侧。


对于矩阵乘法,通常使用np.dotnp.einsumnp.tensordot。其中的某些功能(例如np.einsumnp.tensordot)允许您指定要在矩阵乘法中使用的轴(因此您无需显式添加新轴)。 例如,要将ahatA矩阵相乘,可以使用

np.tensordot(ahat, A, axes=[[1], [0]])

np.einsum('ijk,jl->ikl', ahat, A)

这会将ahat的1轴上的值与A的0轴上的值相乘,然后求和。

答案 1 :(得分:0)

除了可以接受的答案之外,如果有人想知道这两种用于反相的速度更快(根据评论):

import timeit

setup='''import numpy as np
x = np.random.randint(0,100,(3,3,10000))'''


stm1 = 'xinv1 = np.moveaxis(np.asarray(list(map(np.linalg.inv, np.moveaxis(x,2,0)))),0,2)'
stm2 = 'np.apply_along_axis(lambda arr: np.linalg.inv(arr.reshape(3,3)), axis=0, arr=x.reshape(-1, 10000))'

times = timeit.repeat(setup = setup, stmt = stm1, number=100)
print('Using map: ', times)
times = timeit.repeat(setup = setup, stmt = stm2, number=100)
print('Using apply_along_axis: ', times)

给我:

Using map:  [5.976081462009461, 6.024182428998756, 6.218410155008314]
Using apply_along_axis:  [8.279263457996421, 7.926949607004644, 7.928437952010427]