【问题标题】:What are the efficient ways to loop over vectors along a specified axis in numpy ndarray?在numpy ndarray中沿指定轴循环向量的有效方法是什么?
【发布时间】:2016-07-05 15:40:51
【问题描述】:

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

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

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

  1. ndim 是任意的。

对于 3D 数组,我可以像这样沿 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. 获取向量的轴是任意的。

我正在考虑的一种可能方式是

 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)

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

【问题讨论】:

  • 最有效的方法是迭代/循环,即修改(如果尚不适用)process 以一次性处理所有切片。那么,您是否使用特定的 process 函数?如果没有,请查看np.vectorize 我认为。
  • 我没有直接在数组上工作,因为数据数组并不完美。它需要对每个载体进行质量控制。如果不好,向量将被零(或nan)填充。
  • 取决于函数process和循环的数量,使用numbajit装饰器可能是值得的
  • 您能否详细说明为什么要以线性向量而不是整个数组来处理数据
  • 在最后一个示例中您不需要 swapaxes - 请参阅我的编辑。

标签: python arrays numpy


【解决方案1】:

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

根据回答类似“如果必须重复”问题的经验,时间上没有太大差异。 np.frompyfunc 具有适度的速度优势 - 但它的 pyfunc 采用标量,而不是数组或切片。 (np.vectorize 是这个函数的更好的 API,但速度有点慢)。

但是在这里,您希望将数组的一维切片传递给您的函数,同时遍历所有其他维度。我认为替代迭代方法没有太大区别。

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

np.ndindex 使用np.nditer(带有多索引平面)迭代一系列维度。 nditer 在 C 代码中使用时速度很快,但在 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]])

但如果我们屏蔽它们,它们就会从解决方案中“过滤掉”(在这种情况下,它们被临时设置为 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]],
 ...)

【讨论】:

    【解决方案2】:

    你考虑过numpy.nditer吗?

    另见Iterating over arrays

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

    • flatten
    • ravel
    • flat 一维迭代器

    因此,无论数组的初始暗度如何,您都可以像 1D 一样迭代,然后 reshape 将数组恢复到其原始形状。

    【讨论】:

    • 我使用来自stackoverflow.com/questions/29493183/…np.ndindex 得到了类似的想法,但它也提到nditer 甚至比for循环慢。
    • 其实numpy的内置函数mean, sum, fft, ...不管ndimaxis是什么都可以处理ndarray。但我不明白他们是怎么做到的,即使我看了源代码。
    • 我认为它们实际上包装了预编译的 C 代码(其中确实发生了循环),请参阅 What is numpy。您是否考虑过使用一些 numpy 的矢量化函数(例如,参见 docs.scipy.org/doc/numpy/reference/routines.logic.html)和真正强大的 numpy indexing 检查数据的可能性?如果您确实需要循环,也许您应该考虑Cython 并使用它来编写可从 Python 本地调用的循环 C 代码
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-30
    • 2015-07-13
    • 1970-01-01
    • 2017-12-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多