假设我有一个名为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
。
答案 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.dot
或np.einsum
或np.tensordot
。其中的某些功能(例如np.einsum
和np.tensordot
)允许您指定要在矩阵乘法中使用的轴(因此您无需显式添加新轴)。
例如,要将ahat
和A
矩阵相乘,可以使用
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]