Открытые рамки считывания и поиск генов
Чтобы найти ген в геноме, ищут открытые рамки считывания — участки от старт-кодона до стоп-кодона. Это первый шаг предсказания генов.
ORF (Open Reading Frame, открытая рамка считывания) — участок последовательности от старт-кодона ATG до ближайшего стоп-кодона в той же рамке, потенциально кодирующий белок.
Геном — это длинный текст без пробелов и заглавных букв. Где в нём гены? Простейшая гипотеза: ген начинается с ATG и заканчивается стопом, без стопов внутри. Такие участки и есть ORF. Поиск ORF — классическая задача, и она прекрасно решается на чистом Python.
Идея алгоритма
В каждой из трёх рамок плюс-нити ищем ATG, от него читаем тройками до первого стопа — это кандидат-ORF. Затем то же на минус-нити (обратный комплемент). Итого шесть рамок. Длинные ORF вероятнее кодируют настоящий белок, чем короткие случайные.
...A T G | C C C | G G G | T A A... старт кодон кодон стоп |__________ORF__________|
Поиск ORF в трёх рамках
DNA_CODON = {
'TTT':'F','TTC':'F','TTA':'L','TTG':'L','CTT':'L','CTC':'L','CTA':'L','CTG':'L',
'ATT':'I','ATC':'I','ATA':'I','ATG':'M','GTT':'V','GTC':'V','GTA':'V','GTG':'V',
'TCT':'S','TCC':'S','TCA':'S','TCG':'S','CCT':'P','CCC':'P','CCA':'P','CCG':'P',
'ACT':'T','ACC':'T','ACA':'T','ACG':'T','GCT':'A','GCC':'A','GCA':'A','GCG':'A',
'TAT':'Y','TAC':'Y','TAA':'*','TAG':'*','CAT':'H','CAC':'H','CAA':'Q','CAG':'Q',
'AAT':'N','AAC':'N','AAA':'K','AAG':'K','GAT':'D','GAC':'D','GAA':'E','GAG':'E',
'TGT':'C','TGC':'C','TGA':'*','TGG':'W','CGT':'R','CGC':'R','CGA':'R','CGG':'R',
'AGT':'S','AGC':'S','AGA':'R','AGG':'R','GGT':'G','GGC':'G','GGA':'G','GGG':'G',
}
STOPS = {'TAA', 'TAG', 'TGA'}
def find_orfs(seq):
orfs = []
for frame in range(3):
i = frame
while i < len(seq) - 2:
if seq[i:i+3] == 'ATG':
prot = ''
j = i
while j < len(seq) - 2:
codon = seq[j:j+3]
if codon in STOPS:
orfs.append((frame, i, prot))
break
prot += DNA_CODON[codon]
j += 3
i += 3
return orfs
dna = 'AAATGCCCGGGTAAATGTTTTAG'
for frame, start, prot in find_orfs(dna):
print(f'рамка {frame}, старт {start}: {prot}')Вывод:
рамка 2, старт 2: MPG рамка 2, старт 14: MF
Алгоритм нашёл два ORF, оба в рамке 2. Внешний цикл идёт по рамкам, внутренний от каждого ATG читает до стопа. Заметьте: ORF учитываются только если есть стоп — «открытая до конца последовательности» рамка обычно отбрасывается как неполная.
Обе нити: шесть рамок
Гены бывают и на минус-нити, поэтому полноценный поиск прогоняет тот же алгоритм по обратному комплементу. Соберём поиск по всем шести рамкам.
STOPS = {'TAA', 'TAG', 'TGA'}
rc = str.maketrans('ATGC', 'TACG')
def orf_lengths(seq):
found = []
for strand, s in [('+', seq), ('-', seq.translate(rc)[::-1])]:
for frame in range(3):
i = frame
while i < len(s) - 2:
if s[i:i+3] == 'ATG':
j = i
while j < len(s) - 2:
if s[j:j+3] in STOPS:
found.append((strand, (j + 3 - i) // 3))
break
j += 3
i += 3
return found
dna = 'ATGAAATAACCTTACCCCAT'
for strand, codons in orf_lengths(dna):
print(f'нить {strand}: ORF, длина в кодонах = {codons}')Вывод:
нить +: ORF, длина в кодонах = 3 нить -: ORF, длина в кодонах = 3
Как работает под капотом: от ORF к настоящему гену
ORF — лишь кандидат. У бактерий гены плотно упакованы и ORF почти равны генам, поэтому метод работает хорошо. У эукариот всё сложнее: гены разорваны интронами, и наивный поиск ORF найдёт лишь куски. Поэтому реальные предсказатели генов добавляют статистику (характерный состав кодонов кодирующих участков), сигналы промоторов и сплайс-сайтов, машинное обучение и сравнение с известными генами через выравнивание. Но в основе всё равно лежит идея ORF, которую вы только что реализовали.
Частые ошибки
- Искать только на плюс-нити. Половина генов может быть на минус-нити — нужен обратный комплемент.
- Брать любой ATG за старт. Случайных ATG много; фильтруют по длине ORF и контексту.
- Считать ORF готовым геном у эукариот. Интроны рвут ген на части — нужны более тонкие методы.
Итог
- ORF — участок от ATG до стоп-кодона в одной рамке, кандидат на ген.
- Полный поиск прогоняет шесть рамок (3 на плюс-нити, 3 на минус через обратный комплемент).
- Длинные ORF вероятнее кодируют белок, чем короткие случайные.
- У бактерий ORF ≈ ген; у эукариот нужны более сложные предсказатели из-за интронов.