【问题标题】:how to speed up the algorithm about boolean如何加快关于布尔的算法
【发布时间】:2018-04-07 04:21:00
【问题描述】:

如果我有一个非常大的数据,我想找出一些特定的元素并将它们从布尔转换为数字。比如我要查找元素是否在区间(0.3,0.4)内,将True转换为1,将False转换为0

i=np.random.rand(1000,1000,1000)
j=((0.3<i)*(i<0.4))*1

j=((0.3&lt;i)&amp;(i&lt;0.4))*1 和上面的表达式一样吗?

我知道 bool*bool 很耗时并且会占用大量内存,bool 转换为数字也是如此。那么我怎样才能播种算法并节省内存呢?有没有办法快速评估0.3&lt;i&lt;0.4

【问题讨论】:

  • 如果你想知道它是否以同样的方式工作,不要问我们:问 Python。运行一组测试。 Python 运行时系统毕竟是最终权威,我们不是测试服务。
  • 您在寻找类似numexpr 的东西吗?你总是可以编写一个 C 扩展来高效地执行它...
  • C 扩展?我可以控制可以使用多少内存吗?

标签: python numpy optimization boolean


【解决方案1】:

是的,对于布尔数组&amp;* 是相同的,因为如果两个操作数都是True,则两者都是True,否则False

您已经发现每个操作都会创建一个临时数组(尽管新的 NumPy 版本可能会在这方面进行优化),因此您有一个临时布尔数组用于每个 &lt;,一个用于 * 或 @987654328 @ 然后你用* 1 创建一个整数数组。如果不使用其他库,您将无法避免这种情况。 NumPy 速度很快,因为它在 C 中执行循环,但这意味着您必须处理临时数组。

但通过额外的库,您实际上可以加快速度并提高内存效率。

麻木:

import numba as nb
import numpy as np

@nb.njit
def numba_func(arr, lower, upper):
    res = np.zeros(arr.size, dtype=np.int8)
    arr_raveled = arr.ravel()
    for idx in range(arr.size):
        res[idx] = lower < arr_raveled[idx] < upper
    return res.reshape(arr.shape)

>>> numba_func(i, 0.3, 0.4)  # sample call

数值表达式

import numexpr as ne

ne.evaluate('((0.3<i)&(i<0.4))*1')

然而 numexpr 更像是一个黑盒,您无法控制它需要多少内存,但在大多数情况下,当您处理多个元素方面的 NumPy 操作时,它非常快速且内存效率更高 比 NumPy。

赛通

我在这里使用 IPython 魔法。如果您不使用 IPython 或 Jupyter,您可能需要自己 cythonize

%load_ext cython

%%cython

import numpy as np
cimport numpy as cnp

cpdef cnp.int8_t[:] cython_func(double[:] arr, double lower, double upper):
    cdef Py_ssize_t idx
    cdef cnp.int8_t[:] res = np.empty(len(arr), dtype=np.int8)
    for idx in range(len(arr)):
        res[idx] = lower < arr[idx] < upper
    return res

鉴于我在这里使用了 1D-memoryviews,您需要将其转换为数组并在之后对其进行整形:

np.asarray(cython_func(i.ravel(), 0.3, 0.4)).reshape(i.shape)  # sample call

可能有更好的方法来绕过ravelasarrayreshape,但这些方法要求您知道数组的维度。

时间

我使用较小的数组,因为我没有太多 RAM,但您可以轻松更改数字:

i = np.random.random((1000, 1000, 10))

%timeit numba_func(i, 0.3, 0.4)
52.1 ms ± 3.08 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit ne.evaluate('((0.3<i)&(i<0.4))*1')
77.1 ms ± 6.59 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit np.asarray(cython_func(i.ravel(), 0.3, 0.4)).reshape(i.shape)
146 ms ± 3.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit ((0.3<i)&(i<0.4))*1
180 ms ± 2.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

【讨论】:

  • 如果我这样做j=(i&lt;0.4)*1-(i&lt;0.3)*1会更有效吗?附加库(numba、numexpr、cython)能否像 numpy 一样对数组进行矢量化以使其更高效?
  • 它的效率可能会降低,因为那时你有 5 个临时数组,其中 3 个是整数(占用的空间比 bool 多)。但是,您需要对其进行计时或使用内存分析器来找出您的系统上启动了哪些优化。 Numba 和 cython 更像 Python -> C 转换器,两者都能够(据我所知)使用硬件矢量化,但您需要自己编写循环代码。另一方面,Numexpr 只是 NumPy 操作的不同(更优化和并行)后端,因此它默认使用矢量化和广播。
【解决方案2】:

是的,表达式的工作原理相同。检查它

jmult = ((0.3<i)*(i<0.4))*1
jand  = ((0.3<i)&(i<0.4))*1
jand == jmult

【讨论】:

  • 哎呀!您正在使用 NumPy,而不是 Python。不,这对你不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-13
  • 2017-05-23
  • 2014-04-04
  • 1970-01-01
  • 1970-01-01
  • 2015-06-17
相关资源
最近更新 更多