如何有效地操作一个大的numpy数组

Posted

技术标签:

【中文标题】如何有效地操作一个大的numpy数组【英文标题】:How to efficiently operate a large numpy array 【发布时间】:2019-03-31 15:53:39 【问题描述】:

我有一段代码,它基于一个大的 numpy 数组,然后操作另一个数组。因为这是一个非常大的数组,请问是否有一种有效的方法可以实现我的目标? (我认为高效的方式应该是直接对数组进行操作,而不是通过for循环)。

提前致谢,请在下面找到我的代码:

N = 1000000000
rand = np.random.rand(N)
beta = np.zeros(N)
for i in range(0, N):
    if rand[i] < 0.5:
        beta[i] = 2.0*rand[i]
    else:
        beta[i] = 1.0/(2.0*(1.0-rand[i]))

【问题讨论】:

你可以试试scipy的稀疏矩阵。查看此post。 对于这么大的矩阵,这可能仍然太慢,但可能更好:np.where(rand &lt; 0.5, 2.0*rand, 1.0/(2.0*(1.0-rand))) @EricWang,我看不出稀疏矩阵与这个问题的相关性rand 没有高比例的零,beta 也没有。 一种更快的方法,几乎​​不需要更改您的代码(我不会初始化一个完全用零覆盖并添加 @nb.njit(error_model="numpy) 的数组)将是 ***.com/q/52046301/4045774 . 【参考方案1】:

通过在 Python 中执行处理,您在这里基本上失去了 numpy 的效率。 numpy 的想法是处理 bulk 中的项目,因为它在 C++ 中具有执行实际处理的效率算法。您可以将 numpy 的 Python 结尾更多地视为“接口”。

现在回答你的问题,我们基本上可以先构造一个介于 0 和 2 之间的随机数数组,方法是将它与 2 相乘:

rand = 2.0 * np.random.rand(N)

接下来我们可以使用np.where(..) [numpy-doc],它就像一个条件选择器:我们在这里传递三个“数组”:第一个是一个布尔数组,它编码“条件”的真实性,第二个是数组在相关条件为真时要填写的值,第三个值是在条件为假时要插入的值的数组,所以我们可以这样写:

N = 1000000000
rand = 2 * np.random.rand(N)
beta = np.where(rand < 1.0, rand, 1.0 / (2.0 - rand))

【讨论】:

非常感谢,根据答案,有两种方法:1)在我定义一个函数后使用 numpy 的矢量化,2)使用 where。我在我的电脑上用 N=1x10^8 进行了测试,第一种方法需要 30 秒,第二种方法需要 1.3 秒。如果我没有先乘以 2,则需要 1.9 秒。非常感谢。【参考方案2】:

N = 1000000000 为我造成了MemoryError。以最小的例子减少到 100。 您可以使用np.where routine。

在这两种情况下,从根本上讲,您都是在遍历数组并应用一个函数。 然而np.where 使用了一种更快的循环(它基本上是编译代码),而你的“python”循环被解释,因此对于一个大 N 来说真的很慢。

这是一个实现示例。

N = 100
rand = np.random.rand(N)
beta = np.where(rand < 0.5,  2.0 * rand, 1.0/(2.0*(1.0-rand))

【讨论】:

【参考方案3】:

正如其他答案所指出的,在 Python 循环中迭代 numpy 数组的元素几乎总是应该(并且可以)避免。在大多数情况下,从 Python 循环到数组操作的速度提升约为 100 倍。

但是,如果性能绝对关键,您通常可以使用 Cython 挤出 2 倍到 10 倍之间的另一个因素(根据我的经验)。 这是一个例子:

%%cython
cimport numpy as np
import numpy as np
cimport cython
from cython cimport floating

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cpdef np.ndarray[floating, ndim=1] beta(np.ndarray[floating, ndim=1] arr):
    cdef:
        Py_ssize_t i
        Py_ssize_t N = arr.shape[0]
        np.ndarray[floating, ndim=1] result = np.zeros(N)

    for i in range(N):
        if arr[i] < 0.5:
            result[i] = 2.0*arr[i]
        else:
            result[i] = 1.0/(2.0*(1.0-arr[i]))

    return result

然后您可以将其称为beta(rand)。 如您所见,这允许您使用原始循环结构,但现在使用高效的类型化本机代码。与 np.where 相比,我得到了约 2.5 倍的加速。

需要注意的是,在许多情况下,与 numpy 中的单行代码相比,这不值得付出额外的努力——但这很可能是性能至关重要的地方。

【讨论】:

非常感谢,这也是我不知道Numpy高效使用时的一个好答案。

以上是关于如何有效地操作一个大的numpy数组的主要内容,如果未能解决你的问题,请参考以下文章

如何在给定索引列表的情况下有效地更新 numpy ndarray

如何有效地找到 NumPy 中平滑多维数组的局部最小值?

如何在python 3中有效地将原始字节写入numpy数组数据

使用 PySide 时,如何有效地将数据从 NumPy 数组传输到 QPolygonF?

如何有效地计算 numpy 二维数组的块均值(不规则块)?

如何有效地处理类似于 Matlab 的 blkproc (blockproc) 函数的块中的 numpy 数组