Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Cython是否提供了任何相当简单有效的方法来迭代Numpy数组,就好像它们是平面的一样?_Python_Performance_Numpy_Cython - Fatal编程技术网

Python Cython是否提供了任何相当简单有效的方法来迭代Numpy数组,就好像它们是平面的一样?

Python Cython是否提供了任何相当简单有效的方法来迭代Numpy数组,就好像它们是平面的一样?,python,performance,numpy,cython,Python,Performance,Numpy,Cython,假设我想实现Numpy的 x[:] += 1 在赛昂。我会写 @cython.boundscheck(False) @cython.wraparoundcheck(False) def add1(np.ndarray[np.float32_t, ndim=1] x): cdef unsigned long i for i in range(len(x)): x[i] += 1 但是,这仅适用于ndim=1。我可以用 add1(x.reshape(-1)) 但

假设我想实现Numpy的

x[:] += 1
在赛昂。我会写

@cython.boundscheck(False)
@cython.wraparoundcheck(False)
def add1(np.ndarray[np.float32_t, ndim=1] x):
    cdef unsigned long i
    for i in range(len(x)):
        x[i] += 1
但是,这仅适用于
ndim=1
。我可以用

add1(x.reshape(-1))
但这只适用于连续的
x

Cython是否提供了一种相当简单有效的方法来迭代Numpy数组,就好像它们是平面的一样?

(在Cython中重新实现这个特定的操作没有意义,因为上面的Numpy代码应该尽可能快——我只是把它作为一个简单的例子)

更新:

我对提议的解决方案进行了基准测试:

@cython.boundscheck(False)
@cython.wraparound(False)
def add1_flat(np.ndarray x):
    cdef unsigned long i
    for i in range(x.size):
        x.flat[i] += 1

@cython.boundscheck(False)
@cython.wraparound(False)
def add1_nditer(np.ndarray x):
    it = np.nditer([x], op_flags=[['readwrite']])
    for i in it:
        i[...] += 1
第二个函数除了需要
cimport
之外,还需要
import numpy as np
。结果是:

a = np.zeros((1000, 1000))

b = a[100:-100, 100:-100]

%timeit b[:] += 1
1000 loops, best of 3: 1.31 ms per loop

%timeit add1_flat(b)
1 loops, best of 3: 316 ms per loop

%timeit add1_nditer(b)
1 loops, best of 3: 1.11 s per loop
因此,它们比Numpy慢300倍和1000倍

更新2:

add11
版本在
for
循环中使用
for
循环,因此不会像平面一样迭代数组。但是,在这种情况下,它与Numpy一样快:

%timeit add1.add11(b)
1000 loops, best of 3: 1.39 ms per loop

另一方面,建议的解决方案之一
add1_unravel
无法修改
b

的内容。您可以尝试使用
ndarray
flat
属性,该属性在展平的数组对象上提供迭代器。它总是以C大调顺序迭代,最后一个索引变化最快。比如:

for i in range(x.size):
    x.flat[i] += 1

这是一个很好的使用
nditer
的教程。它以一个
cython
版本结束
nditer
是numpy
c
级别代码中的通用数组迭代器

Cython memoryview页面上还有一些很好的数组示例:

ndarray
的数据缓冲区是平面缓冲区。因此,无论数组的形状和步幅如何,都可以以平面
c
指针方式在整个缓冲区上迭代。但是像
nditer
memoryview
这样的东西会处理元素大小的细节。因此,在
c
level编码中,以平面方式逐步遍历所有元素实际上比逐行遍历更容易——逐行遍历必须考虑行的步幅

这在Python中运行,我认为它将很好地转换为cython(目前我的机器上没有这种设置):

是我不久前为模拟
einsum
而编写的产品脚本的总和

w=sop(x,y)
计算的核心是:

it = np.nditer(ops, flags, op_flags, op_axes=op_axes, order=order)
it.operands[nop][...] = 0
it.reset()
for xarr, yarr, warr in it:
    x = xarr
    y = yarr
    w = warr
    size = x.shape[0]
    for i in range(size):
       w[i] = w[i] + x[i] * y[i]
return it.operands[nop]
===================

nditer.html
文档中复制想法,我得到了一个
add1
版本,它的速度只有原生
numpy
a+1
的一半。在
cython
中,朴素的
nditer
(如上)并不比在
python
中快多少。许多加速可能是由于
外部环路
造成的

@cython.boundscheck(False)
def add11(arg):
   cdef np.ndarray[double] x
   cdef int size
   cdef double value
   it = np.nditer([arg],
        flags=['external_loop','buffered'], 
        op_flags=[['readwrite']])
   for xarr in it:
       x = xarr
       size = x.shape[0]
       for i in range(size):
           x[i] = x[i]+1.0
   return it.operands[0]
我还用python编写了这个
nditer
,打印了
size
,发现它在
b
上迭代了78个大小为8192的块-这是缓冲区大小,不是
b
及其数据布局的一些特征

In [15]: a = np.zeros((1000, 1000)) 
In [16]: b = a[100:-100, 100:-100]

In [17]: timeit add1.add11(b)
100 loops, best of 3: 4.48 ms per loop

In [18]: timeit b[:] += 1
100 loops, best of 3: 8.76 ms per loop

In [19]: timeit add1.add1(b)    # for the unbuffered nditer 
1 loop, best of 3: 3.1 s per loop

In [21]: timeit add1.add11(a)
100 loops, best of 3: 5.44 ms per loop

numpy.ravel(a,order='C')

返回一个连续的展开数组

返回一个包含输入元素的一维数组。副本 只有在需要时才制作


很有趣,尽管我怀疑效率很低。我稍后会运行一些测试。@MaxB似乎你对效率低下的看法是正确的。IPython中的快速
%timeit
显示,它比仅迭代数组的所有维度要多花费10%的时间。另外,我对排序的看法不正确,
flat
属性总是在C排序中返回元素的迭代器。另一个选择是在
x
的内存上创建一个重新成形的视图,并使用您提出的解决方案。例如,
new_array=x.view().reforme(-1,1)
.AFAIK,它不适用于非连续数组(我在问题中提到过)。修改一个视图的内容,如果它不是连续的,会在写入时进行某种形式的复制,并且它查看的数组保持不变(虽然我没有在Cython中尝试过)编辑:实际上,我怀疑它在创建时复制谢谢,我将通读教程,看看它们是否回答了我的问题。我认为如果它包含了
x[:]+=1
的实际实现,答案会有所改进(这是有效的,可以处理任意数量的维度和不连续的x)。我添加了一个更快的cython版本。这有点不令人满意,因为声明的目标是像平面一样迭代
x
。嵌套循环并不能完全做到这一点,尽管您的解决方案可能与它得到的一样好。这里唯一的嵌套是由
nditer
缓冲区大小决定的。检查我的编辑。这些计时是为了编译cython代码?@hpaulj是的,除非我搞砸了(我是cython新手)。这些定义在myfile.pyx中;setup.py包括setup(ext_modules=cythonize(“myfile.pyx”)),在将myfile导入ipython之前,我调用“python setup.py build_ext--inplace”。@hpaulj无法告诉Cython x元素的类型。如果我指定它,Cython假设ndim=1。另外,我认为flat和nditer是Cython通过pythonth调用的Python结构。这可能是合适的
In [15]: a = np.zeros((1000, 1000)) 
In [16]: b = a[100:-100, 100:-100]

In [17]: timeit add1.add11(b)
100 loops, best of 3: 4.48 ms per loop

In [18]: timeit b[:] += 1
100 loops, best of 3: 8.76 ms per loop

In [19]: timeit add1.add1(b)    # for the unbuffered nditer 
1 loop, best of 3: 3.1 s per loop

In [21]: timeit add1.add11(a)
100 loops, best of 3: 5.44 ms per loop
@cython.boundscheck(False)
@cython.wraparound(False)
def add1_ravel(np.ndarray xs):
    cdef unsigned long i
    cdef double[::1] aview

    narray = xs.ravel()
    aview = narray

    for i in range(aview.shape[0]):
        aview[i] += 1

    # return xs if the memory is shared
    if not narray.flags['OWNDATA'] or np.may_share_memory(xs, narray):
        return xs

    # otherwise new array reshaped
    shape = tuple(xs.shape[i] for i in range(xs.ndim))
    return narray.reshape(shape)