Python 迭代多个numpy数组并处理当前和以前元素的有效方法?
最近我读了很多关于在numpy数组上迭代的不同技术的书,似乎大家的共识是根本不迭代(例如,请参阅)。有几个类似的问题,但我的情况有点不同,因为我必须结合“迭代”(或不迭代)和访问以前的值 假设在一个列表Python 迭代多个numpy数组并处理当前和以前元素的有效方法?,python,arrays,performance,numpy,itertools,Python,Arrays,Performance,Numpy,Itertools,最近我读了很多关于在numpy数组上迭代的不同技术的书,似乎大家的共识是根本不迭代(例如,请参阅)。有几个类似的问题,但我的情况有点不同,因为我必须结合“迭代”(或不迭代)和访问以前的值 假设在一个列表X中有N个(N很小,通常是4个,最多可能是7个)1-D numpy数组,它们的float128,所有数组的大小都相同。为了让您有一点了解,这些是来自PDE集成的数据,每个数组代表一个函数,我想应用一个庞加莱截面。不幸的是,算法应该同时具有内存和具有时效性的,因为这些数组有时每个都是1GB,并且在板
X
中有N个(N很小,通常是4个,最多可能是7个)1-D numpy数组,它们的float128
,所有数组的大小都相同。为了让您有一点了解,这些是来自PDE集成的数据,每个数组代表一个函数,我想应用一个庞加莱截面。不幸的是,算法应该同时具有内存和具有时效性的,因为这些数组有时每个都是1GB,并且在板上只有4GB的RAM(我刚刚学习了NUMPY数组的MMPMAP,现在考虑使用它们代替普通的RAM)。
其中一个数组用于“过滤”其他数组,因此我从secaxis=X.pop(idx)
开始。现在我必须找到(secaxis[I-1]>0和secaxis[I]<0)或(secaxis[I-1]<0和secaxis[I]>0)
的索引对,然后对剩余数组应用简单的代数变换,X
(并保存结果)。值得一提的是,在此操作过程中不应浪费数据
有多种方法可以做到这一点,但在我看来,没有一种方法是有效的(而且不够优雅)。一种是类似C的方法,您只需在for循环中迭代:
import array # better than lists
res = [ array.array('d') for _ in X ]
for i in xrange(1,secaxis.size):
if condition: # see above
co = -secaxis[i-1]/secaxis[i]
for j in xrange(N):
res[j].append( (X[j][i-1] + co*X[j][i])/(1+co) )
这显然是非常低效的,而且不是一种蟒蛇式的方式
另一种方法是使用numpy.nditer,但我还没有弄清楚如何访问上一个值,尽管它允许一次迭代多个数组:
# without secaxis = X.pop(idx)
it = numpy.nditer(X)
for vec in it:
# vec[idx] is current value, how do you get the previous (or next) one?
第三种可能性是首先使用有效的numpy切片找到所寻求的索引,然后将它们用于批量乘法/加法。我现在更喜欢这个:
res = []
inds, = numpy.where((secaxis[:-1] < 0) * (secaxis[1:] > 0) +
(secaxis[:-1] > 0) * (secaxis[1:] < 0))
coefs = -secaxis[inds] / secaxis[inds+1] # array of coefficients
for f in X: # loop is done only N-1 times, that is, 3 to 6
res.append( (f[inds] + coefs*f[inds+1]) / (1+coefs) )
这不仅看起来很难看,而且要花很多时间才能完成
因此,我有以下问题:
secaxis
数组加载到RAM中,将其他数组保存在磁盘上,然后使用第三种方法.npy
文件,这些文件的大小事先未知(但N是未知的)。读取一个数组,然后为一个2-D numpy数组分配内存(这里的内存开销很小),然后将剩余的内存读入该2-D数组,这样会更高效吗numpy.where()
版本足够快,您可以通过method3()
稍微加速它。如果
条件可以更改为=
,也可以使用method4()
我需要更详细地阅读你的文章,但将从一些一般性的观察开始(来自之前的迭代问题)
在Python中,没有一种有效的方法来迭代数组,尽管有些事情会减慢速度。我喜欢区分迭代机制(nditer
,for A:
)和操作(alist.append(…)
,x[I+1]+=1
)。大时间消费者通常是多次完成的动作,而不是迭代机制本身
让numpy
在编译代码中进行迭代是最快的
xdiff = x[1:] - x[:-1]
比以前快多了
xdiff = np.zeros(x.shape[0]-1)
for i in range(x.shape[0]:
xdiff[i] = x[i+1] - x[i]
np.nditer
并没有更快
nditer
建议作为编译代码中的通用迭代工具。但它的主要价值在于处理广播和协调多个数组(输入/输出)上的迭代。您需要使用缓冲和类似于代码的c
来从nditer
获得最佳速度(我将查找最近的一个问题)
未研究相关的迭代
教程页面(以cython
示例结尾的页面),请勿使用nditer
=========================
从经验来看,这种方法将是最快的。是的,它将在secaxis
上迭代多次,但这些都是在编译代码中完成的,并且比Python中的任何迭代都要快得多。对于X中的f:
迭代只需几次
res = []
inds, = numpy.where((secaxis[:-1] < 0) * (secaxis[1:] > 0) +
(secaxis[:-1] > 0) * (secaxis[1:] < 0))
coefs = -secaxis[inds] / secaxis[inds+1] # array of coefficients
for f in X:
res.append( (f[inds] + coefs*f[inds+1]) / (1+coefs) )
如果X
是一个数组,res
也可以是一个数组
res = (X[:,inds] + coefs*X[:,inds1])/coefs1
但是对于小的N
我怀疑列表res
也一样好。不需要使阵列变得比需要的更大。这些调整很小,只是为了避免重新计算
=================
使用np.where
只是np.nonzero
。这实际上对数组进行了两次遍历,一次使用np.count\u nonzero
确定它将返回多少值,并创建返回结构(现在已知长度的数组列表)。第二个循环用于填充这些索引。因此,如果多个迭代保持操作简单,那么多个迭代就可以了
xdiff = np.zeros(x.shape[0]-1)
for i in range(x.shape[0]:
xdiff[i] = x[i+1] - x[i]
res = []
inds, = numpy.where((secaxis[:-1] < 0) * (secaxis[1:] > 0) +
(secaxis[:-1] > 0) * (secaxis[1:] < 0))
coefs = -secaxis[inds] / secaxis[inds+1] # array of coefficients
for f in X:
res.append( (f[inds] + coefs*f[inds+1]) / (1+coefs) )
inds1 = inds+1
coefs = -secaxis[inds] / secaxis[inds1]
coefs1 = coefs+1
for f in X:
res.append(( f[inds] + coefs*f[inds1]) / coefs1)
res = (X[:,inds] + coefs*X[:,inds1])/coefs1