#python #list #character
#python #Список #символ
Вопрос:
Я хочу выполнить итерацию по списку kmers, и выбранные элементы содержат только символы A, T, G и C
kmers=["AL","AT","GC","AA","AP"]
for kmer in kmers:
for letter in kmer:
if letter not in ["A","T","G","C"]:
pass
else:
DNA_kmers.append(kmer)
print("DNA_kmers",DNA_kmers)
вывод:
DNA_kmers ['AL', 'AT', 'AT', 'GC', 'GC', 'AA', 'AA', 'AP']
желаемый результат:
DNA_kmers=["AT","GC","AA"]
Единственный метод, который я знаю, это
if "B" in kmer or "D" in kmer or "E" in kmer or "F" in kmer or "H" in kmer or "I" in kmer or "J" in kmer or "K" in kmer or "L" in kmer or "M" in kmer or "N" in kmer or "O" in kmer or "P" in kmer or "Q" in kmer or "R" in kmer or "S" in kmer or "U" in kmer or "V" in kmer or "W" in kmer or "X" in kmer or "Y" in kmer or "Z" in kmer:
pass
Комментарии:
1. Вы гарантируете, что внутренние строки всегда имеют длину 2 или что существует какой-то частотный шаблон с вхождениями символов? Насколько важна эффективность — с каким списком размеров и строками вы имеете дело?
2. @ggorlen Спасибо. Я новичок в python, работаю над практическим файлом с размером файла около 400 тыс. Kmer будет небольшим списком для практики.
Ответ №1:
В данный момент ваш код будет добавлять любые элементы, в которых любой символ совпадает. Мы можем настроить его так, чтобы добавлять только элементы, в которых совпадают оба символа:
kmers=["AL","AT","GC","AA","AP"]
DNA_kmers =[]
for kmer in kmers:
for letter in kmer:
if letter not in ["A","T","G","C"]:
break
else:
DNA_kmers.append(kmer)
print("DNA_kmers",DNA_kmers)
Если вы не знакомы с Python, я использовал else
предложение в for
цикле. Это доступно не на всех языках. else
Блок будет запущен тогда и только тогда, когда цикл завершит все итерации.
Существуют значительно более простые способы сделать то, что вы пытаетесь сделать. Например, следующее выполнит работу, используя понимание вложенного списка:
kmers=["AL","AT","GC","AA","AP"]
allowed = set("AGCT")
print([k for k in kmers if all([c in allowed for c in k])])
Более производительным решением общего назначения является использование регулярных выражений:
import re
kmers=["AL","AT","GC","AA","AP"]
r = re.compile("^[ATGC]*$")
print([k for k in kmers if r.match(k)])
Если мы ограничим проблему только k-мер, где k = 2, мы сможем дополнительно оптимизировать производительность. Производительность регулярных выражений должна немного увеличиться при сопоставлении строки фиксированной длины, например, с помощью [AGCT]{2}
. Мы также можем использовать product
для создания набора, который будет использоваться для поиска в постоянное время:
import itertools
kmers=["AL","AT","GC","AA","AP"]
allowed = {a b for a,b in itertools.product("AGCT", repeat=2)}
print([k for k in kmers if k in allowed])
Комментарии:
1. @ggorlen Я не уверен, перераспределяется ли он каждый раз или нет, но я обновил свой ответ, чтобы вытащить инициализацию. Похоже, это примерно в 2 раза быстрее: repl.it/@jncraton/ImmaterialPassionateIrc#main.py
2. Да, но
all
до сих пор нет раннего спасения, поэтому, еслиk
длина 100000, а первого символа нетallowed
, вы действительно хотите сразу остановиться. Смотрите Мой ответ для правильной версии. Как я уже упоминал, предостережение заключается в том, что если длина строки всегда равна 2, накладные расходы генератора на распределение могут сделать его медленнее, чем LC. Я попросил OP уточнить.3. @ggorlen Верно. Похоже, что этот
re
метод является самым быстрым из этих решений. Выражение компилируется до чего-то эффективного, включающего раннее спасение, которое вы описываете.4. Причина
re
, по которой версия быстра для коротких строк, заключается в том, что она имеет наименьшие накладные расходы на выделение объектов в цикле относительно LC и generator, и это узкое место.5. @ggorlen Это кажется правильным. Я добавил версию, которая использует поиск по набору, и она самая быстрая.