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 является чем-то простым 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-символьных строк. 18 cdef str [:] out_arr = np.zeros (in_arr.shape, dtype = 'a5')
ValueError: Несоответствие буфера dtype, ожидаемый "объект Unicode", но получил строку
Точно так же, если я использую bytes[:]
, он выдает ошибку "Несоответствие типа буфера, ожидаемый" объект байтов ", но получил строку" - и это даже не касается проблемы с тем фактом, что я нигде не указываю, что эти строки имеют длину 6 ".
Интересно, что я могу включить строки фиксированной длины в структурированный тип, как в этом вопросе, но я не думаю, что это правильный способ объявления типов.
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
является строковым типом Unicode при компиляции с Python3.
Я подозреваю что 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]))))
Эти функции возвращают представление памяти, которое необходимо преобразовать в массив для печати.
версия с одним символом
Однобайтовая копия памяти:
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-я версия в юникоде
Я посмотрел на эту проблему в прошлом году: Cython: хранение Unicode в массиве NumPy
Это обрабатывает строки 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 также имеет пример структурированного массива с байтовыми строками.