【问题标题】:Efficiently using Numpy to assign function values to array有效地使用 Numpy 将函数值分配给数组
【发布时间】:2018-01-24 06:53:09
【问题描述】:

我有兴趣找到使用 Numpy 在 Python3.6 中进行简单操作的最快方法。我希望创建一个函数并从给定数组到函数值数组。这是一个使用map 的简化代码:

import numpy as np
def func(x):
    return x**2
xRange = np.arange(0,1,0.01)
arr_func = np.array(list(map(func, xRange)))

但是,由于我使用复杂的函数运行它并使用大型数组,因此运行速度对我来说非常重要。有没有已知更快的方法?

编辑我的问题与this 的问题不同,因为我问的是从函数而不是生成器分配。

【问题讨论】:

  • 实际的实现会涉及到具体的优化。所以,没有看到它,一般情况下没有神奇的方法。
  • 谢谢@Divakar 我确实在寻找一种更快的方法来处理一般情况。
  • 为什么选择 xRange 和 pRange?在这种特殊情况下,**2 操作已经过矢量化,因此您通过执行映射而不是仅执行 arr_func = func(xRange) 会受到惩罚。在一般情况下,您必须尽可能多地尝试和利用矢量化操作。
  • 感谢@IgnacioVergaraKausel,pRange 是粘贴错误。我删除了它。
  • 只是补充一下,如果你刚刚做了func(xRange),我会得到 44.8 微秒,而你的地图列出到数组需要 33.4 毫秒(对于 100000 个随机元素的数组)。

标签: python arrays python-3.x numpy


【解决方案1】:

查看相关的How do I build a numpy array from a generator?,其中最引人注目的选项似乎是预分配 numpy 数组和设置值,而不是创建一次性中间列表。

arr_func = np.empty(len(xRange))
for i in range(len(xRange)):
  arr_func[i] = func(xRange[i])

【讨论】:

    【解决方案2】:

    对于一个无法用已编译的 numpy 函数重写的复杂函数,我们无法大幅提高速度。

    使用需要标量的math 方法定义一个函数,例如:

    def func(x):
        return math.sin(x)**2 + math.cos(x)**2
    
    In [868]: x = np.linspace(0,np.pi,10000)
    

    做一个简单的列表理解作为参考:

    In [869]: np.array([func(i) for i in x])
    Out[869]: array([ 1.,  1.,  1., ...,  1.,  1.,  1.])
    
    In [870]: timeit np.array([func(i) for i in x])
    13.4 ms ± 211 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    您的列表地图稍微快一点:

    In [871]: timeit np.array(list(map(func, x)))
    12.6 ms ± 12.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    对于这样的一维数组,np.array 可以替换为np.fromiter。它也适用于生成器,包括 Py3 map

    In [875]: timeit np.fromiter(map(func, x),float)
    13.1 ms ± 176 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    这样就可以避免先创建整个列表可能带来的时间损失。但在这种情况下,它没有帮助。

    另一个迭代器是np.frompyfuncnp.vectorize 使用它,但通常速度更快且开销更少。它返回一个 dtype 对象数组:

    In [876]: f = np.frompyfunc(func, 1, 1)
    In [877]: f(x)
    Out[877]: array([1.0, 1.0, 1.0, ..., 1.0, 1.0, 1.0], dtype=object)
    In [878]: timeit f(x)
    11.1 ms ± 298 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    In [879]: timeit f(x).astype(float)
    11.2 ms ± 85.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    速度略有提高。我注意到 1000 项 x 有了更多改进。如果您的问题需要多个可以相互广播的数组,这会更好。

    分配给预先分配的out 数组可以节省内存,并且通常建议作为列表追加迭代的替代方法。但是在这里它并没有提高速度:

    In [882]: %%timeit 
         ...: out = np.empty_like(x)
         ...: for i,j in enumerate(x): out[i]=func(j)
    16.1 ms ± 308 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    enumerate的使用比range迭代稍快)。

    【讨论】:

      猜你喜欢
      • 2017-01-29
      • 2020-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多