【发布时间】:2022-01-25 02:29:08
【问题描述】:
我希望每个索引 i 都有一个循环,其中数组 X(布尔)是 True。
有没有比在np.nditer 中包装np.nonzero 更高效/pythonic 的方法如下?
for i in np.nditer(np.nonzero(X), flags=['zerosize_ok']):
myfunction(Y[i],Z2[Z[i]])
这里的问题是它迭代了两次而不是一次,并且占用了内存(首先,np.nonzero 遍历 X 并将其存储到一个大数组中,然后 np.nditer 遍历该数组)。
是否有一个命令(可以说有点类似于np.nditer)用于直接有效地迭代布尔数组的True 条目,而不首先用np.nonzero 明确列出它们? (遍历所有条目并使用 if 语句检查每个条目的效率可能低于 Numpy 提供的某些迭代器(如果存在)。)
【问题讨论】:
-
让你的代码变慢的主要是 CPython 函数调用和循环中对 Numpy 数组的许多直接访问。删除第二个可以提高一点性能,但函数调用仍然是一个巨大的瓶颈。如果不更改函数本身(使用简单的装饰器或完全重写函数),就无法删除函数调用。你能描述/分享这个函数的作用吗?
-
@JérômeRichard 如果函数变得过于复杂,那么删除函数调用可能是不可能的,对吧?我在问
np.nditer(np.nonzero(X), flags=['zerosize_ok'])是否可以用更高效/pythonic 的东西代替;在当前的形式中,它迭代两次而不是一次,并占用内存(首先np.nonzero迭代通过X并将其存储到一个大数组,然后np.nditer迭代通过该数组)。 -
在不知道您的输入和自定义函数的情况下很难说,但缓慢不太可能来自两次迭代。
np.nonzero即使对于非常大的数组也非常快。 -
@root 这里的双重迭代显然不是问题。与迭代(约快 17 倍)和函数调用(约 25 倍)相比,
np.nonzero(X)调用非常快。这是因为 Numpy 进行本地调用,而 CPython 解释器非常慢。如果你想要一个快速的代码,你需要删除热路径中所有使用的纯 Python 代码。由于 Python(和解释器)的设计方式,可迭代对象也很慢。至于函数,Numpy 中有np.vectorize之类的东西,但效率不高(由于前面提到:Numpy 无法超越 CPython 规则)。 -
@root 一旦矢量化被分开,因为该函数被假定为不可矢量化(手动)。剩下的唯一选择是绕过 CPython,例如使用嵌入式 JIT/AOT 编译器或不使用 CPython(请参阅 PyPy,它是一个 JIT)。但是,这种嵌入式编译器工具需要对函数进行修饰和/或限制为某些操作。至于 PyPy(或其他解释器),它们也几乎没有限制,并且如果通常不是免费的,则使用另一个解释器。我认为没有其他解决方案。