Python 维度的适当子集上的numpy.ndarray枚举?

Python 维度的适当子集上的numpy.ndarray枚举?,python,numpy,Python,Numpy,(在这篇文章中,让np成为numpy的简写) 假设a是(n+k)维np.ndarray对象,对于某些整数n>1和k>1。(IOW,n+k>3是a.ndim的值)。我想在其前n个维度上枚举a;这意味着,在每次迭代中,枚举器/迭代器生成一对,其第一个元素是n个索引的元组ii,第二个元素是a[ii]处的k维子ndarray 诚然,编写一个函数来实现这一点并不困难(事实上,我在下面给出了这样一个函数的示例),但我想知道: numpy是否提供任何特殊语法或函数来执行这种类型的“部分”枚举 (通常,当我想迭

(在这篇文章中,让
np
成为
numpy
的简写)

假设
a
是(n+k)维
np.ndarray
对象,对于某些整数n>1和k>1。(IOW,n+k>3是
a.ndim
的值)。我想在其前n个维度上枚举
a
;这意味着,在每次迭代中,枚举器/迭代器生成一对,其第一个元素是n个索引的元组
ii
,第二个元素是
a[ii]
处的k维子
ndarray

诚然,编写一个函数来实现这一点并不困难(事实上,我在下面给出了这样一个函数的示例),但我想知道:

numpy
是否提供任何特殊语法或函数来执行这种类型的“部分”枚举

(通常,当我想迭代一个多维
np.ndarray
对象时,我使用
np.ndenumerate
,但在这里没有帮助,因为(据我所知)
np.ndenumerate
将迭代所有n+k维。)

假设上述问题的答案是肯定的,则有以下后续问题:


如果要迭代的n个维度不是连续的,那么情况如何


(在这种情况下,枚举器/迭代器在每次迭代时返回的一对元素的第一个元素将是r>n个元素的元组,其中一些元素将是表示“全部”的特殊值,例如
slice(None)
;这对元素的第二个元素仍然是长度为k的
ndarray
。)

谢谢


下面的代码有望澄清问题规范。函数
partial_enumerate
执行我想使用任何特殊
numpy
结构执行的操作。下面是n=k=2情况下的一个简单示例

输出的每一行都是“一对元组”,其中第一个元组表示
a
中n个坐标的部分集合,第二个元组表示这些部分坐标处
a
的k维子数组的形状;(根据数组的规则性,第二对的值对于所有行都是相同的):


相反,在这种情况下,迭代
np.ndenumerate(a)
将导致
a.size
迭代,每个迭代访问
a
的单个单元格,我想您正在
numpy
中寻找函数。只需获取所需子阵列的一部分:

from numpy import *

# Create the array
A = zeros((2,3,4,5))

# Identify the subindex you're looking for
idx = ndindex(A.shape[:2])

# Iterate through the array
[(x, A[x].shape) for x in idx]
这就产生了预期的结果:

[((0, 0), (4, 5)), ((0, 1), (4, 5)), ((0, 2), (4, 5)), ((1, 0), (4, 5)), ((1, 1), (4, 5)), ((1, 2), (4, 5))]

可以使用numpy广播规则生成笛卡尔乘积。
numpy.ix
函数创建适当数组的列表。这相当于以下内容:

>>> def pseudo_ix_gen(*arrays):
...     base_shape = [1 for arr in arrays]
...     for dim, arr in enumerate(arrays):
...         shape = base_shape[:]
...         shape[dim] = len(arr)
...         yield numpy.array(arr).reshape(shape)
... 
>>> def pseudo_ix_(*arrays):
...     return list(pseudo_ix_gen(*arrays))
或者,更简洁地说:

>>> def pseudo_ix_(*arrays):
...     shapes = numpy.diagflat([len(a) - 1 for a in arrays]) + 1
...     return [numpy.array(a).reshape(s) for a, s in zip(arrays, shapes)]
结果是可广播数组的列表:

>>> numpy.ix_(*[[2, 4], [1, 3], [0, 2]])
[array([[[2]],

       [[4]]]), array([[[1],
        [3]]]), array([[[0, 2]]])]
将此结果与
numpy.ogrid
的结果进行比较:

>>> numpy.ogrid[0:2, 0:2, 0:2]
[array([[[0]],

       [[1]]]), array([[[0],
        [1]]]), array([[[0, 1]]])]
如您所见,这是相同的,但是
numpy.ix
允许您使用非连续索引。现在,当我们应用numpy广播规则时,我们得到一个笛卡尔积:

>>> list(numpy.broadcast(*numpy.ix_(*[[2, 4], [1, 3], [0, 2]])))
[(2, 1, 0), (2, 1, 2), (2, 3, 0), (2, 3, 2), 
 (4, 1, 0), (4, 1, 2), (4, 3, 0), (4, 3, 2)]
如果我们不将
numpy.ix_uu
的结果传递给
numpy.broadcast
,而是使用它对数组进行索引,我们得到以下结果:

>>> a = numpy.arange(6 ** 4).reshape((6, 6, 6, 6))
>>> a[numpy.ix_(*[[2, 4], [1, 3], [0, 2]])]
array([[[[468, 469, 470, 471, 472, 473],
         [480, 481, 482, 483, 484, 485]],

        [[540, 541, 542, 543, 544, 545],
         [552, 553, 554, 555, 556, 557]]],


       [[[900, 901, 902, 903, 904, 905],
         [912, 913, 914, 915, 916, 917]],

        [[972, 973, 974, 975, 976, 977],
         [984, 985, 986, 987, 988, 989]]]])
然而,买主要注意。可广播数组对于索引很有用,但如果您确实想枚举这些值,最好使用
itertools.product

>>> %timeit list(itertools.product(range(5), repeat=5))
10000 loops, best of 3: 196 us per loop
>>> %timeit list(numpy.broadcast(*numpy.ix_(*([range(5)] * 5))))
100 loops, best of 3: 2.74 ms per loop
因此,如果您要合并for循环,那么
itertools.product
可能会更快。不过,您可以使用上述方法在纯numpy中获得一些类似的数据结构:

>> pgrid_idx = numpy.ix_(*[[2, 4], [1, 3], [0, 2]])
>>> sub_indices = numpy.rec.fromarrays(numpy.indices((6, 6, 6)))
>>> a[pgrid_idx].reshape((8, 6))
array([[468, 469, 470, 471, 472, 473],
       [480, 481, 482, 483, 484, 485],
       [540, 541, 542, 543, 544, 545],
       [552, 553, 554, 555, 556, 557],
       [900, 901, 902, 903, 904, 905],
       [912, 913, 914, 915, 916, 917],
       [972, 973, 974, 975, 976, 977],
       [984, 985, 986, 987, 988, 989]])
>>> sub_indices[pgrid_idx].reshape((8,))
rec.array([(2, 1, 0), (2, 1, 2), (2, 3, 0), (2, 3, 2), 
           (4, 1, 0), (4, 1, 2), (4, 3, 0), (4, 3, 2)], 
          dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8')])
>pgrid_idx=numpy.ix_(*[2,4],[1,3],[0,2]]
>>>sub_index=numpy.rec.fromArray(numpy.index((6,6,6)))
>>>a[pgrid_idx].重塑((8,6))
数组([[468469470471471472473],
[480, 481, 482, 483, 484, 485],
[540, 541, 542, 543, 544, 545],
[552, 553, 554, 555, 556, 557],
[900, 901, 902, 903, 904, 905],
[912, 913, 914, 915, 916, 917],
[972, 973, 974, 975, 976, 977],
[984, 985, 986, 987, 988, 989]])
>>>子索引[pgrid_idx]。重塑((8,))
记录数组([(2,1,0),(2,1,2),(2,3,0),(2,3,2),
(4, 1, 0), (4, 1, 2), (4, 3, 0), (4, 3, 2)], 

dtype=[('f0','“如果要迭代的维度不连续,该怎么办?”我不确定纯numpy数组是否可以实现这一点。与标准python列表不同,整个数组存储在连续的内存块中。例如,您是指结果为
[row.shape for row in a]
=
[1,2,1,3,…]
?“在这种情况下,枚举器/迭代器在每次迭代时返回的对的第一个元素将是一个由r>n个元素组成的元组,其中一些元素将是一个表示“all”的特殊值,例如slice(None);该对的第二个元素仍然是长度为k的ndarray。”这对我来说没有意义,因为它创建了一个不一致的习惯用法。它不只是由单个项索引组成,而是由单个项索引和索引序列组成。因此,它将不再是索引和子数组之间的双射;一个索引可以指代多个子数组。我相信这将导致n长度为k+j的数组,其中j是索引元组中的序列数(即非单项索引)。我相信您的
产品网格
numpy.ix_
@Bago是一样的,与重新发明轮子不同,是吗?
>>> %timeit list(itertools.product(range(5), repeat=5))
10000 loops, best of 3: 196 us per loop
>>> %timeit list(numpy.broadcast(*numpy.ix_(*([range(5)] * 5))))
100 loops, best of 3: 2.74 ms per loop
>> pgrid_idx = numpy.ix_(*[[2, 4], [1, 3], [0, 2]])
>>> sub_indices = numpy.rec.fromarrays(numpy.indices((6, 6, 6)))
>>> a[pgrid_idx].reshape((8, 6))
array([[468, 469, 470, 471, 472, 473],
       [480, 481, 482, 483, 484, 485],
       [540, 541, 542, 543, 544, 545],
       [552, 553, 554, 555, 556, 557],
       [900, 901, 902, 903, 904, 905],
       [912, 913, 914, 915, 916, 917],
       [972, 973, 974, 975, 976, 977],
       [984, 985, 986, 987, 988, 989]])
>>> sub_indices[pgrid_idx].reshape((8,))
rec.array([(2, 1, 0), (2, 1, 2), (2, 3, 0), (2, 3, 2), 
           (4, 1, 0), (4, 1, 2), (4, 3, 0), (4, 3, 2)], 
          dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8')])