Python 迭代多个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,并且在板

最近我读了很多关于在numpy数组上迭代的不同技术的书,似乎大家的共识是根本不迭代(例如,请参阅)。有几个类似的问题,但我的情况有点不同,因为我必须结合“迭代”(或不迭代)和访问以前的值

假设在一个列表
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) )
这不仅看起来很难看,而且要花很多时间才能完成

因此,我有以下问题:

  • 在所有这些方法中,第三种真的是最好的吗?如果是这样,可以采取什么措施来取消最后一个
  • 还有其他更好的吗
  • 出于好奇,有没有办法用nditer解决这个问题
  • 最后,使用memmap版本的numpy阵列会更好,还是会大大降低速度?也许我应该只将
    secaxis
    数组加载到RAM中,将其他数组保存在磁盘上,然后使用第三种方法
  • (附加问题)长度相等的1-D numpy数组列表来自加载N
    .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