【问题标题】:Cython specify numpy array of fixed length stringsCython 指定固定长度字符串的 numpy 数组
【发布时间】:2017-07-21 11:23:01
【问题描述】:

我想使用 Cython 处理大量固定长度字符串的函数。对于标准的 cython 函数,我可以像这样声明数组的类型:

cpdef double[:] g(double[:] in_arr):
    cdef double[:] out_arr = np.zeros(in_arr.shape, dtype='float64')

    cdef i
    for i in range(len(in_arr)):
        out_arr[i] = in_arr[i]

    return out_arr

当 dtype 是 int32floatdouble 等简单的 dtype 时,它​​会按预期编译和工作。但是,我无法弄清楚如何创建固定长度字符串的类型化内存视图 - 即例如,相当于np.dtype('a5')

如果我使用这个:

cpdef str[:] f(str[:] in_arr):
    # arr should be a numpy array of 5-character strings
    cdef str[:] out_arr = np.zeros(in_arr.shape, dtype='a5')

    cdef i
    for i in range(len(in_arr)):
        out_arr[i] = in_arr[i]

    return out_arr

函数可以编译,但是这样:

in_arr = np.array(['12345', '67890', '22343'], dtype='a5')
f(in_arr)

抛出以下错误:

---> 16 cpdef str[:] f(str[:] in_arr): 17 # arr 应该是一个由 5 个字符组成的 numpy 数组 18 cdef str[:] out_arr = np.zeros(in_arr.shape, dtype='a5')

ValueError: 缓冲区 dtype 不匹配,预期为 'unicode object' 但得到了 字符串

类似地,如果我使用bytes[:],它会给出错误“缓冲区 dtype 不匹配,预期为 'bytes object' 但得到了一个字符串”——这甚至没有解决我没有指定的问题这些字符串的长度为 6。

有趣的是,我可以在结构化类型中包含固定长度的字符串,如this question,但我认为这不是声明类型的正确方法。

【问题讨论】:

    标签: python python-3.x numpy cython


    【解决方案1】:

    在 Python3 会话中,您的 a5 数组包含字节串。

    In [165]: np.array(['12345', '67890', '22343'], dtype='a5')
    Out[165]: 
    array([b'12345', b'67890', b'22343'], 
          dtype='|S5')
    

    http://cython.readthedocs.io/en/latest/src/tutorial/strings.html 表示str 用 Python3 编译时是 unicode 字符串类型。

    我怀疑np.array(['12345', '67890', '22343'], dtype='U5') 会被接受为您的函数的输入数组。但是复制到a5out_arr会有问题。

    对象版本

    此循环的对象版本有效:

    cpdef str[:] objcopy(str[:] in_arr):
        cdef str[:] out_arr = np.zeros(in_arr.shape[0], dtype=object)
        cdef int N
        N = in_arr.shape[0]
        for i in range(N):
            out_arr[i] = in_arr[i]
        return out_arr
    
    narr = np.array(['one','two','three'], dtype=object)
    cpy = objcopy(narr)
    print(cpy)
    print(np.array(cpy))
    print(np.array(objcopy(np.array([None,'one', 23.4]))))
    

    这些函数返回一个内存视图,必须将其转换为数组才能打印。

    单字符版本

    单字节内存视图副本:

    cpdef char[:] chrcopy(char[:] in_arr):
        cdef char[:] out_arr = np.zeros(in_arr.shape[0], dtype='uint8')
        cdef int N
        N = in_arr.shape[0]
        for i in range(N):
            out_arr[i] = in_arr[i]
        return out_arr
    print(np.array(chrcopy(np.array([b'one',b'two',b'three']).view('S1'))).view('S5'))
    

    使用view 将字符串转换为单字节并返回。

    二维码版本

    我去年研究过这个问题:Cython: storing unicode in numpy array

    这会处理 unicode 字符串,就好像它们是 2d int 数组的行一样;前后都需要reshape。

    cpdef int[:,:] int2dcopy(int[:,:] in_arr):
        cdef int[:,:] out_arr = np.zeros((in_arr.shape[0], in_arr.shape[1]), dtype=int)
        cdef int N
        N = in_arr.shape[0]
        for i in range(N):
            out_arr[i,:] = in_arr[i,:]
        return out_arr
    
    narr = np.array(['one','two','three', 'four', 'five'], dtype='U5')
    cpy = int2dcopy(narr.view('int').reshape(-1,5))
    print(cpy)
    print(np.array(cpy))
    print(np.array(cpy).view(narr.dtype)) # .reshape(-1)
    

    对于字节串,类似的 2d char 版本应该可以工作。

    c 结构版本

    byte5 = cython.struct(x=cython.char[5])
    cpdef byte5[:] byte5copy(byte5[:] in_arr):
        cdef byte5[:] out_arr = np.zeros(in_arr.shape[0], dtype='|S5')
        cdef int N
        N = in_arr.shape[0]
        for i in range(N):
            out_arr[i] = in_arr[i]
        return out_arr
    
    narr = np.array(['one','four','six'], dtype='|S5')
    cpy = byte5copy(narr)
    print(cpy)
    print(repr(np.array(cpy)))
    # array([b'one', b'four', b'six'], dtype='|S5')
    

    C 结构正在创建一个包含 5 字节元素的内存视图,这些元素映射到数组 S5 元素上。

    https://github.com/cython/cython/blob/master/tests/memoryview/numpy_memoryview.pyx 也有一个带有字节串的结构化数组示例。

    【讨论】:

    • 这并不能说明bytes[:] 不起作用。
    • 去年我想将 unicode 字符串作为二维数组的行来处理,U5 是 5 列 int 行。
    • 还没有时间查看您的编辑,但如果您仍在处理此问题,则无需关注 unicode 部分。如有必要,我可以使用字节串,它们是固定长度的 ASCII 标识符。
    • 到目前为止,我最好的尝试对于 unicode 和字节同样适用——仅使用 int v. uint8(每个“元素”4 个字节 v 1 个)。
    • 是啊,看这个,好像object的版本是最好的,但即使放弃使用memoryview对象的想法,为什么没有这个版本可以做类似于cdef np.ndarray[np.dtype('a6')] arr = ... 的事情吗?将其视为对象数组而不是固定长度字符串数组似乎会丢弃非常有价值的类型和内存布局信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多