如何将布尔掩码存储为Cython类的属性?

3

我未能将一个布尔掩码作为Cython类的属性保存。在实际代码中,我需要此掩码以更高效地执行任务。以下是一个示例代码:

core.pyx

import numpy as np
cimport numpy as np

cdef class MyClass():
    cdef public np.uint8_t[:] mask # uint8 has the same data structure of a boolean array
    cdef public np.float64_t[:] data

    def __init__(self, size):
        self.data = np.random.rand(size).astype(np.float64)
        self.mask = np.zeros(size, np.uint8)

script.py

import numpy as np
import pyximport
pyximport.install(setup_args={'include_dirs': np.get_include()})

from core import MyClass

mc = MyClass(1000000)
mc.mask = np.asarray(mc.data) > 0.5 

错误

当我运行 script.py 时,Cython 成功编译,但却抛出以下错误:

Traceback (most recent call last):
  File "script.py", line 8, in <module>
    mc.mask = np.asarray(mc.data) > 0.5
  File "core.pyx", line 6, in core.MyClass.mask.__set__
    cdef public np.uint8_t[:] mask
ValueError: Does not understand character buffer dtype format string ('?')

解决方法

我的当前解决方法是在所有需要的函数中传递掩码,使用 cast=True,例如:

cpdef func(MyClass mc, np.ndarray[np.uint8_t, ndim=1, cast=True] mask):
    return np.asarray(mc.data)[mask]

问题

有没有任何关于如何在Cython类中存储掩码的想法?

2个回答

2

所以我不认为memoryview实际上支持布尔索引。因此,要索引数组,您始终需要执行以下操作:

np.asarray(mc.data)[mask]
# or
mc.data.base[mask] # if you're sure it's always a view of something that supports boolean indexing)

我认为@ead提到的Cython更新不会改变这一点。我猜想原因是赋值操作(mc.data[mask] = x)可能相当容易,但是mc.data[mask]应该返回什么类型并不明显 - 它不是一个memoryview。
因此,无论您做什么都将涉及一些混乱的代码。
对于分配给memoryview的部分,可以使用以下方法完成。
mc.mask = (np.asarray(mc.data) > 0.5).view(np.uint8)

使用以下代码将其转换为 Numpy 布尔数组并返回:

np.asarray(mc.mask).view(np.bool)

这两种方式都不应涉及复制。


如果是我设计的话,我会将memoryviews设置为非公共属性(仅供Cython使用),并在Python接口中使用普通对象属性来保存底层Numpy数组。您可以使用property来保持它们同步(并进行强制转换):

cdef class MyClass:
    cdef np.uint8_t[:] mask_mview
    cdef object _mask

    @property
    def mask(self):
        return np.asarray(self._mask).view(np.bool)

    @mask.setter
    def mask(self, value):
        self._mask = value
        self.mask_view = value.view(np.uint8)

    # and the same for data

这样你就可以拥有一个memoryview,用于在Cython中快速逐个元素地迭代,同时可以在Python中访问纯Numpy数组,两者保持同步(至少由Python接口保证)。


1

如果您不想使用解决方法,最好的选择可能是等待发布Cython 0.29.14。这个问题已经被修复,并且很可能是0.29.14的一部分。

以下是一个最简示例

%%cython
import numpy as np
cimport numpy as np
cdef np.uint8_t[:] mask  = np.random.rand(20)>.5

通常情况下,使用以下代码将无法导入:

ValueError:不理解字符缓冲区dtype格式字符串(“?”)

对于Cython 0.29.13,但可以在 github上的0.29.x分支(或主分支)的当前状态下工作。


太好了听到这个消息,一直携带这些口罩的解决方法相当烦人,谢谢。 - Saullo G. P. Castro
如果有帮助的话,mc.mask = (np.asarray(mc.data) > 0.5).view(np.uint8_t) 可以正确地分配(作为另一种解决方法)。 - DavidW
@DavidW 看起来很有前途,你能详细说明一下吗?你可以使用像这样存储的掩码进行花式索引吗? - Saullo G. P. Castro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接