【问题标题】:Using numpy.array in cython在 cython 中使用 numpy.array
【发布时间】:2014-06-09 05:13:47
【问题描述】:

我想以 cython 格式重写一个类并将其保存为 demo.pyx。该类的输入参数可以是具有Nx2 形状的 2D np.array,例如a=np.array([[0.2,-0.8],[3.7,0.02],..,[-0.92,-3.33]]),或instance a=[0.1,2.7] 的列表。

cimport numpy as np
DTYPE = np.float64
ctypedef np.float64_t DTYPE_t
cdef class halo_positions(object):
    cdef unsigned double *x 
    cdef unsigned double *y 
    def __init__(self, np.ndarray[np.float64_t,ndim=2,mode='c'] positions):
        self.x = &positions[:,0]
        self.y = &positions[:,1]

我尝试写的内容导致错误消息如下:

running build_ext
cythoning demo.pyx to demo.c

Error compiling Cython file:
------------------------------------------------------------
...
cimport numpy as np
DTYPE = np.float64
ctypedef np.float64_t DTYPE_t
cdef class halo_positions(object):
    cdef unsigned double *x 
        ^
------------------------------------------------------------

demo.pyx:5:9: Unrecognised type modifier combination

Error compiling Cython file:
------------------------------------------------------------
...
cimport numpy as np
DTYPE = np.float64
ctypedef np.float64_t DTYPE_t
cdef class halo_positions(object):
    cdef unsigned double *x 
    cdef unsigned double *y 
        ^
------------------------------------------------------------

demo.pyx:6:9: Unrecognised type modifier combination

Error compiling Cython file:
------------------------------------------------------------
...
ctypedef np.float64_t DTYPE_t
cdef class halo_positions(object):
    cdef unsigned double *x 
    cdef unsigned double *y 
    def __init__(self, np.ndarray[np.float64_t,ndim=2,mode='c'] positions):
        self.x = &positions[:,0]
                ^
------------------------------------------------------------

demo.pyx:8:17: Cannot take address of Python variable

Error compiling Cython file:
------------------------------------------------------------
...
cdef class halo_positions(object):
    cdef unsigned double *x 
    cdef unsigned double *y 
    def __init__(self, np.ndarray[np.float64_t,ndim=2,mode='c'] positions):
        self.x = &positions[:,0]
        self.y = &positions[:,1]
                ^
------------------------------------------------------------

demo.pyx:9:17: Cannot take address of Python variable

我知道我使用指针的方式有问题,但是如果我想保持xy 的类型不明确,我需要使用这个。我怎样才能让我的class 工作?

【问题讨论】:

  • C 中没有 unsigned double 这样的东西。浮点类型总是有符号的。

标签: python arrays pointers numpy cython


【解决方案1】:

当您执行 positions[:,0]positions[:,1] 时,您正在 Cython 中创建一个新的、一维且未声明的缓冲区。这不是您可以从中获取地址的元素。该地址将对应于数组中的单个值,因此您应该执行以下操作:

cimport numpy as np
DTYPE = np.float64
ctypedef np.float64_t DTYPE_t
cdef class halo_positions(object):
    cdef double *x 
    cdef double *y 
    def __init__(self, np.ndarray[np.float64_t, ndim=2, mode='c'] positions):
        cdef np.ndarray[np.float64_t, ndim=1] buffx, buffy
        buffx = positions[:,0].copy()
        buffy = positions[:,1].copy()
        self.x = &buffx[0]
        self.y = &buffy[0]

请注意:

  • YES,你可以把数组中某个元素的地址作为double *数组使用,比如b=&positions[0,0]positions[0,0]==b[0]positions[0,0+1]==b[1];当positions 是一个 C 连续二维数组时。您没有将np.ndarray 转换为double *,只是直接从内存中访问其数据;

  • 我使用.copy() 来保证您在内存中有连续的数据。如果 positions 是 Fortran 连续的,则这将是不必要的。

【讨论】:

  • 我收到此错误消息 `positions.pyx:16:26: Cannot convert 'double *' to Python object building 'positions' extension'
  • @Dalek,但这个错误似乎在其他地方,您是否已经通过了原始问题指出的问题?
  • 好吧,我添加了def debug(self): print self.x, self.y 并尝试调用它。
  • @Dalek 因为self.xself.y 是原始双精度数组,您无法打印它们,但您可以打印它们的元素self.x[0] 等等...
  • 我发布的那个构建并运行没有任何错误
【解决方案2】:

& 运算符获取对象的地址。您想将self.x 指定为positions[0] 的地址。所以应该是self.x = &position[0]。这读作将 self 的成员 x 设置为位置的第 0 个元素的地址。您尝试做的是将 x 的地址设置为某物。但你不被允许这样做。 & 只允许在方程的右边。

【讨论】:

  • 但是在分配任何东西之前我需要声明xy 的类型吗?
【解决方案3】:

Cython 无法将 numpy 数组转换为 double *,您可以改用 double[:],例如:

cimport numpy as np
cdef class halo_positions(object):
    cdef double *x 
    cdef double *y 
    def __init__(self, double[:] positions):
        self.x = &positions[0]
        self.y = &positions[1]

    def debug(self):
        print self.x[0], self.y[0]

但这很危险:

a = np.array([1.0, 2.0, 3.0, 4.0])
hp = halo_positions(a)
hp.debug()
del a
hp.debug() # x and y is wild pointer now.

也许您应该在 halo_positions 类中保留对 positions 的引用。

【讨论】:

  • 我不声明类型并使用指针的目的是我希望能够提供一个列表,例如 a=[1,2] 给这个类,它能够将它们分开并将它们分配给 @ 987654328@ 和y 或像a = np.array([[1.0, 2.0],[ 3.0, 4.0]]) 这样的二维数组,我可以将第一列分配给x,将第二列分配给y。但是你只是用 1D array 举例 qorks 。
  • 你能发布一些例子来展示如何使用halo_positions类吗?如果你想传递 1-dim 和 2-dim 数组,那么你应该将 positions 声明为 ndarray 对象。
  • 我更新了我写的类以及我想传递给类的输入类型。提前致谢。
【解决方案4】:

您的代码存在一些问题。首先,也是最容易解决的问题是,没有“无符号双精度”之类的东西,因此您应该首先删除 unsigned

另外,&positions[:,0] 不是获取数组地址的正确语法,因为: 将返回一个 Python 对象。你需要做&positions[0,0],它指向数组的初始元素。

应该指出,您的 __init__ 仅适用于 Numpy 数组,而不适用于列表,正如您所说的那样。您必须事先将任何列表转换为数组。还要记住,您需要通过指针保持对您使用的任何数组的有效引用,否则您将遇到麻烦。

不过,根据您想要对代码执行的操作,在您的类中使用指针变量通常可能不是最安全的选择。

【讨论】:

  • 但是我最初应该如何定义 xy
  • 就像你做的那样,但没有未签名,即cdef double *xcdef double *y
【解决方案5】:

我从谷歌的 cython.group 得到的答案非常有效:

import cython
cimport cython

import numpy as np
cimport numpy as np

DTYPE = np.float64
ctypedef np.float64_t DTYPE_t

cdef class halo_positions(object):

    cdef double [:] _x
    property x:
        def __get__(self):
            return np.array(self._x)
        def __set__(self, np.ndarray[DTYPE_t, ndim=1] x):
            self._x = x

    cdef double [:] _y
    property y:
        def __get__(self):
            return np.array(self._y)
        def __set__(self, np.ndarray[DTYPE_t, ndim=1] y):
            self._y = y

    def __init__(self, np.ndarray[DTYPE_t,ndim=2] positions):
        self._x = positions[:,0]
        self._y = positions[:,1]

    def debug(self):
        print self.x, self.y

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-21
    • 2017-08-24
    • 2019-07-30
    • 1970-01-01
    • 2014-07-15
    相关资源
    最近更新 更多