2012-02-23 47 views
6

我是一个相对有经验的Python程序员,但在很长一段时间里没有写任何C语言,所以我试图理解Cython。我正在尝试编写一个将在NumPy recarray的列上运行的Cython函数。访问Cython中的NumPy记录数组列

我到目前为止的代码如下。

recarray_func.pyx:

import numpy as np 
cimport numpy as np 

cdef packed struct rec_cell0: 
    np.float32_t f0 
    np.int64_t i0, i1, i2 

def sum(np.ndarray[rec_cell0, ndim=1] recarray): 
    cdef Py_ssize_t i 
    cdef rec_cell0 *cell 
    cdef np.float32_t running_sum = 0 

    for i in range(recarray.shape[0]): 
     cell = &recarray[i] 
     running_sum += cell.f0 
    return running_sum 

在解释器提示:

array = np.recarray((100,), names=['f0', 'i0', 'i1', 'i2'], 
          formats=['f4', 'i8', 'i8', 'i8']) 
recarray_func.sum(array) 

这简单地求和recarray的f0柱。它编译和运行没有问题。

我的问题是,我会如何修改它,以便它可以在任何列上操作?在上面的例子中,总和列是硬编码的,并通过点符号访问。是否有可能改变函数,这样总和列被作为参数传入?

回答

1

你想要什么需要弱打字,哪些C没有。如果你所有的记录类型都是相同的,你可能会得到如下的结果:(免责声明我没有Cython在这台机器上,所以我的代码是盲目的)。

import numpy as np 
cimport numpy as np 

cdef packed struct rec_cell0: 
    np.float32_t f0 
    np.int64_t i0, i1, i2 

def sum(np.ndarray[rec_cell0, ndim=1] recarray, colname): 
    cdef Py_ssize_t i 
    cdef rec_cell0 *cell 
    cdef np.float32_t running_sum = 0 

    loc = recarray.dtype.fields[colname][1] 

    for i in range(recarray.shape[0]): 
     cell = &recarray[i] 
     running_sum += *(int *)(&cell+loc); 
    return running_sum 
+0

像这样的东西可能会工作 - 你可以传递一个融合的类型作为running_sum的类型,并将其作为指针传递,然后可以将该类型转换为该类型。 – shaunc 2012-05-05 04:16:35

2

我相信这应该可以使用Cython的memoryviews。沿着这些线路的东西应该工作(代码未测试):

import numpy as np 
cimport numpy as np 

cdef packed struct rec_cell0: 
    np.float32_t f0 
    np.int64_t i0, i1, i2 

def sum(rec_cell0[:] recview): 
    cdef Py_ssize_t i 
    cdef np.float32_t running_sum = 0 

    for i in range(recview.shape[0]): 
     running_sum += recview[i].f0 
    return running_sum 

速度也许可以通过确保传递给用Cython记录阵列是连续增加。在python(调用)方面,您可以使用np.require,而函数签名应该更改为rec_cell0[::1] recview以指示可以假定该数组是连续的。与往常一样,一旦代码已经过测试,在Cython中关闭boundscheck,wraparoundnonecheckcompiler directives可能会进一步提高速度。