Последовательности как строки
Главная абстракция биоинформатики проста: биологическая последовательность — это строка. И весь арсенал работы со строками сразу ваш.
Последовательность в биоинформатике — упорядоченная цепочка символов из фиксированного алфавита (нуклеотиды или аминокислоты), то есть обычная строка.
Прежде чем парсить форматы файлов, договоримся о модели данных. Если принять, что последовательность — это str, то срезы, поиск, подсчёт, конкатенация работают «из коробки». Это решение определяет стиль всего курса: мы редко создаём классы, чаще работаем со строками и стандартными контейнерами.
Что даёт строковая модель бесплатно
| Операция со строкой | Биологический смысл |
len(seq) | длина последовательности |
seq[10:20] | вырезать участок (например, ген) |
seq.count("GC") | сколько раз встречается мотив |
seq.find("ATG") | позиция первого старт-кодона |
seq[::-1] | развернуть (шаг к обратному комплементу) |
seq = "ATGCGTACGTTAGCATG"
print("Длина:", len(seq))
print("Участок [4:9]:", seq[4:9])
print("Сколько ATG:", seq.count("ATG"))
print("Первый ATG в позиции:", seq.find("ATG"))
print("Развёрнутая:", seq[::-1])Вывод:
Длина: 17 Участок [4:9]: GTACG Сколько ATG: 2 Первый ATG в позиции: 0 Развёрнутая: GTACGATTGCATGCGTA
Координаты: 0-based против 1-based
Здесь скрыт самый коварный подвох биоинформатики. Python индексирует с нуля и использует полуоткрытые интервалы. Но многие биологические форматы и базы (GFF, VCF, привычка биологов) считают с единицы и включают оба конца. Перепутать — значит сдвинуть весь анализ на одну позицию.
Строка: A T G C G 0-based: 0 1 2 3 4 срез [1:3) = TG 1-based: 1 2 3 4 5 позиции 2..3 = TG
Правило: внутри Python считайте с нуля, а при вводе/выводе в биологические форматы аккуратно конвертируйте. Покажем конвертацию.
# Биолог говорит: ген с 3-й по 7-ю позицию включительно (1-based)
seq = "AATGCCGTTA"
start_1based, end_1based = 3, 7
gene = seq[start_1based - 1 : end_1based] # перевод в 0-based полуоткрытый
print("Ген:", gene)Вывод:
Ген: TGCCG
Как работает под капотом: валидация алфавита
Строка позволяет хранить что угодно, но биологическая последовательность — только буквы алфавита. Реальные данные грязны: встречаются строчные буквы, символ N (неизвестный нуклеотид), пробелы, переводы строк. Хорошая привычка — нормализовать и проверять.
def clean_dna(raw):
seq = raw.upper().replace(" ", "").replace("\n", "")
allowed = set("ATGCN")
bad = set(seq) - allowed
if bad:
raise ValueError(f"Недопустимые символы: {bad}")
return seq
print(clean_dna("atg cgt\nacn"))Вывод:
ATGCGTACN
Когда строки мало
Для геномов в миллиарды букв одна строка в памяти — это гигабайты. Тогда переходят к потоковой обработке (читать файл кусками), к bytes вместо str, к 2-битному кодированию (A/T/G/C влезают в 2 бита). Но для обучения и большинства задач обычной строки достаточно — оптимизацию оставляем на потом.
Стоит заранее понимать порядки величин, с которыми работает биоинформатика, — это объясняет, почему сложность алгоритмов здесь критична. Геном бактерии — это миллионы букв, геном человека — около 3,2 миллиарда, а в одном эксперименте по секвенированию РНК легко получаются десятки миллионов ридов. Алгоритм со сложностью O(n²), безобидный на строке в 100 символов, при n в миллиарды потребовал бы 10¹⁸ операций — это годы счёта. Поэтому в курсе мы постоянно будем отмечать, как алгоритм масштабируется: то, что мгновенно на учебном примере, должно оставаться выполнимым на реальном геноме, иначе метод бесполезен на практике.
Ещё одна практическая деталь — неоднозначные символы. Кроме N (любой нуклеотид) стандарт IUPAC определяет буквы для частичной неопределённости: R значит «A или G» (пурин), Y — «C или T» (пиримидин), и так далее. В сырых данных они встречаются редко, но устойчивый код не должен падать, наткнувшись на такой символ. Хорошее правило: явно решите, что делать с не-ATGC буквами — игнорировать, заменять на N или сообщать об ошибке, — и зафиксируйте это решение в коде, а не полагайтесь на случай.
Частые ошибки
- Off-by-one в координатах. Самый частый баг: путаница 0-based/1-based и включения концов.
- Не нормализовать регистр. 'a' и 'A' — разные символы для
count; приводите к одному регистру. - Забыть про N. Неизвестные нуклеотиды ломают наивные счётчики, если их не учесть.
Итог
- Биологическая последовательность — это обычная строка, и весь инструментарий строк доступен сразу.
- Срезы, поиск, подсчёт, разворот покрывают огромную долю базовых операций.
- Главный подвох — координаты: Python 0-based полуоткрытый, биология часто 1-based включительно.
- Реальные данные грязны: нормализуйте регистр, чистите пробелы, учитывайте символ N.