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

时间:2017-03-01 22:40:14

标签: python python-3.x numpy cython

我有一个函数,我想使用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[:],它会给出错误“缓冲区dtype不匹配,预期'字节对象'但是得到了一个字符串” - 这甚至没有解决问题,因为我无处可去指定这些字符串的长度为6。

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

1 个答案:

答案 0 :(得分:2)

在Python3会话中,您的a5数组包含bytestrings。

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 说使用Python3编译时str是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]))))

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

单字母版

单字节memoryview copy:

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

2d unicode version

去年我研究过这个问题: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 struct正在创建一个包含5个字节元素的内存视图,这些元素映射到数组S5元素。

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