在numpy ndarray中沿指定轴循环向量的有效方法是什么?

时间:2016-07-05 15:40:51

标签: python arrays numpy

我正在通过沿着numpy ndarray的轴(可以是任何轴)循环向量来处理数据(可以是任何维度)。

我没有直接使用数组,因为数据并不完美。它需要对每个载体进行质量控制。如果不好,矢量将由零(或nan)填充,并且没有真正的处理。

我发现this Q相似,但我的问题要困难得多,因为

  1. ndim是任意的。
  2. 对于3D数组,我可以像{<1}}一样沿着axis 1采集向量

     x = np.arange(24).reshape(2,3,4)
     for i in range(x.shape[0]):
         for k in range(x.shape[2]):
             process(x[i,:,k])
    

    但如果ndim和拍摄的axis未修复,如何拍摄矢量?

    1. 取向量的轴是任意的。
    2. 我正在考虑的一种可能方式是

       y = x.swapaxes(ax,-1)
       # loop over vectors along last axis
       for i in np.ndindex(y.shape[:-1]):
           process(y[i+(slice(None),)])
       # then swap back
       z = y.swapaxes(ax,-1)
      

      但我怀疑这种方法的效率。

2 个答案:

答案 0 :(得分:2)

测试效率的最佳方法是对现实示例进行时间测试。但是%timeit(ipython)对玩具示例的测试是一个开始。

根据回答相似的经验,如果你必须迭代&#39;问题,时间上没有多大区别。 np.frompyfunc具有适度的速度优势 - 但其pyfunc需要标量,而不是数组或切片。 (np.vectorize是这个函数的一个更好的API,有点慢。)

但是在这里你要传递一个数组的1d切片到你的函数,同时迭代所有其他维度。我不认为替代迭代方法存在很大差异。

swapaxistransposeravel等操作速度很快,通常只是创建一个具有不同形状和步幅的新视图。

np.ndindex使用np.nditer(使用多索引平面)迭代一系列维度。在C代码中使用nditer时速度很快,但在Python代码中使用时并不特别。

np.apply_along_axis创建一个(i,j,:,k)索引元组,并逐步调整变量。这是一个很好的通用方法,但并没有做任何特别的事情来加快速度。 itertools.product是另一种生成索引的方法。

但通常情况下,迭代机制不会降低速度,而是重复调用您的函数。您可以使用一个简单的函数来测试迭代机制,例如

def foo(x):
   return x

===================

您不需要swapaxes使用ndindex;你可以用它来迭代任何轴组合。

例如,制作一个3d数组,并沿中间维度求和:

In [495]: x=np.arange(2*3*4).reshape(2,3,4)

In [496]: N=np.ndindex(2,4)

In [497]: [x[i,:,k].sum() for i,k in N]
Out[497]: [12, 15, 18, 21, 48, 51, 54, 57]

In [498]: x.sum(1)
Out[498]: 
array([[12, 15, 18, 21],
       [48, 51, 54, 57]])

我认为它不会对速度产生影响;代码更简单。

===================

另一个可能的工具是np.ma,蒙版数组。将那些元素标记为蒙版(因为它们是nan0)。它的代码可以评估summeanproduct之类的内容,使掩盖的值不会对解决方案造成损害。

再次使用3d数组:

In [517]: x=np.arange(2*3*4).reshape(2,3,4)

添加一些不好的值:

In [518]: x[1,1,2]=99    
In [519]: x[0,0,:]=99

这些价​​值让正常sum

陷入困境
In [520]: x.sum(axis=1)
Out[520]: 
array([[111, 113, 115, 117],
       [ 48,  51, 135,  57]])

但是,如果我们屏蔽它们,它们就会被过滤掉&#39;解决方案(在这种情况下,它们暂时设置为0)

In [521]: xm=np.ma.masked_greater(x,50)

In [522]: xm
Out[522]: 
masked_array(data =
 [[[-- -- -- --]
  [4 5 6 7]
  [8 9 10 11]]

 [[12 13 14 15]
  [16 17 -- 19]
  [20 21 22 23]]],
             mask =
 [[[ True  True  True  True]
 ...
  [False False False False]]],
       fill_value = 999999)

In [523]: xm.sum(1)
Out[523]: 
masked_array(data =
 [[12 14 16 18]
 [48 51 36 57]],
 ...)

答案 1 :(得分:1)

您考虑过numpy.nditer吗?

另见Iterating over arrays

编辑:也许另一种解决方案就是使用:

  • flatten
  • ravel
  • flat 1D迭代器

因此,无论数组的初始暗淡,您都可以迭代1D,然后将reshape数组转换为其原始形状。