#python #pandas #string #lambda #nested-loops
Вопрос:
У меня есть код, который принимает строку ДНК, в которой найдено только 4 символа: A, C, T и G, например «ATACAAG», и для каждого символа, если найдено 3 других возможных символа. Код включает в себя цикл для строки и еще один цикл для списка возможных символов. Проблема в том, что строки очень длинные: до сотен тысяч символов, поэтому это не быстро, и компьютер нагревается (его вентиляторы начинают работать быстро).
Я ищу более быстрый способ сделать это. Я пытался понять список, но это все еще довольно медленно. Я также попытался вызвать код как функцию из лямбды панд, и это все равно занимает около минуты для каждой строки. Это лучшее, что я могу получить?
Для каждого символа код записывает 3 альтернативы в отдельных строках файла.
Код:
bases = set(list('ACGT')) alts = {base: list(bases.difference(base)) for base in bases} def get_variants(data, output_path): # pb: position base, b: base [open(output_path f'/{data.symbol}_variants.txt', 'a').writelines( [f'{data.chromosome}t{data.end index}t{data.end index}t{pb}/{b}t{data.strand}n' for b in alts[pb]]) for index, pb in enumerate(data.sequence)]
Вызов функции для «ATACAAG»:
get_variants(pandas.Series({'symbol': 'XYZ', 'sequence': 'ATACAAG', 'chromosome': 12, 'start': 9067664, 'end': 9067671, 'strand': '-'}), 'write_an_existing_output_directory_path_here')
Выходные данные расположены в файле в следующих столбцах:
chromosome number, start position, end position, original character/alternative character, strand (can or -)
Это приводит к следующим строкам в файле XYZ_variants.txt:
12 9067664 9067664 A/T - 12 9067664 9067664 A/G - 12 9067664 9067664 A/C - 12 9067665 9067665 T/A - 12 9067665 9067665 T/G - 12 9067665 9067665 T/C - 12 9067666 9067666 A/T - 12 9067666 9067666 A/G - 12 9067666 9067666 A/C - 12 9067667 9067667 C/T - 12 9067667 9067667 C/A - 12 9067667 9067667 C/G - 12 9067668 9067668 A/T - 12 9067668 9067668 A/G - 12 9067668 9067668 A/C - 12 9067669 9067669 A/T - 12 9067669 9067669 A/G - 12 9067669 9067669 A/C - 12 9067670 9067670 G/T - 12 9067670 9067670 G/A - 12 9067670 9067670 G/C -
Спасибо.
Комментарии:
1. пожалуйста, приведите пример вашего входного файла/фрейма данных
2. У меня есть. Вопрос включает в себя
pandas.Series
объект, который является примером для строки в фрейме данных.3. Если вы хотите повторить каждый символ в строке, то нет, более быстрого метода не существует. Понимание списка только уменьшает количество кодов, но не ускоряет процесс.
Ответ №1:
Вот как бы я это сделал.
Начиная с фрейма данных:
symbol sequence chromosome start end strand 0 XYZ ATACAAG 12 9067664 9067671 -
Я бы explode
выбрал последовательность, reindex
чтобы иметь все комбинации A/C/G/T и сохранить только то, где начальная база отличается
import numpy as np df2 = df.assign(base=df['sequence'].apply(list)).explode('base').reset_index() df2 = (df2.reindex(df2.index.repeat(4)) .assign(variant=np.tile(list('ACGT'), len(df2))) .loc[lambda d: d['base'].ne(d['variant'])] .assign(var=lambda d:d['base'] '/' d['variant']) )
Промежуточный выход:
gt;gt;gt; df2.head() index symbol sequence chromosome start end strand base variant var 0 0 XYZ ATACAAG 12 9067664 9067671 - A C A/C 0 0 XYZ ATACAAG 12 9067664 9067671 - A G A/G 0 0 XYZ ATACAAG 12 9067664 9067671 - A T A/T 1 0 XYZ ATACAAG 12 9067664 9067671 - T A T/A 1 0 XYZ ATACAAG 12 9067664 9067671 - T C T/C
Затем экспортируйте:
df2[['start', 'end', 'var', 'strand']].to_csv('variants.txt', sep='t', index=False, header=None)
пример вывода (первые строки):
9067664 9067671 A/C - 9067664 9067671 A/G - 9067664 9067671 A/T - 9067664 9067671 T/A - 9067664 9067671 T/C - 9067664 9067671 T/G - 9067664 9067671 A/C - 9067664 9067671 A/G - 9067664 9067671 A/T - 9067664 9067671 C/A -
оптимизация
Теперь мы удаляем все, что не нужно, чтобы сохранить минимальный размер:
df2 = (df.drop(columns=['symbol', 'chromosome']) .assign(sequence=df['sequence'].apply(list)) .explode('sequence').reset_index(drop=True) ) df2 = (df2.reindex(df2.index.repeat(4)) .assign(var=np.tile(list('ACGT'), len(df2))) .loc[lambda d: d['sequence'].ne(d['var'])] .assign(var=lambda d:d['sequence'] '/' d['var']) ) df2[['start', 'end', 'var', 'strand']].to_csv('variants.txt', sep='t', index=False, header=None)
Комментарии:
1. Спасибо. Является ли это хорошим подходом, учитывая, что последовательности состоят из сотен тысяч символов? Я попытался использовать ваш код и получил фрейм данных примерно в 3,3 миллиона строк. В будущем он будет работать на сервере с большим количеством памяти и ядер, так что все может быть в порядке.
2. Ну, если у вас миллионы строк, это займет время, несмотря ни на что. Хорошая вещь здесь в том, что ваши строки, вероятно, независимы, поэтому это идеальный случай для разделения входных данных на более мелкие фреймы данных, применения к разным потокам/ядрам и объединения выходных данных 😉 Дай мне знать, как все пройдет!
3. Кроме того, вывод включает хромосому в первом столбце, поэтому первая строка кода будет
df2 = (df.drop(columns=['symbol'])
, пятая строка будетdf2 = (df2.reindex(df2.index.repeat(4))
и последняя строка кодаdf2[['chromosome', 'start', 'end', 'var', 'strand']]
?4. Кажется правильным, попробуйте на тестовом образце 😉
5. Я хочу сохранить каждое имя символа / гена в отдельном файле, поэтому я разделил фрейм данных на несколько фреймов данных и использовал предложенный вами код для каждого из них отдельно. Вы бы сделали это по-другому?