Python 如何从没有嵌套循环的二维阵列切片三维阵列?
我有四个矩阵,部分由彼此创建: A是表示灰度图像堆栈的3D矩阵,形状为n、h、w B是也表示图像堆栈的3D矩阵,其中每个切片分别从a中的对应切片计算,并且也是形状n、h、w C是2D矩阵,包含z方向上最大值为B的索引,形状为h,w D是2D矩阵,其中a中的值从某个切片复制而来,该切片由位置x、y处C中的值表示。 使用循环实现的最小示例如下所示:Python 如何从没有嵌套循环的二维阵列切片三维阵列?,python,arrays,numpy,image-processing,Python,Arrays,Numpy,Image Processing,我有四个矩阵,部分由彼此创建: A是表示灰度图像堆栈的3D矩阵,形状为n、h、w B是也表示图像堆栈的3D矩阵,其中每个切片分别从a中的对应切片计算,并且也是形状n、h、w C是2D矩阵,包含z方向上最大值为B的索引,形状为h,w D是2D矩阵,其中a中的值从某个切片复制而来,该切片由位置x、y处C中的值表示。 使用循环实现的最小示例如下所示: import numpy as np A = np.random.randint(0, 255, size=(3, 4, 5)) B = np.ran
import numpy as np
A = np.random.randint(0, 255, size=(3, 4, 5))
B = np.random.randint(0, 255, size=(3, 4, 5))
C = np.argmax(B, axis=0)
D = np.zeros(C.shape, dtype=int)
for y in range(C.shape[0]):
for x in range(C.shape[1]):
D[y, x] = A[C[y, x], y, x]
> A
array([[[ 24, 84, 155, 8, 147],
[ 25, 4, 49, 195, 57],
[ 93, 76, 233, 83, 177],
[ 70, 211, 201, 132, 239]],
[[177, 144, 247, 251, 207],
[242, 148, 28, 40, 105],
[181, 28, 132, 94, 196],
[166, 121, 72, 14, 14]],
[[ 55, 254, 140, 142, 14],
[112, 28, 85, 112, 145],
[ 16, 72, 16, 248, 179],
[160, 235, 225, 14, 211]]])
> B
array([[[246, 14, 55, 163, 161],
[ 3, 152, 128, 104, 203],
[ 43, 145, 59, 169, 242],
[106, 169, 31, 222, 240]],
[[ 41, 26, 239, 25, 65],
[ 47, 252, 205, 210, 138],
[194, 64, 135, 127, 101],
[ 63, 208, 179, 137, 59]],
[[112, 156, 183, 23, 253],
[ 35, 6, 233, 42, 100],
[ 66, 119, 102, 217, 64],
[ 82, 67, 135, 6, 8]]])
> C
array([[0, 2, 1, 0, 2],
[1, 1, 2, 1, 0],
[1, 0, 1, 2, 0],
[0, 1, 1, 0, 0]])
> D
array([[ 24, 254, 247, 8, 14],
[242, 148, 85, 40, 57],
[181, 76, 132, 248, 177],
[ 70, 121, 72, 132, 239]])
我的问题是:如何用C高效地分割A,消除嵌套的for循环?我最初的想法是将C扩展到一个3D布尔掩码,其中只有位置[C,y,x]被设置为真,然后简单地将其元素乘以a,并在z轴上求和。但是我想不出没有循环的pythonesque实现,如果我知道如何编写的话,我可能就不需要再创建布尔掩码了
我发现的最接近已经实现的功能是,但C只需要32个元素。这里的标准方法是结合核心思想使用,也在以下内容中介绍:
y, x = np.ogrid[:C.shape[0],:C.shape[1]]
D = A[C[y, x], y, x]
将提出的方法与显式循环和基于np.ogrid的方法进行比较,可以得出:
import numpy as np
def take_by_axis_loop_fix(arr, pos):
result = np.zeros(pos.shape, dtype=int)
for i in range(pos.shape[0]):
for j in range(pos.shape[1]):
result[i, j] = arr[pos[i, j], i, j]
return result
def take_by_axis_ogrid_fix(arr, pos):
i, j = np.ogrid[:pos.shape[0], :pos.shape[1]]
return arr[pos[i, j], i, j]
def take_by_axis_np(arr, pos, axis=0):
return np.take_along_axis(arr, np.expand_dims(pos, axis=axis), axis=axis).squeeze()
def take_by_axis_ogrid(arr, pos, axis=0):
ij = tuple(np.ogrid[tuple(slice(None, d, None) for d in pos.shape)])
ij = ij[:axis] + (pos[ij],) + ij[axis:]
return arr[ij]
表明这是迄今为止提出的最有效的方法。
还请注意,基于np.take_沿_轴的方法和基于_轴的take_ogrid方法对于高维输入基本不变,与_fix方法相反
特别是,take_by_axis_ogrid是take_by_axis_ogrid_fix的轴不可知版本,本质上是。基本上,给定A、B和C,您正在寻找一种矢量化的无显式循环方法来计算D。这正确吗?是的,这正是我想要的!我已经看到了“沿轴移动”功能,但无法尝试。我的numpy版本似乎太旧了,由于不兼容的风险,我不敢升级。不过,谢谢你帮我试了一下,并给我定了时间!你有哪个版本的numpy?np.apply_沿_轴/np.take对您可用吗?我的numpy版本是1.14.5-我可以同时调用np.apply_沿_轴和np.take。这两个函数我都见过,但我不知道如何在这里使用它们。这正是我一直在寻找的,即使它可能不是最快的版本。非常感谢!不客气。根据我的经验,可读性比效率更重要。
import numpy as np
def take_by_axis_loop_fix(arr, pos):
result = np.zeros(pos.shape, dtype=int)
for i in range(pos.shape[0]):
for j in range(pos.shape[1]):
result[i, j] = arr[pos[i, j], i, j]
return result
def take_by_axis_ogrid_fix(arr, pos):
i, j = np.ogrid[:pos.shape[0], :pos.shape[1]]
return arr[pos[i, j], i, j]
def take_by_axis_np(arr, pos, axis=0):
return np.take_along_axis(arr, np.expand_dims(pos, axis=axis), axis=axis).squeeze()
def take_by_axis_ogrid(arr, pos, axis=0):
ij = tuple(np.ogrid[tuple(slice(None, d, None) for d in pos.shape)])
ij = ij[:axis] + (pos[ij],) + ij[axis:]
return arr[ij]
A_ = np.random.randint(0, 255, size=(300, 400, 500))
B_ = np.random.randint(0, 255, size=(300, 400, 500))
C_ = np.argmax(B_, axis=0)
funcs = take_by_axis_loop_fix, take_by_axis_ogrid_fix, take_by_axis_ogrid, take_by_axis_np
for func in funcs:
print(func.__name__, np.all(func(A_, C_) == take_by_axis_loop_fix(A_, C_)))
%timeit func(A_, C_)
print()
# take_by_axis_loop_fix True
# 10 loops, best of 3: 114 ms per loop
# take_by_axis_ogrid_fix True
# 100 loops, best of 3: 5.94 ms per loop
# take_by_axis_ogrid True
# 100 loops, best of 3: 5.54 ms per loop
# take_by_axis_np True
# 100 loops, best of 3: 3.34 ms per loop