【问题标题】:Most efficient way to do (lo <= k && k <= hi) ? 1 : 0 for k a numpy array, lo, hi constants最有效的方法 (lo <= k && k <= hi) ? 1 : 0 表示 k 一个 numpy 数组,lo,hi 常量
【发布时间】:2016-09-07 05:40:23
【问题描述】:

我有一个未指定形状的大型 numpy 数组 k,我想构造一个形状相同的数组 d,当 k 中的相应条目介于两个常量 lo 和 @987654325 之间时,它是 1.0 @,否则为 0.0。 (因为更大的代码在做什么,我确实想要一个布尔值数组。)

这样做的明显方法是

d = np.ones_like(k)
d[np.less(k, lo)] = 0
d[np.greater(k, hi)] = 0

但是,np.lessnp.greater 调用涉及创建大型临时布尔数组,我认为这是一个很大的开销。有没有一种方法可以在不涉及创建任何大型临时对象的同时保持完全矢量化的情况下执行此操作?

【问题讨论】:

  • 如果大型临时数组是一个足够大的问题,您需要摆脱它们,那么大多数常见的 NumPy 技术都会遇到同样的问题。 NumPy 非常喜欢构建巨大的临时数组。
  • 我认为在使用普通 NumPy 时没有办法避免此操作的任何临时数组。您可能需要查看 C(编写自定义 ufunc)、Cython 或 Numba。

标签: python performance python-3.x numpy


【解决方案1】:

正如其他人所说,numpy 对临时缓冲区很重,并且不能对其提供太多控制。如果内存占用真的是一个障碍,你可以加入你自己的小程序。例如,

def process(x, lo, hi):
    """ lo <= x < hi ? 1.0 : 0.0."""
    x_shape = x.shape
    xx = np.ascontiguousarray(x).ravel()
    out = np.empty_like(xx)
    _process(xx, lo, hi, out)
    return out.reshape(x_shape)

_process 在 cython 中的位置:

%%cython --annotate

import cython

@cython.boundscheck(False)
@cython.wraparound(False)
def _process(double[::1] x, double lo, double hi, double[::1] out):
    """ lo <= x < hi ? 1.0 : 0.0.""" 
    cdef:
        Py_ssize_t j
        double xj

    for j in range(x.shape[0]):
        xj = x[j]
        if lo <= xj < hi:
            out[j] = 1.0
        else:
            out[j] = 0.0

在这里,我使用了 jupyter notebook(因此使用了有趣的 %%cython 语法)。在实际项目中,您需要输入 setup.py 来编译扩展等。这样做的好处是否值得麻烦由您决定。

【讨论】:

    【解决方案2】:

    您可以根据比较创建布尔数组,然后转换为浮点类型,一次完成,就像这样 -

    d = ((k >=lo) & (k <= hi)).astype(float)
    

    【讨论】:

    • 这仍然会创建临时对象。事实上,我相信它会创建 更多 个临时对象,而不是我所拥有的(我数了其中三个)。
    • @zwol 是的,我在性能效率方面做得更多,因为它显示了一些很好的加速(基于我最后完成的快速运行时测试)。在内存使用方面,必须进行相应的分析。是啊,我想这里有三个,然后其中一个被分配为输出,所以如果分配部分不复制,它与原始的相同吗?我想是的!
    【解决方案3】:

    lessgreater 采用 out 参数:

    out=np.ones_like(k)
    np.less(k,80,out=out);
    out &= np.greater(k,20);
    # np.logical_and(np.greater(k,20),out,out=out);
    

    这可能最终会节省一个中间数组。虽然我对 ufunc out 的印象是它仍然会创建一个临时数组,但随后只需将其复制到 out

    在小型 (10x10) 数组上,这比 @zwol 的方法快,但比 @Divakar 的慢。但差异并不大。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-20
      相关资源
      最近更新 更多