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

Pau*_*aul 4 python numpy cython python-3.x

我有一个函数,我想使用 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
Run Code Online (Sandbox Code Playgroud)

当 dtype 是简单的东西(如 、 、 等)时,它会按预期进行编译和工作int32float但是double,我无法弄清楚如何创建固定长度字符串的类型化内存视图 - 例如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
Run Code Online (Sandbox Code Playgroud)

该函数可以编译,但是:

in_arr = np.array(['12345', '67890', '22343'], dtype='a5')
f(in_arr)
Run Code Online (Sandbox Code Playgroud)

抛出以下错误:

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

ValueError:缓冲区数据类型不匹配,预期为“unicode 对象”,但得到了一个字符串

同样,如果我使用bytes[:],它会给出错误“缓冲区 dtype 不匹配,预期是‘字节对象’,但得到了一个字符串”——而且这甚至没有解决问题,因为我没有指定这些字符串的长度为 6。

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

hpa*_*ulj 5

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

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

http://cython.readthedocs.io/en/latest/src/tutorial/strings.htmlstr用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]))))
Run Code Online (Sandbox Code Playgroud)

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

单字符版本

单字节内存视图副本:

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'))
Run Code Online (Sandbox Code Playgroud)

用于view将字符串与单个字节相互转换。

2d unicode 版本

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

这会像处理 2d int 数组的行一样处理 unicode 字符串;之前和之后都需要重塑。

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)
Run Code Online (Sandbox Code Playgroud)

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

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')
Run Code Online (Sandbox Code Playgroud)

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

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