在 Cython 中访问 NumPy 记录数组列

发布于 2025-01-08 15:21:05 字数 889 浏览 2 评论 0原文

我是一位相对经验丰富的 Python 程序员,但很长一段时间没有编写任何 C 语言,并且正在尝试理解 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)

这只是对 rearray 的 f0 列求和。它编译并运行没有问题。

我的问题是,如何修改它以便它可以在任何列上运行?在上面的示例中,要求和的列是硬编码的,并通过点表示法访问。是否可以更改函数,以便将要求和的列作为参数传入?

I'm a relatively experienced Python programmer, but haven't written any C in a very long time and am attempting to understand Cython. I'm trying to write a Cython function that will operate on a column of a NumPy recarray.

The code I have so far is below.

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

At the interpreter prompt:

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

This simply sums the f0 column of the recarray. It compiles and runs without a problem.

My question is, how would I modify this so that it can operate on any column? In the example above, the column to sum is hard coded and accessed through dot notation. Is it possible to change the function so the column to sum is passed in as a parameter?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

·深蓝 2025-01-15 15:21:05

我相信使用 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以指示该数组可以是假设是连续的。与往常一样,一旦代码经过测试,请关闭 boundscheckwraparoundnonecheck 编译器指令 可能会进一步提高速度。

I believe this should be possible using Cython's memoryviews. Something along these lines should work (code not tested):

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

Speed can probably be increased by ensuring that the record array you pass to Cython is contiguous. On the python (calling) side, you can use np.require, while the function signature should change to rec_cell0[::1] recview to indicate that the array can be assumed to be contiguous. And as always, once the code has been tested, turning off the boundscheck, wraparound and nonecheck compiler directives in Cython will likely further improve speed.

猫烠⑼条掵仅有一顆心 2025-01-15 15:21:05

你想要的需要弱类型,而 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

What you want requires weak typing, which C doesn't have. If all your record types are the same you might be able to pull off something like: (disclaimer I don't have Cython on this machine so I am coding blind).

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文