提问人:Simon 提问时间:11/11/2023 最后编辑:Simon 更新时间:11/11/2023 访问量:66
使用元组进行多索引
Multi-indexing with tuples
问:
我有一个多维的.我知道第一个 N 维和最后一个 M 维的形状。例如,np.array
>>> n = (3,4,5)
>>> m = (6,)
>>> a = np.ones(n + m)
>>> a.shape
(3, 4, 5, 6)
使用元组作为索引可以快速索引,例如前 N 个维度,例如
>>> i = (1,1,2)
>>> a[i].shape
(6,)
使用 list 不会给我我需要的相同结果
>>> i = [1,1,2]
>>> a[i].shape
(3, 4, 5, 6)
但是我在进行多索引(检索/分配值)时遇到了麻烦。例如
>>> i = (1,1,2)
>>> j = (2,2,2)
我需要传递类似的东西
>>> a[[i, j]]
并得到 的输出形状。(2, 6)
相反,我得到了
>>> a[[i, j]].shape
(2, 3, 4, 5, 6)
或
>>> a[(i, j)].shape
(3, 5, 6)
我总是可以循环或更改索引事物的方式(例如使用 和 ),但是有没有更 pythonic 的方法来实现我需要的东西?np.reshape
np.unravel_index
编辑对于任意数量的索引,我都需要它,例如,
>>> i = (1,1,2)
>>> j = (2,2,2)
>>> k = (0,0,0)
...
答:
提取每个选择,然后将它们拼接成一个新数组?
>>> np.array([a[i], a[j]]).shape
(2, 6)
评论
indices = [i, j, k]
np.array([a[i] for i in indices])
让我们迂腐一点,以澄清每种情况下发生的事情。
In [19]: >>> n = (3,4,5)
...: >>> m = (6,)
加上元组(以及列表和字符串)连接它们:
In [20]: n+m
Out[20]: (3, 4, 5, 6)
In [21]: a=np.ones(n+m)
使用元组进行索引与单独输入每个标量相同。组成元组的是逗号,而不是 ()。实际上,解释器将元组传递给对象;它是对象自己的方法来解释它。(如果你给它们一个元组,列表会抱怨,但像这样的数组:) )。getitem
In [22]: a[1,1,2].shape
Out[22]: (6,)
有一个自动尾随切片。为了清楚起见,在下文中,我将包括这一点:
In [23]: a[1,1,2,:].shape
Out[23]: (6,)
In [24]: >>> i = (1,1,2)
...: >>> j = (2,2,2)
[i,j]
转换为数组 (2,3) 形状:
In [25]: np.array([i,j])
Out[25]:
array([[1, 1, 2],
[2, 2, 2]])
因此,该数组仅用于索引第一个维度。其余的都在特拉林。
In [26]: a[np.array([i,j]),:,:,:].shape
Out[26]: (2, 3, 4, 5, 6)
元组的元组:
In [27]: (i,j)
Out[27]: ((1, 1, 2), (2, 2, 2))
内部元组被转换为列表,或者更确切地说是数组。因此,两个索引一起广播,选择一个 (3,) 形状(将其视为“对角线”)
In [29]: a[[1,1,2],[2,2,2],:,:].shape
Out[29]: (3, 5, 6)
添加 ,将得到 (3,6) 形状。k
a[i,j,k]
我不知道你应该如何产生 (2,6) 形状i,j
等等,也许它相当于
In [32]: np.stack([a[i],a[j]]).shape
Out[32]: (2, 6)
或者等效地将 2 个选择与 连接起来。np.array
就像经验一样,这里有一个等价物:
In [45]: b=np.arange(3*4*5*6).reshape(a.shape)
In [46]: c=np.stack([b[i],b[j]])
In [47]: d=b[[1,2],[1,2],[2,2]]
In [48]: np.allclose(c,d)
Out[48]: True
因此,我们需要将元组转换为这个对对元组。i,j
In [55]: tuple([[i1,j1] for i1,j1 in zip(i,j)])
Out[55]: ([1, 2], [1, 2], [2, 2])
In [56]: tuple(np.array([i,j]).T.tolist())
Out[56]: ([1, 2], [1, 2], [2, 2])
In [57]: tuple(np.stack([i,j],1).tolist())
Out[57]: ([1, 2], [1, 2], [2, 2])
并带有第三个元组
In [58]: k=(0,0,0)
In [59]: tuple(np.stack([i,j,k],1).tolist())
Out[59]: ([1, 2, 0], [1, 2, 0], [2, 2, 0])
我们不需要 ,尽管它非常快:tolist
In [61]: b[tuple(np.stack([i,j,k],1))]
Out[61]:
array([[162, 163, 164, 165, 166, 167],
[312, 313, 314, 315, 316, 317],
[ 0, 1, 2, 3, 4, 5]])
评论
()
m = 6,
,
i, j, k
a[i, j, k]
i
j
k
:
我测试了 ShadowRanger 和 Chrysophylaxs 的解决方案,将它们与我最初的 ravel 解决方案进行了比较。我给它们计时,Chrysophylaxs'是最快的。
import time
import numpy as np
n = (3, 4, 5)
m = (2, 4, 6)
a = np.random.rand(*(n + m))
indices = [tuple(np.random.randint(n)) for _ in range(100)]
test = 100000
t1 = time.time()
for _ in range(test):
x = a[tuple(zip(*indices))]
t2 = time.time()
for _ in range(test):
y = np.array([a[i] for i in indices])
t3 = time.time()
for _ in range(test):
b = a.reshape(np.prod(n), np.prod(m))
j = [np.ravel_multi_index(i, n) for i in indices]
z = b[j].reshape(-1, *m)
t4 = time.time()
>>> print(t2 - t1, t3 - t2, t4 - t3)
>>> print(x.shape, y.shape, z.shape)
>>> assert(np.all(x - y == 0))
>>> assert(np.all(x - z == 0))
3.8938186168670654 4.831823110580444 37.469303131103516
(100, 2, 4, 6) (100, 2, 4, 6) (100, 2, 4, 6)
考虑以下指数列表:
idx = [
(1, 1, 2), # Your i
(2, 2, 2), # Your j
(0, 0, 0), # Your k
(1, 2, 1), # ...
(2, 0, 1), # extend as necessary
]
和你的数组 形状 .a
(3, 4, 5, 6)
当你写的时候,numpy是这样解释的:out = a[idx]
out = np.array([
[a[1], a[1], a[2]],
[a[2], a[2], a[2]],
[a[0], a[0], a[0]],
[a[1], a[2], a[1]],
[a[2], a[0], a[1]],
])
其中,例如,只是 的第一个子数组,因此具有 形状 !a[0]
a
(4, 5, 6)
因此,您只剩下表示形状数组(索引的形状)的内容,其中包含 !(...最终结果为 ,或 )。(5, 3)
(4, 5, 6)
a
(5, 3, 4, 5, 6)
np.shape(idx) + a.shape[1:]
相反,您想要的是以下内容:
out = np.array([
a[1, 1, 2],
a[2, 2, 2],
a[0, 0, 0],
a[1, 2, 1],
a[2, 0, 1],
])
在 numpy 中“矢量化”完成它的方法如下:
out = a[
[1, 2, 0, 1, 2], # [idx[0][0], idx[1][0], idx[2][0], ...]
[1, 2, 0, 2, 0], # [idx[0][1], idx[1][1], idx[2][1], ...]
[2, 2, 0, 1, 1] # [idx[0][2], idx[1][2], idx[2][2], ...]
]
该行为记录在索引指南中:
高级索引始终作为一个索引进行广播和迭代:
result[i_1, ..., i_M] == x[ind_1[i_1, ..., i_M], ind_2[i_1, ..., i_M], ..., ind_N[i_1, ..., i_M]]
若要将原始文件转换为此类索引器,可以使用此技巧。idx
tuple(zip(*idx))
Numpy的索引系统具有神奇的灵活性,但这种灵活性的代价是这些“简单”的任务变得不直观......至少在我看来;)
评论
a[ [1, 2], [1, 2], [2, 2] ]
a[tuple(zip(i, j))]
zip
tuple
zip
tuple
zip
list
tuple
tuple
tuple
numpy
tuple
a[*zip(i, j)]
tuple