我有一个函数,我想使用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像int32
,float
,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
该函数编译,但是:
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中包含结构化类型中的固定长度字符串,但我认为这不是声明类型的正确方法。
答案 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
将字符串转换为单个字节并返回。
去年我研究过这个问题: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
版本应该有效。
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也有一个带字节串的结构化数组示例。