如何使用Cython将C结构数组转换为numpy数组

时间:2019-09-09 18:35:03

标签: c numpy cython

我正在尝试创建一个Python模块,该模块将从C共享库返回的C结构数组转换为Numpy数组。我是Numpy和Cython的新手(但是已经有很长时间的C语言了),所以我一直在学习。 一些注意事项: 1)名为HBtrial的C共享库位于另一个目录中 2)C代码calloc()的内存,填充结构并返回指向结构数组的指针 3)我需要返回的数组是Numpy数组(最好是结构化数组,然后可能会转换为Pandas Dataframe)

尝试了许多事情之后,通过执行以下操作,我得到了最远的帮助(包括让.pyx文件进行编译)。

trial.pyx

import cython
from cpython.ref cimport PyTypeObject
cimport numpy as np
import numpy as np
cimport HBtrial


cdef extern from "numpy/ndarrayobject.h":
    object PyArray_NewFromDescr(PyTypeObject *subtype,
        np.dtype newdtype,
        int nd,
        np.npy_intp* dims,
        np.npy_intp* strides,
        void* data,
        int flags,
        object parent)

np.import_array()

class MyStruct(object):
    dtype_mystruct = np.dtype ([('item', 'S16'),
                                ('date', 'S16'),
                                ('val1', 'u1'),
                                ('val2', 'u1'),
                                ('val3', 'i2')
                               ])

    def __init__(self):
        pass

    def return_dtype(self):
        return self.dtype_mystruct

    @cython.boundscheck(False)
    def return_values(self):
        cdef int rows
        cdef HBtrial.MYSTRUCT *arr = HBtrial.return_values(&rows)

        print arr[1]
        print "npy array"
        cdef np.npy_intp dims = rows

        nparr = np.PyArray_NewFromDescr(np.ndarray,
                                        self.dtype_mystruct,
                                        1,
                                        dims,
                                        <object>NULL,
                                        <object><void *>arr,
                                        0,
                                        <object>NULL)

        print nparr[1]
        return nparr

它可以编译,但是随后我尝试在小型Python脚本中使用它,如下所示:

try.py:

#!/usr/bin/env python

import sys
import os
import numpy as np

from trial import MyStruct

def main():
    mystruct = MyStruct()
    dt = mystruct.return_dtype()
    print dt
    arr = mystruct.return_values()
    print arr

if __name__ == "__main__":
    main()

运行它时,它可以很好地打印出“ print dt”行,但是出现以下错误:

Traceback (most recent call last):
  File "./try.py", line 18, in <module>
    main()
  File "./try.py", line 14, in main
    arr = mystruct.return_values()
  File "trial.pyx", line 43, in trial.MyStruct.return_values (trial.c:1569)
    nparr = np.PyArray_NewFromDescr(np.ndarray,
AttributeError: 'module' object has no attribute 'PyArray_NewFromDescr'

如何克服此错误? 我觉得我可能缺少一些基本知识。有任何想法吗?如果我完全不了解自己的方法,也请告诉我。

以下是其他文件,如果有帮助的话:

trial.pxd:

from libc.stdint cimport int8_t, int16_t, uint8_t, uint16_t

cdef extern from "HBtrial.h" nogil:

    ctypedef packed struct MYSTRUCT:
        char item[16];
        char date[16];
        uint8_t val1;
        uint8_t val2;
        int16_t val3;

    cdef MYSTRUCT *return_values(int *rows)

HBtrial.h:

#ifndef HBTRIAL_H
#define HBTRIAL_H

typedef struct {
    char item[16];
    char date[16];
    uint8_t val1;
    uint8_t val2;
    int16_t val3;
} MYSTRUCT;

MYSTRUCT *return_values(int *rows);

#endif

HBtrial.c:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "HBtrial.h"

MYSTRUCT *return_values(int *rows)
{
    int i;
    MYSTRUCT *arr;
    int numrows = 5;

    arr = calloc(numrows, sizeof(MYSTRUCT));

    for (i=0; i < numrows; i++) {
        sprintf(arr[i].item, "row%d", i);
        sprintf(arr[i].date, "201908100%d", i+1);
        arr[i].val1 = i+2;
        arr[i].val2 = i+i;
        arr[i].val3 = i*i;
    }
    *rows = numrows;
    return(arr);
}

HBtrial.c和HBtrial.h位于/ home / xxxx / lib / try3中,并被编译到共享库“ libHBtrial.so”中。

setup.py:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
import numpy as np

trial = Extension(
    name="trial",
    sources=["trial.pyx"],
    extra_compile_args=["-std=c99"],
    libraries=["HBtrial"],
    library_dirs=["/home/xxxx/lib/try3"],
    include_dirs=[np.get_include(), "/home/xxxx/lib/try3"]
)

setup(
    name="trial",
    ext_modules=cythonize([trial])
)

如果有更好的方法,我也会对此感兴趣。例如,我尝试了其他方法,例如将返回的数组转换为Cython类型的memoryview,或使用np.frombuffer(),但始终会收到一个错误,提示它"Cannot convert MYSTRUCT * to"一个memoryview或python对象或其他任何东西。

1 个答案:

答案 0 :(得分:0)

好吧,我终于开始工作了。感谢@ead的评论和我已经在考虑的事情,我需要对PyArray_FromNewDescr进行调用并更改几个参数。
作为“ cdef extern ...”块的一部分,我添加了:

PyTypeObject PyArray_Type

然后对例程的调用变为:

cdef np.ndarray nparr = PyArray_NewFromDescr(&PyArray_Type,
                                            self.dtype_mystruct,
                                            1,
                                            &dims,
                                            NULL,
                                            <void *>arr,
                                            0,
                                            <object>NULL)

现在,我可以从例程返回时以适当的值打印数组。
至于内存泄漏,根据我发现的其他帖子,我应该可以通过在返回数组之前在数组上设置OWNDATA标志来解决此问题,对吗?