Как я могу распараллелить вычисления в файле fasta, где каждый процессор занимает одну последовательность - PullRequest
0 голосов
/ 11 января 2019

Я не знаю, как распараллелить код в Python, который берет каждую строку файла FASTA и создает некоторую статистику, например, вычисляет содержимое GC. У вас есть несколько советов или библиотек, которые помогут мне сократить время, затрачиваемое на выполнение?

Я пытался использовать os.fork (), но это дает мне больше времени выполнения, чем последовательный код. Вероятно, из-за того, что я не очень хорошо знаю, как дать каждому ребенку различную последовательность.

#Computing GC Content
from Bio import SeqIO                  
with open('chr1.fa', 'r') as f:
    records = list (SeqIO.parse(f,'fasta'))
    GC_for_sequence=[]
    for i in records:
        GC=0
        for j in i:
            if j in "GC":
                GC+=1
        GC_for_sequence.append(GC/len(i))
    print(GC_for_sequence)

Ожидаемое выполнение: каждый процесс занимает одну последовательность и выполняет статистику параллельно.

Ответы [ 2 ]

0 голосов
/ 11 января 2019

несколько замечаний по вашему существующему коду для начала:

  1. Я бы посоветовал не делать: list (SeqIO.parse(…)), поскольку это приостановит выполнение до тех пор, пока все последовательности не будут загружены в память, вам гораздо лучше (память и общее время выполнения), просто оставив его в качестве итератора и потребляя элементы для работников по мере необходимости

  2. цикл по каждому символу довольно медленный, использование str.count будет намного быстрее

сложив это вместе, вы можете сделать:

from Bio import SeqIO

with open('chr1.fa') as fd:
    gc_for_sequence=[]
    for seq in SeqIO.parse(fd, 'fasta'):
        gc = sum(seq.seq.count(base) for base in "GC")
        gc_for_sequence.append(gc / len(seq))

если это все еще недостаточно быстро, вы можете использовать модуль multiprocessing, например:

from Bio import SeqIO
from multiprocessing import Pool

def sequence_gc_prop(seq):
    return sum(seq.count(base) for base in "GC") / len(seq)

with open('chr1.fa') as fd, Pool() as pool:
    gc_for_sequence = pool.map(
        sequence_gc_prop,
        (seq.seq for seq in SeqIO.parse(fd, 'fasta')),
        chunksize=1000,
    )

комментарии Лукаша в основном применимы. другие неочевидные вещи:

  • странные seq.seq for seq in… вещи - убедиться, что мы не собираем ненужные данные
  • Я устанавливаю chunksize на довольно большое значение, потому что функция должна быть быстрой, поэтому мы хотим дать детям разумный объем работы, чтобы родительский процесс не тратил все свое время на управление вещами
0 голосов
/ 11 января 2019

Вот одна идея со стандартным многопроцессорным модулем:

from multiprocessing import Pool
import numpy as np

no_cores_to_use = 4

GC_for_sequence = [np.random.rand(100) for x in range(10)]

with Pool(no_cores_to_use) as pool:
    result = pool.map(np.average, GC_for_sequence)

print(result)

В коде я использовал модуль numpy для имитации списка с некоторым содержанием. pool.map принимает функцию, которую вы хотите использовать в ваших данных, в качестве первого аргумента, а список данных - в качестве второго. Функция, которую вы можете легко определить самостоятельно. По умолчанию он должен принимать один аргумент. Если вы хотите передать больше, используйте functools.partial.

[EDIT] Вот пример, гораздо более близкий к вашей проблеме:

from multiprocessing import Pool
import numpy as np

records = ['ACTGTCGCAGC' for x in range(10)]
no_cores_to_use = 4

def count(sequence):
    count = sequence.count('GC')
    return count

with Pool(no_cores_to_use) as pool:
    result = pool.map(count, records)

print(sum(result))
...