#python #cython
#python #cython
Вопрос:
Это работает плавно и быстро:
solexa_scores = '!"#$%amp;' "'()* ,-./0123456789:;<=>?@ABCDEFGHI"
cdef np.ndarray[np.uint32_t, ndim=2] sums = np.zeros(shape=(length, len(solexa_scores) 33), dtype=np.uint32)
cdef bytes line
cdef str decoded_line
cdef int counter=0 # Useful to know if it's the 3rd or 4th line of the current sequence in fastq.
with gzip.open(file_in, "rb") as f:
for line in f:
if counter%4==0: # first line of the sequence (obtain tile info)
counter=0
elif counter%3==0: # 3rd line of the sequence (obtain the qualities)
decoded_line = line.decode('utf-8')
for n in range(len(decoded_line)): # enumerate(line.decode('utf-8')):
sums[n, ord(decoded_line[n])] =1
counter =1
Здесь суммы numpy ndarray содержат результаты.
Однако вместо одного массива numpy мне нужно неизвестное количество массивов в словаре (с именем tiles), и это код, который должен достичь моей цели:
solexa_scores = '!"#$%amp;' "'()* ,-./0123456789:;<=>?@ABCDEFGHI"
cdef dict tiles = {} # each tile will have it's own 'sums' numpy array
cdef bytes line
cdef str decoded_line
cdef str tile
cdef int counter=0 # Useful to know if it's the 3rd or 4th line of the current sequence in fastq.
with gzip.open(file_in, "rb") as f:
for line in f:
if counter%4==0: # first line of the sequence (obtain tail info)
decoded_line = line.decode('utf-8')
tile = decoded_line.split(':')[4]
if tile != tile_specific and tile not in tiles.keys(): # tile_specific is mentiones elsewhere.
tiles[tile] = np.zeros(shape=(length, len(solexa_scores) 33), dtype=np.uint32)
counter=0
elif counter%3==0: # 3rd line of the sequence (obtain the qualities)
decoded_line = line.decode('utf-8')
for n in range(len(decoded_line)): # enumerate(line.decode('utf-8')):
tiles[tile][n, ord(decoded_line[n])] =1
counter =1
В этом втором примере я априори не знаю количество ключей в плитках словаря, и поэтому массивы numpy будут объявлены и инициализированы во время выполнения (пожалуйста, поправьте меня, если я ошибаюсь или использую неправильные термины).
Cython не переводил / компилировал при использовании cython-объявления массивов numpy, и, следовательно, я оставил его как tiles[tile] = np.zeros(shape=(length, len(solexa_scores) 33), dtype=np.uint32)
.
Поскольку все другие оптимизации cython для кода, который является общим для двух фрагментов, в порядке, я считаю, что это объявление массива numpy является проблемой.
Как я должен это исправить? Здесь в руководстве указаны способы динамического выделения памяти, но я не знаю, как это работает с массивами numpy и должен ли я вообще это делать.
Спасибо!
Ответ №1:
Я бы проигнорировал документацию о динамическом распределении памяти. Это не то, что вы хотите сделать — это очень много на уровне C, и вы обрабатываете объекты Python.
Вы можете легко переназначить переменную, введенную как массив Numpy (или, в равной степени, более новый типизированный memoryview) несколько раз, чтобы она ссылалась на другой массив Numpy. Я подозреваю, что вы хотите что-то вроде
# start of function
cdef np.ndarray[np.uint32_t, ndim=2] tile_array
# in "if counter%4==0":
if tile != tile_specific and tile not in tiles.keys(): # tile_specific is mentiones elsewhere.
tiles[tile] = np.zeros(shape=(length, len(solexa_scores) 33), dtype=np.uint32)
tile_array = tiles[tile] # not a copy! Just two references to exactly the same object
# in "if counter%3==0"
tile_array[n, ord(decoded_line[n])] =1
tile_array = tiles[tile]
Просто для проверки типов требуется небольшая стоимость, поэтому, вероятно, это будет полезно, только если вы используете tile_array
несколько раз между каждым назначением (трудно точно угадать, каков порог, но сопоставьте его с вашей текущей версией).
Комментарии:
1. Я не совсем понимаю, почему это быстрее, но из-за того, что он длится вечно, он теперь заканчивается через 17 секунд для тех же входных данных. Спасибо! Я надеюсь, что скоро смогу понять концепцию memoryview.
2. Больше всего Cython ускоряет индексацию отдельных элементов массивов: например
tile_array[n, ord(decoded_line[n])] =1
. Это изменяется с двух довольно медленных вызовов Python (один для получения и один для установки элемента) на одну быструю операцию C. Для этого Cython должен знать тип массива. Вот почему это значительно быстрее.