【问题标题】:Numpy: Substituting for Iterating over Arrays - apply_over_axisNumpy:代替迭代数组 - apply_over_axis
【发布时间】:2013-01-04 04:48:11
【问题描述】:

据我了解,np.apply_over_axis 是迭代 Numpy 数组的可行替代方案,因为使用 python 方式执行此操作时会出现瓶颈,从而使事情变慢;然而,似乎迭代需要大约 9% 的时间 apply_over_axis !捎带这个previous post,我决定为自己做一个快速的计时:

import numpy as np
import timeit

def triv():
     ial = [i for i in xrange(100)]

def super(fluous):
     return fluous

>>> print (timeit.timeit("triv()", setup="from __main__ import triv"))
12.3305490909
>>> print (timeit.timeit("np.apply_along_axis(super, 0, np.arange(100))", setup="from __main__ import np, super"))
130.721563921

为什么会这样?我真的不太了解timeit 的复杂性(或者关于timeit 的任何事情),但我认为我的例子足够简单。我想知道是否有人找到了一个好的解决方法,因为我在非常大的数组中迭代行的笛卡尔积的实际问题是如此缓慢以至于阻碍了进展。

提前致谢。

【问题讨论】:

    标签: python numpy iteration


    【解决方案1】:

    首先,您的示例很奇怪,因为一维数组上的apply_over_axis 只会将整个数组作为单个参数传递。它不会为每个元素重复调用。为此,您需要vectorize

    不过,更一般地说,numpy 并不能真正加速任意 Python 函数的应用。 numpy 的主要优点是它提供了许多数学函数的自己的实现,并且 那些 很快。它不能神奇地获取任何功能,只是让它运行得更快。

    此外,您的示例并不完全平行。一方面,您不会在两者中调用相同的函数。更具体地说,您的测试都包括在定时测试中创建输入——也就是说,您在测试的定时部分中创建 xrange(100)np.arange(100)。因此,您测量的部分内容是创建 Numpy 数组比创建 xrange 对象要慢:

    >>> timeit.timeit("np.arange(100)", "import numpy as np")
    0.95243776
    >>> timeit.timeit("xrange(100)", "import numpy as np")
    0.2129926399999995
    

    这大约是 5 倍。但在实际应用程序中,您几乎可以肯定已经创建了输入数组,因此这不是一个现实的测试。

    使用并行测试,我发现普通列表版本的速度只有大约两倍:

    def doNothing(crud):
        return crud
    
    >>> timeit.timeit("np.vectorize(doNothing)(thing)", "import numpy as np; from __main__ import doNothing; thing = np.arange(100)")
    49.13036320000003
    >>> timeit.timeit("[doNothing(x) for x in thing]", "import numpy as np; from __main__ import doNothing; thing = np.arange(100)")
    25.873566400000072
    

    此外,如果您应用的是 numpy 函数,则 numpy 版本可以大大加快:

    >>> timeit.timeit("np.log(thing)", "import numpy as np; import math; from __main__ import doNothing; thing = np.arange(100)+1")
    3.2039433600000393
    >>> timeit.timeit("[math.log(x) for x in thing]", "import numpy as np; import math; from __main__ import doNothing; thing = np.arange(100)+1")
    37.74219519999997
    

    这个故事的寓意是 numpy 真的是 NUMpy - 它是为进行 numerical 计算而设计的,并且它具有快速计算的功能。这不仅仅是加速你所有循环的东西。如果您只是有大量对象要对其应用任意函数,那么 numpy 不太可能加快您的代码速度,甚至可能减慢它的速度。 (它对于非数字数据仍然非常有用,因为它对多维数组的复杂索引等功能很方便,并且可能比使用嵌套列表等的等效 Python 结构更快。它只是对加速循环没有用处这些结构。)

    【讨论】:

    • +1 -- 我还要补充一点,Numpy 只能很好地处理固定数据数值计算。例如,如果您想要任意大小的整数,最好坚持使用纯 python,因为 numpy 只会增加一些开销。此外,如果 Op 想要在他的循环中加快速度,也许他可以尝试 PyPy,它可能实现一些速度提升。
    • @BrenBarn 谢谢。 这很有帮助,让我更好地了解它是如何工作的。
    • +1。您能否列出有助于“复杂索引到多维数组”的 numpy 函数?他们四处散落。 (新问题?似乎太模糊了)
    • @Denis:主要指的不是函数,而是使用[] 直接索引(例如,使用布尔数组进行索引的能力)。见here
    【解决方案2】:

    如果你进入你的numpy安装目录,你可以在\ma子目录的extras.py文件中找到np.apply_along_axis的python代码。

    其迟缓的原因是,当它将您的函数应用于沿选定轴的数组的一维切片时,它将结果存储在dtype=object 的数组中并跟踪所有返回dtypes。一旦完成处理,它会选择列表中最大的dtype,并在返回之前重铸整个内容。这使它成为一个非常健壮且非常缓慢的代码。一般来说,numpy中所有的functional programming例程最好避免:如果需要使用,最好不要numpy,因为速度不会有。

    这并不意味着您的问题无法通过标准、快速、numpy 功能正确解决。你为什么不把它作为一个问题发布?

    【讨论】:

    • 这几乎是一个如何让这个更快的问题。这是否符合 Stackoverflow 的问题标准?
    • @NoobSaibot 至少有一半带有numpy 标签的问题属于此类。如果您要发布 100 行代码,那么也许codereview.stackexchange.com 会是一个更好的提问地方。但是,如果您可以充分确定问题的范围,我认为这不是问题。
    • 我听取了您的建议并提出了具体的加速问题here。感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 2013-05-05
    • 2011-10-03
    • 2016-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多