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') будет принят в качестве входного массива для вашей функции. Но копирование в a5out_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 также имеет пример структурированного массива с байтовыми строками.

Другие вопросы по тегам