我正在尝试学习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 boundsflags=['reduce_ok','c_iter']-> it.operands[1][it.iterindex][...]=info = IndexError: index out of boundsflags=['reduce_ok','multi_iter']-> it.operands[1][it.multi_index][...]=info = IndexError: index out of boundsit[0][it.multi_index[1]][...]=info =IndexError: 0-d arrays can't be indexed
...等等。我想念什么?提前致谢。

奖金问题

我只是碰巧碰到this nice article on 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,并且it.operands[1]中没有20的任何内容。

如果您正确地执行了multi_index并忽略了列,则可以进行此工作……但是,您仍将执行5x4迭代,仅将每个步骤重复4次,而不是执行所需的5x1迭代。

每次循环时,您的it.operands[1][...]=info都会用5x1行替换整个输出。通常,您无需对it.operands[1]进行任何操作-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的所有速度优势来自使其能够在 native C/Fortran或较低级别的矢量操作中执行紧密循环。一旦遍历array,您实际上只是在以稍微更好的语法来执行缓慢的Python数字:
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的成本。

关于python - 脾气暴躁的:初学者,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14188807/

10-11 03:33