我有ndarray
个shape(z,y,x)
个值。我试图用另一个ndarray
的{{1}}索引这个数组,其中包含我感兴趣的值的z-index。
shape(y,x)
由于我的数组相当大,我尝试使用import numpy as np
val_arr = np.arange(27).reshape(3,3,3)
z_indices = np.array([[1,0,2],
[0,0,1],
[2,0,1]])
来避免不必要的数组副本,但却无法用我的头围绕索引三维数组。
如何使用np.take
索引val_arr
以获取所需z轴位置的值?预期的结果将是:
z_indices
答案 0 :(得分:6)
您可以使用choose
进行选择:
>>> z_indices.choose(val_arr)
array([[ 9, 1, 20],
[ 3, 4, 14],
[24, 7, 17]])
函数choose
非常有用,但理解起来可能有些棘手。基本上,给定一个数组(val_arr
),我们可以沿着第一个轴从每个n维切片做出一系列选择(z_indices
)。
此外:任何花哨的索引操作都将创建一个新数组,而不是原始数据的视图。在不创建全新数组的情况下,无法使用val_arr
对z_indices
进行索引。
答案 1 :(得分:4)
具有可读性,np.choose
看起来确实很棒。
如果性能至关重要,您可以计算线性指数,然后使用np.take
或使用带有.ravel()
的展平版本,并从val_arr
中提取这些特定元素。实现看起来像这样 -
def linidx_take(val_arr,z_indices):
# Get number of columns and rows in values array
_,nC,nR = val_arr.shape
# Get linear indices and thus extract elements with np.take
idx = nC*nR*z_indices + nR*np.arange(nR)[:,None] + np.arange(nC)
return np.take(val_arr,idx) # Or val_arr.ravel()[idx]
运行时测试并验证结果
来自here
的基于Ogrid的解决方案被制作为这些测试的通用版本,如下所示:
In [182]: def ogrid_based(val_arr,z_indices):
...: v_shp = val_arr.shape
...: y,x = np.ogrid[0:v_shp[1], 0:v_shp[2]]
...: return val_arr[z_indices, y, x]
...:
案例#1:数据量越小
In [183]: val_arr = np.random.rand(30,30,30)
...: z_indices = np.random.randint(0,30,(30,30))
...:
In [184]: np.allclose(z_indices.choose(val_arr),ogrid_based(val_arr,z_indices))
Out[184]: True
In [185]: np.allclose(z_indices.choose(val_arr),linidx_take(val_arr,z_indices))
Out[185]: True
In [187]: %timeit z_indices.choose(val_arr)
1000 loops, best of 3: 230 µs per loop
In [188]: %timeit ogrid_based(val_arr,z_indices)
10000 loops, best of 3: 54.1 µs per loop
In [189]: %timeit linidx_take(val_arr,z_indices)
10000 loops, best of 3: 30.3 µs per loop
案例#2:更大的数据化
In [191]: val_arr = np.random.rand(300,300,300)
...: z_indices = np.random.randint(0,300,(300,300))
...:
In [192]: z_indices.choose(val_arr) # Seems like there is some limitation here with bigger arrays.
Traceback (most recent call last):
File "<ipython-input-192-10c3bb600361>", line 1, in <module>
z_indices.choose(val_arr)
ValueError: Need between 2 and (32) array objects (inclusive).
In [194]: np.allclose(linidx_take(val_arr,z_indices),ogrid_based(val_arr,z_indices))
Out[194]: True
In [195]: %timeit ogrid_based(val_arr,z_indices)
100 loops, best of 3: 3.67 ms per loop
In [196]: %timeit linidx_take(val_arr,z_indices)
100 loops, best of 3: 2.04 ms per loop
答案 2 :(得分:3)
y,x = np.ogrid[0:3, 0:3]
print [z_indices, y, x]
[array([[1, 0, 2],
[0, 0, 1],
[2, 0, 1]]),
array([[0],
[1],
[2]]),
array([[0, 1, 2]])]
print val_arr[z_indices, y, x]
[[ 9 1 20]
[ 3 4 14]
[24 7 17]]
我不得不承认,多维花式索引可能会让人感到麻烦和混乱:)
答案 3 :(得分:2)
如果您的numpy> = 1.15.0,则可以使用numpy.take_along_axis
。就您而言:
result_array = numpy.take_along_axis(val_arr, z_indices.reshape((3,3,1)), axis=2)
那应该用一整段代码为您提供所需的结果。注意索引数组的大小。它需要具有与val_arr
相同的尺寸数(并且在前两个尺寸中具有相同的尺寸)。