【问题标题】:Faster alternative to using the `map` function使用 `map` 函数的更快替代方案
【发布时间】:2017-07-10 10:39:55
【问题描述】:

我有一个函数f,例如:

def f(x):
    return x**2

并希望获得一个由f 组成的数组,该数组在一个区间内进行评估,例如单位区间 (0,1)。我们可以这样做:

import numpy as np
X = np.arange(0,1,0.01)
arr = np.array(list(map(f, X)))

但是,当函数很复杂时,最后一行非常耗时(在我的例子中,它涉及一些积分)。有没有办法更快地做到这一点?我很高兴有一个不优雅的解决方案 - 重点是速度。

【问题讨论】:

  • 如果X 的长度不是很大,我不会担心map() 的开销。花时间优化您的f()
  • 这不是您问题的答案,但是仅仅为了创建一个数组而构建一个列表是非常低效的。您可以使用fromiter() 来避免这样做。您需要知道长度,但鉴于它是一个映射操作,您知道它将与您映射的数组相同。但是,如果您想使用 Numpy 提高速度,则需要使用 numpy 操作 - 映射本质上意味着 python 端循环和操作,这会更慢。
  • @zwer 同意。您可以使用列表理解获得一些加速,但它不会显着改变整体运行时间。查看我为回答 this question 所做的一些实验
  • @GarethLatty 实际上,我认为如果您只是将代码重构为一个 for 循环,该循环采用 np.empty 数组并通过索引分配正确的值,它会比 fromiter 更快,不确定尽管。 fromiter 通常很慢,numpy 数组几乎是 C 数组,并且零碎地构建它根本没有效率。如果在后台,fromiter 将迭代器消耗到列表中,我不会感到惊讶......
  • @juanpa.arrivillaga 文档说:'Specify count to improve performance. It allows fromiter to pre-allocate the output array, instead of resizing it on demand.' - 听起来在这种情况下它会做最好的事情 - 至少与制作列表或多次调整大小相比。

标签: python arrays python-3.x numpy dictionary


【解决方案1】:

您可以使用列表推导来稍微减少运行时间。

arr = [f(x) for x in range(0, 5)] # range is the interval

这应该可行。不过,它只会稍微减少运行时间。除非您在 map() 中使用非常大的数字,否则您不必担心运行时间。

【讨论】:

    【解决方案2】:

    如果f 太复杂以至于不能用编译后的数组操作来表示,只能采用标量,我发现frompyfunc 的性能最好(与显式循环相比大约是2 倍)

    In [76]: def f(x):
        ...:     return x**2
        ...: 
    
    In [77]: foo = np.frompyfunc(f,1,1)
    
    In [78]: foo(np.arange(4))
    Out[78]: array([0, 1, 4, 9], dtype=object)
    
    In [79]: foo(np.arange(4)).astype(int)
    Out[79]: array([0, 1, 4, 9])
    

    它返回 dtype 对象,所以需要一个astypenp.vectorize 也使用它,但速度有点慢。两者都可以推广到各种形状的输入数组。

    对于一维结果 fromitermap(没有 list)部分一起使用:

    In [84]: np.fromiter((f(x) for x in range(4)),int)
    Out[84]: array([0, 1, 4, 9])
    
    In [86]: np.fromiter(map(f, range(4)),int)
    Out[86]: array([0, 1, 4, 9])
    

    您必须根据实际情况自行安排时间。

    【讨论】:

      【解决方案3】:

      使用对整个数组进行操作的操作。例如,使用一个仅对输入进行平方的函数(根据您的示例稍作修正):

      def f(x):
          return x**2
      

      那你就这么做

      arr = f(X)
      

      因为 NumPy 定义了像 ** 这样的运算符来一次对整个数组进行操作。

      您的实际功能可能并不那么简单。你说有积分;要使整个数组操作与之一起工作,您可能必须以不同的方式传递参数或更改用于计算积分的内容。不过,总的来说,整个数组操作将大大优于任何需要在循环中调用 Python 级代码的操作。

      【讨论】:

        【解决方案4】:

        你可以试试numpy.vectorize。将函数应用于列表或数组是非常好的方法

        import numpy as np
        
        def foo(x):
            return x**2
        
        foo = np.vectorize(foo)
        arr = np.arange(10)
        
        In [1]: foo(arr)                                                                                    
        Out[1]: array([ 0,  1,  4,  9, 16, 25, 36, 49, 64, 81])  
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-07-19
          • 1970-01-01
          • 2018-02-23
          • 2018-12-11
          • 2012-07-05
          • 2012-01-23
          • 2013-07-13
          相关资源
          最近更新 更多