我已经阅读了很多关于最近迭代numpy数组的不同技术,似乎共识不是迭代(例如,参见a comment here)。关于SO有几个类似的问题,但我的情况有点不同,因为我必须结合"迭代" (或不迭代)并访问先前的值。
让我们说列表float128
中有N(N是小的,通常是4,可能多达7个){1}的1-D numpy数组,所有数组都是大小相同。为了给你一点见解,这些是来自PDE集成的数据,每个数组代表一个功能,我想应用Poincare部分。遗憾的是,该算法应该具有内存和时间效率,因为这些阵列有时每个大约1Gb,并且板上只有4Gb的RAM(我刚刚了解了关于numpy数组的memmap和现在的数据,现在考虑使用它们而不是常规的。)
其中一个数组用于"过滤"其他人,所以我从X
开始。现在我必须找到secaxis = X.pop(idx)
的索引对,然后将简单的代数转换应用于剩余的数组(secaxis[i-1] > 0 and secaxis[i] < 0) or (secaxis[i-1] < 0 and secaxis[i] > 0)
(并保存结果)。值得一提的是,在此操作过程中不应浪费数据。
有多种方法可以做到这一点,但对我来说,它们似乎都没有效率(而且足够优雅)。一种是类似C的方法,只需迭代for循环:
X
这显然非常低效,而且不是Pythonic方式。
另一种方法是使用numpy.nditer,但我还没弄清楚如何访问前一个值,尽管它允许一次迭代多个数组:
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切片找到所寻找的指数,然后将它们用于批量乘法/加法。我现在更喜欢这个:
# 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?
但这似乎是在7 + 2 *(N - 1)次传递中完成的,此外,我不确定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) )
类型的寻址(它不是切片,通常它必须找到所有索引的元素就像第一种方法一样,不是吗?)。
最后,我也尝试过使用itertools,它导致了巨大而模糊的结构,这可能源于我对函数式编程不太熟悉的事实:
secaxis[inds]
这不仅非常丑陋,而且还需要花费大量时间才能完成。
所以,我有以下问题:
def filt(x):
return (x[0] < 0 and x[1] > 0) or (x[0] > 0 and x[1] < 0)
import array
from itertools import izip, tee, ifilter
res = [ array.array('d') for _ in X ]
iters = [iter(x) for x in X] # N-1 iterators in a list
prev, curr = tee(izip(*iters)) # 2 similar iterators, each of which
# consists of N-1 iterators
next(curr, None) # one of them is now for current value
seciter = tee(iter(secaxis))
next(seciter[1], None)
for x in ifilter(filt, izip(seciter[0], seciter[1], prev, curr)):
co = - x[0]/x[1]
for r, p, c in zip(res, x[2], x[3]):
r.append( (p+co*c) / (1+co) )
数组加载到RAM中,将其他数据保存在磁盘上并使用第三种方法?secaxis
个文件,这些文件的大小事先未知(但是N是)。读取一个数组,然后为一个2-D numpy数组分配内存(这里是轻微的内存开销)并将剩余的数据读入该二维数组会更有效吗?答案 0 :(得分:3)
numpy.where()
版本足够快,您可以将其加速method3()
。如果>
条件可以更改为>=
,您还可以使用method4()
。
import numpy as np
a = np.random.randn(100000)
def method1(a):
idx = []
for i in range(1, len(a)):
if (a[i-1] > 0 and a[i] < 0) or (a[i-1] < 0 and a[i] > 0):
idx.append(i)
return idx
def method2(a):
inds, = np.where((a[:-1] < 0) * (a[1:] > 0) +
(a[:-1] > 0) * (a[1:] < 0))
return inds + 1
def method3(a):
m = a < 0
p = a > 0
return np.where((m[:-1] & p[1:]) | (p[:-1] & m[1:]))[0] + 1
def method4(a):
return np.where(np.diff(a >= 0))[0] + 1
assert np.allclose(method1(a), method2(a))
assert np.allclose(method2(a), method3(a))
assert np.allclose(method3(a), method4(a))
%timeit method1(a)
%timeit method2(a)
%timeit method3(a)
%timeit method4(a)
%timeit
结果:
1 loop, best of 3: 294 ms per loop
1000 loops, best of 3: 1.52 ms per loop
1000 loops, best of 3: 1.38 ms per loop
1000 loops, best of 3: 1.39 ms per loop
答案 1 :(得分:3)
我需要更详细地阅读您的帖子,但会从一些一般性观察开始(来自之前的迭代问题)。
在Python中迭代数组并不是一种有效的方法,尽管有些东西可以减慢速度。我喜欢区分迭代机制("syd"
,nditer
)和操作(for x in 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
获得最佳速度(我将查看最近的SO问题)。
https://stackoverflow.com/a/39058906/901925
请勿在未学习相关nditer
教程页面(以nditer
示例结尾的页面)的情况下使用iteration
。
=========================
从经验来看,这种方法将是最快的。是的,它会多次迭代cython
,但这些都是在编译的代码中完成的,并且比Python中的任何迭代都要快得多。 secaxis
迭代只是几次。
for f in X:
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) )
已经探索了使@HYRY
步骤更快的替代方案。但正如你所看到的那样,差异并不大。其他可能的调整
where
如果inds1 = inds+1
coefs = -secaxis[inds] / secaxis[inds1]
coefs1 = coefs+1
for f in X:
res.append(( f[inds] + coefs*f[inds1]) / coefs1)
是数组,X
也可以是数组。
res
但对于小res = (X[:,inds] + coefs*X[:,inds1])/coefs1
我怀疑列表N
同样好。不要让阵列超出必要的范围。调整很小,只是试图避免重新计算。
=================
res
的使用只是np.where
。这实际上是两次传递数组,一次使用np.nonzero
来确定它将返回多少个值,并创建返回结构(现在已知长度的数组列表)。并填写第二个循环来填写这些指数。因此,如果让操作变得简单,那么多次迭代就可以了。