NumPy的:初级nditer(Numpy: Beginner nditer)

2019-08-05 19:55发布

我努力学习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的点,如果它这么慢?

Answer 1:

它确实有助于打印出什么沿途回事打破下来。

首先,让我们用这个替换您的整个循环:

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不必要的。



文章来源: Numpy: Beginner nditer