在 Numpy C 扩展中返回可变长度数组?

发布于 2024-10-11 00:50:46 字数 195 浏览 3 评论 0原文

我之前在这个 site 的帮助下制作了一些 Numpy C 扩展,但到目前为止正如我所看到的,返回的参数都是固定长度的。

有没有办法让 Numpy C 扩展返回可变长度的 numpy 数组?

I have made some Numpy C-extensions before with great help from this site, but as far as I can see the returned parameters are all fixed length.

Is there any way to have a Numpy C-extension return a variable length numpy array instead?

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

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

发布评论

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

评论(2

如果没有你 2024-10-18 00:50:46

您可能会发现使用 Numpy C-API 在 Cython 中进行 numpy 扩展更容易,这简化了过程,因为它允许您混合 python 和 c 对象。在这种情况下,制作可变长度数组没有什么困难,您可以简单地指定具有任意形状的数组。

Cython numpy 教程 可能是有关此主题的最佳来源。

例如,这是我最近编写的一个函数:

import numpy as np
cimport numpy as np
cimport cython

dtype = np.double
ctypedef double dtype_t

np.import_ufunc()
np.import_array()

def ewma(a, d, axis):
    #Calculates the exponentially weighted moving average of array a along axis using the parameter d.
    cdef void *args[1]

    cdef double weight[1]
    weight[0] = <double>np.exp(-d)


    args[0] = &weight[0]

    return apply_along_axis(&ewma_func, np.array(a, dtype = float), np.double, np.double, False, &(args[0]), <int>axis)

cdef void ewma_func(int n, void* aData,int astride, void* oData, int ostride, void** args):
    #Exponentially weighted moving average calculation function 

    cdef double avg = 0.0
    cdef double weight = (<double*>(args[0]))[0]
    cdef int i = 0

    for i in range(n): 

        avg = (<double*>((<char*>aData) + i * astride))[0]*weight + avg * (1.0 - weight) 


        (<double*>((<char*>oData) + i * ostride))[0] = avg  

ctypedef void (*func_1d)(int, void*, int, void*, int, void **)

cdef apply_along_axis(func_1d function, a, adtype, odtype, reduce,  void** args, int axis):
    #generic function for applying a cython function along a particular dimension

    oshape = list(a.shape)

    if reduce :
        oshape[axis] = 1

    out = np.empty(oshape, odtype)

    cdef np.flatiter ita, ito

    ita = np.PyArray_IterAllButAxis(a,   &axis)
    ito = np.PyArray_IterAllButAxis(out, &axis)

    cdef int axis_length = a.shape[axis]
    cdef int a_axis_stride = a.strides[axis]
    cdef int o_axis_stride = out.strides[axis]

    if reduce: 
        o_axis_stride = 0

    while np.PyArray_ITER_NOTDONE(ita):

        function(axis_length, np.PyArray_ITER_DATA (ita), a_axis_stride, np.PyArray_ITER_DATA (ito), o_axis_stride, args)

        np.PyArray_ITER_NEXT(ita)
        np.PyArray_ITER_NEXT(ito)

    if reduce:  
        oshape.pop(axis)
        out.shape = oshape

    return out  

如果这不适合您,有一个函数可以创建任意形状的新空数组(链接)。

You may find it easier to make numpy extensions in Cython using the Numpy C-API which simplifies the process as it allows you to mix python and c objects. In that case there is little difficult about making a variable length array, you can simply specify an array with an arbitrary shape.

The Cython numpy tutorial is probably the best source on this topic.

For example, here is a function I recently wrote:

import numpy as np
cimport numpy as np
cimport cython

dtype = np.double
ctypedef double dtype_t

np.import_ufunc()
np.import_array()

def ewma(a, d, axis):
    #Calculates the exponentially weighted moving average of array a along axis using the parameter d.
    cdef void *args[1]

    cdef double weight[1]
    weight[0] = <double>np.exp(-d)


    args[0] = &weight[0]

    return apply_along_axis(&ewma_func, np.array(a, dtype = float), np.double, np.double, False, &(args[0]), <int>axis)

cdef void ewma_func(int n, void* aData,int astride, void* oData, int ostride, void** args):
    #Exponentially weighted moving average calculation function 

    cdef double avg = 0.0
    cdef double weight = (<double*>(args[0]))[0]
    cdef int i = 0

    for i in range(n): 

        avg = (<double*>((<char*>aData) + i * astride))[0]*weight + avg * (1.0 - weight) 


        (<double*>((<char*>oData) + i * ostride))[0] = avg  

ctypedef void (*func_1d)(int, void*, int, void*, int, void **)

cdef apply_along_axis(func_1d function, a, adtype, odtype, reduce,  void** args, int axis):
    #generic function for applying a cython function along a particular dimension

    oshape = list(a.shape)

    if reduce :
        oshape[axis] = 1

    out = np.empty(oshape, odtype)

    cdef np.flatiter ita, ito

    ita = np.PyArray_IterAllButAxis(a,   &axis)
    ito = np.PyArray_IterAllButAxis(out, &axis)

    cdef int axis_length = a.shape[axis]
    cdef int a_axis_stride = a.strides[axis]
    cdef int o_axis_stride = out.strides[axis]

    if reduce: 
        o_axis_stride = 0

    while np.PyArray_ITER_NOTDONE(ita):

        function(axis_length, np.PyArray_ITER_DATA (ita), a_axis_stride, np.PyArray_ITER_DATA (ito), o_axis_stride, args)

        np.PyArray_ITER_NEXT(ita)
        np.PyArray_ITER_NEXT(ito)

    if reduce:  
        oshape.pop(axis)
        out.shape = oshape

    return out  

If this doesn't suit you, there is a function for making a new empty array with arbitrary shape (link).

つ可否回来 2024-10-18 00:50:46

我将你的问题解释为“我有一个函数,它接受长度为 n 的 NumPy 数组,但它将返回与 n 不同的另一个长度为 m 的数组”。如果是这种情况,您将需要在扩展中 malloc 一个新的 C 数组,例如

new_array = malloc(m * sizeof(int64)); // or whatever your data type is

,然后用它创建一个新的 NumPy 数组。此示例假设一个一维数组:

int npy_intp dims[1];
dims[0] = m;
PyArrayObject *out = (PyArrayObject *)PyArray_SimpleNewFromData(1,          // 1D array
                                                                dims,       // dimensions
                                                                NPY_INT64,  // type
                                                                new_array);
PyArray_ENABLEFLAGS(out, NPY_ARRAY_OWNDATA);

然后返回新数组。这里重要的部分是设置 NPY_ARRAY_OWNDATA 标志,以便在 Python 对象被垃圾收集时释放您分配的内存。

I am interpreting your question to mean "I have a function that takes a NumPy array of length n, but it will return another array of length m different from n." If that is the case, you will need to malloc a new C array in the extension, e.g.

new_array = malloc(m * sizeof(int64)); // or whatever your data type is

then create a new NumPy array with that. This example assumes a 1D array:

int npy_intp dims[1];
dims[0] = m;
PyArrayObject *out = (PyArrayObject *)PyArray_SimpleNewFromData(1,          // 1D array
                                                                dims,       // dimensions
                                                                NPY_INT64,  // type
                                                                new_array);
PyArray_ENABLEFLAGS(out, NPY_ARRAY_OWNDATA);

Then return the new array. The important part here is to set the NPY_ARRAY_OWNDATA flag so that the memory you allocated is freed when the Python object is garbage collected.

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