我努力学习nditer为可能使用在加快我的申请。 在这里,我尝试做一个滑稽的重塑计划,将采取大小20阵列,并重塑一个5x4的阵列:
myArray = np.arange(20)
def fi_by_fo_100(array):
offset = np.array([0, 4, 8, 12, 16])
it = np.nditer([offset, None],
flags=['reduce_ok'],
op_flags=[['readonly'],
['readwrite','allocate']],
op_axes=[None, [0,1,-1]],
itershape=(-1, 4, offset.size))
while not it.finished:
indices = np.arange(it[0],(it[0]+4), dtype=int)
info = array.take(indices)
'''Just for fun, we'll perform an operation on data.\
Let's shift it to 100'''
info = info + 81
it.operands[1][...]=info
it.iternext()
return it.operands[1]
test = fi_by_fo_100(myArray)
>>> test
array([[ 97, 98, 99, 100]])
显然,程序覆盖每个结果成一排。 所以,我尝试使用nditer的索引功能,但仍然没有骰子。
flags=['reduce_ok','c_iter']
- > it.operands[1][it.index][...]=info
=
IndexError: index out of bounds
flags=['reduce_ok','c_iter']
- > it.operands[1][it.iterindex][...]=info
=
IndexError: index out of bounds
flags=['reduce_ok','multi_iter']
- > it.operands[1][it.multi_index][...]=info
=
IndexError: index out of bounds
it[0][it.multi_index[1]][...]=info
=
IndexError: 0-d arrays can't be indexed
...等等。 我在想什么? 提前致谢。
奖金问题
我对面发生在nditer这个漂亮的文章 。 我可能是新的numpy的,但是这是我第一次看到numpy的速度基准,这远远落后。 这是我的理解是人们选择numpy的为它的数值的速度和实力,但是迭代是其中的一部分,不是吗? 什么是nditer的点,如果它这么慢?
它确实有助于打印出什么沿途回事打破下来。
首先,让我们用这个替换您的整个循环:
i = 0
while not it.finished:
i += 1
print i
这将打印20,不是5那是因为你正在做一个5x4的迭代,不5X1。
那么,这是为什么,甚至接近正常工作? 好吧,让我们来看看在循环更仔细:
while not it.finished:
print '>', it.operands[0], it[0]
indices = np.arange(it[0],(it[0]+4), dtype=int)
info = array.take(indices)
info = info + 81
it.operands[1][...]=info
print '<', it.operands[1], it[1]
你会看到一个第一五个环经历[0 4 8 12 16]
五次,生成[[81 82 83 84]]
然后[[85 86 87 88]]
等。并且再下五个环做同样的事情,一次又一次。
这也是为什么你c_index
解决方案没有奏效,因为it.index
将会范围从0到19,和你没有任何东西20 it.operands[1]
如果你做了正确的multi_index而忽视了列,可以使这项工作......但尽管如此,你会做一个5x4的重复,只是重复每一步的4倍,而不是做的5X1迭代你想要的。
你it.operands[1][...]=info
每一次循环替换为5X1行的整个输出。 一般情况下,你不应该曾经有做任何事情来it.operands[1]
的-the整点nditer
是,你只需要每个照顾it[1]
并最终it.operands[1]
是结果。
当然,一个5x4的遍历所有行是没有意义的。 无论是做了单独的值的5x4的迭代,或通过行的5X1迭代。
如果你想前,要做到这一点是重塑输入数组的最简单的方法,那只是重复说:
it = np.nditer([array.reshape(5, -1), None],
op_flags=[['readonly'],
['readwrite','allocate']])
for a, b in it:
b[...] = a + 81
return it.operands[1]
但是,当然,这是愚蠢的,它只是一个写的慢,更复杂的方法:
return array+81
而这将是一个有点傻表明,“写你自己的方式reshape
是第一次调用reshape
,然后......”
所以,你要遍历行,对不对?
让我们摆脱简化事情有点的allocate
,并明确建立一个5x4的阵列入手:
outarray = np.zeros((5,4), dtype=array.dtype)
offset = np.array([0, 4, 8, 12, 16])
it = np.nditer([offset, outarray],
flags=['reduce_ok'],
op_flags=[['readonly'],
['readwrite']],
op_axes=[None, [0]],
itershape=[5])
while not it.finished:
indices = np.arange(it[0],(it[0]+4), dtype=int)
info = array.take(indices)
'''Just for fun, we'll perform an operation on data.\
Let's shift it to 100'''
info = info + 81
it.operands[1][it.index][...]=info
it.iternext()
return it.operands[1]
这是一个有点滥用的nditer
,但至少它做正确的事。
既然你只是做了源一维迭代,基本上忽略了第二,真的没有什么理由使用nditer
这里。 如果你需要做的步调一致迭代在多个阵列, for a, b in nditer([x, y], …)
比遍历清洁x
和使用索引来访问y
-只是像for a, b in zip(x, y)
的外numpy
。 如果你需要遍历多维数组, nditer
通常比其它清洁剂。 但在这里,你会真正做的是遍历[0, 4, 8, 16, 20]
做其结果的东西,并将其复制到另一个array
。
此外,正如我在评论中提到的,如果你发现自己使用迭代中numpy
,你平时做错了什么。 所有的速度好处numpy
来自让它在本地C / Fortran语言或更低级别的矢量运算执行紧密的循环。 一旦你遍历array
S,你有效地只是在做缓慢的Python NUMERICS略带优雅的语法:
import numpy as np
import timeit
def add10_numpy(array):
return array + 10
def add10_nditer(array):
it = np.nditer([array, None], [],
[['readonly'], ['writeonly', 'allocate']])
for a, b in it:
np.add(a, 10, b)
return it.operands[1]
def add10_py(array):
x, y = array.shape
outarray = array.copy()
for i in xrange(x):
for j in xrange(y):
outarray[i, j] = array[i, j] + 10
return out array
myArray = np.arange(100000).reshape(250,-1)
for f in add10_numpy, add10_nditer, add10_py:
print '%12s: %s' % (f.__name__, timeit.timeit(lambda: f(myArray), number=1))
在我的系统,这个打印:
add10_numpy: 0.000458002090454
add10_nditer: 0.292730093002
add10_py: 0.127345085144
这说明你使用的成本nditer
不必要的。