首页 文章

Cython指定固定长度字符串的numpy数组

提问于
浏览
2

我有一个函数,我想使用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 等简单的东西时,这会编译并按预期工作 . 但是,我无法弄清楚如何创建固定长度字符串的类型化存储器视图 - 例如,相当于 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对象'但得到一个字符串

类似地,如果我使用 bytes[:] ,它会给出错误"Buffer dtype mismatch, expected 'bytes object' but got a string" - 这甚至不会导致问题,因为我没有指定这些字符串的长度为6 .

有趣的是,我可以在结构化类型中包含固定长度的字符串,如this question,但我没有't think that'是声明类型的正确方法 .

1 回答

  • 2

    在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') 将被接受为您的函数的输入数组 . 但是复制到 a5 out_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]))))
    

    这些函数返回一个内存视图,必须将其转换为要打印的数组 .

    单个char版本

    单字节memoryview复制:

    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 将字符串转换为单个字节并返回 .

    2 d unicode版本

    我去年调查过这个问题:Cython: storing unicode in numpy array

    这会处理unicode字符串,就像它们是2d int数组的行一样;之前和之后需要重塑 .

    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 struct version

    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也有一个带字节串的结构化数组示例 .

相关问题