【问题标题】:Iterating over arbitrary dimension of numpy.array遍历 numpy.array 的任意维度
【发布时间】:2010-12-08 01:31:48
【问题描述】:

是否有函数可以在 numpy 数组的任意维度上获取迭代器?

迭代第一个维度很容易......

In [63]: c = numpy.arange(24).reshape(2,3,4)

In [64]: for r in c :
   ....:     print r
   ....: 
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
[[12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]

但是在其他维度上迭代更难。比如最后一个维度:

In [73]: for r in c.swapaxes(2,0).swapaxes(1,2) :
   ....:     print r
   ....: 
[[ 0  4  8]
 [12 16 20]]
[[ 1  5  9]
 [13 17 21]]
[[ 2  6 10]
 [14 18 22]]
[[ 3  7 11]
 [15 19 23]]

我正在制作一个生成器来自己执行此操作,但我很惊讶没有一个名为 numpy.ndarray.iterdim(axis=0) 的函数可以自动执行此操作。

【问题讨论】:

    标签: python numpy loops


    【解决方案1】:

    您提出的建议很快,但可以通过更清晰的表格提高易读性:

    for i in range(c.shape[-1]):
        print c[:,:,i]
    

    或者,更好(更快、更通用、更明确):

    for i in range(c.shape[-1]):
        print c[...,i]
    

    但是,上面的第一种方法似乎比swapaxes() 方法慢两倍:

    python -m timeit -s 'import numpy; c = numpy.arange(24).reshape(2,3,4)' \
        'for r in c.swapaxes(2,0).swapaxes(1,2): u = r'
    100000 loops, best of 3: 3.69 usec per loop
    
    python -m timeit -s 'import numpy; c = numpy.arange(24).reshape(2,3,4)' \
        'for i in range(c.shape[-1]): u = c[:,:,i]'
    100000 loops, best of 3: 6.08 usec per loop
    
    python -m timeit -s 'import numpy; c = numpy.arange(24).reshape(2,3,4)' \
        'for r in numpy.rollaxis(c, 2): u = r'
    100000 loops, best of 3: 6.46 usec per loop
    

    我猜这是因为swapaxes() 不复制任何数据,并且因为c[:,:,i] 的处理可能通过通用代码完成(处理: 被更复杂的切片替换的情况) .

    但是请注意,更明确的第二个解决方案 c[...,i] 既清晰又快速:

    python -m timeit -s 'import numpy; c = numpy.arange(24).reshape(2,3,4)' \
        'for i in range(c.shape[-1]): u = c[...,i]'
    100000 loops, best of 3: 4.74 usec per loop
    

    【讨论】:

    • 如果目标是迭代最后一个维度,为什么不使用for r in range c.T或更一般的c.transpose?另外,从 numpy 1.10 开始,应该可以使用np.moveaxis(c, dim, 0)
    • 转置通常不会给出所需的行为,因为轴都是倒置的(最后一个轴变成第二个,等等),所以在给出结果时你需要另一个转置:双转置是不必要的跳跃。 moveaxis 听起来不错,但转换添加了一行代码:尚不清楚是否值得将最后一个轴移到前面,因为 NumPy 直接让您可以访问它(通过此答案中的代码)。
    • "moveaxis 听起来不错,但是转换添加了一行代码:不清楚是否值得移动前面的最后一个轴" 怎么样?只是for u in np.moveaxis(c, -1, 0)。事实上,我测试了 python -m timeit -s 'import numpy; c = numpy.zeros((32, 32, 1024))' 'for i in range(c.shape[-1]): u = c[...,i]'python -m timeit -s 'import numpy; c = numpy.zeros((32, 32, 1024))' 'for u in numpy.moveaxis(c, -1, 0): pass',后者在 Ryzen 3900 CPU 上的速度是后者的两倍。
    【解决方案2】:

    我会使用以下内容:

    c = numpy.arange(2 * 3 * 4)
    c.shape = (2, 3, 4)
    
    for r in numpy.rollaxis(c, 2):
        print(r)
    

    rollaxis 函数在数组上创建一个新视图。在这种情况下,它将轴 2 移动到前面,相当于操作c.transpose(2, 0, 1)

    【讨论】:

    • +1:相当直接,但不幸的是比简单的c[:,:,i] 方法慢一点(不知道为什么)。
    【解决方案3】:

    因此,正如您所展示的,您可以轻松地迭代第一个维度。对任意维度执行此操作的另一种方法是使用 numpy.rollaxis() 将给定维度带到第一个维度(默认行为),然后使用返回的数组(这是一个视图,因此速度很快)作​​为迭代器.

    In [1]: array = numpy.arange(24).reshape(2,3,4)
    
    In [2]: for array_slice in np.rollaxis(array, 1):
       ....:     print array_slice.shape
       ....:
    (2, 4)
    (2, 4)
    (2, 4)
    

    编辑:我将评论我向 numpy 提交了一个 PR 来解决这个问题:https://github.com/numpy/numpy/pull/3262。共识是这还不足以添加到 numpy 代码库中。我认为使用 np.rollaxis 是最好的方法,如果你想要一个交互器,请将它包装在 iter() 中。

    【讨论】:

      【解决方案4】:

      我猜没有功能。当我编写函数时,我最终采用了 EOL 也建议的迭代。对于未来的读者,这里是:

      def iterdim(a, axis=0) :
        a = numpy.asarray(a);
        leading_indices = (slice(None),)*axis
        for i in xrange(a.shape[axis]) :
          yield a[leading_indices+(i,)]
      

      【讨论】:

      • 标准 NumPy 语法 a[..., i] 会更轻巧,并且不需要 leading_indices
      • @EOL 但这仅适用于最后一个轴,leading_indices 更通用...
      • 好点@lukas:最初的问题确实提到了“在任意维度上”迭代——而我的想法是在最后一个维度上进行积分。
      【解决方案5】:

      您可以使用 numpy.shape 获取尺寸,然后使用 range 对其进行迭代。

      n0, n1, n2 = numpy.shape(c)
      
      for r in range(n0):
          print(c[r,:,:])
      

      【讨论】:

        【解决方案6】:

        以下正是您要查找的内容:

        for x in np.moveaxis(X, axis, 0):
        

        【讨论】:

          猜你喜欢
          • 2012-04-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-07-20
          • 2012-03-21
          • 1970-01-01
          • 2014-07-08
          • 1970-01-01
          相关资源
          最近更新 更多