Последовательности как строки

Главная абстракция биоинформатики проста: биологическая последовательность — это строка. И весь арсенал работы со строками сразу ваш.

Последовательность в биоинформатике — упорядоченная цепочка символов из фиксированного алфавита (нуклеотиды или аминокислоты), то есть обычная строка.

Прежде чем парсить форматы файлов, договоримся о модели данных. Если принять, что последовательность — это 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.
Проверьте себя
1. Какое представление обычно используют для ДНК в Python-биоинформатике?
AСписок чисел
BОбычную строку (str)
CМножество (set)
DСловарь
2. Биолог просит участок «с 3-й по 7-ю позицию включительно» (1-based). Какой срез в Python?
Aseq[3:7]
Bseq[2:7]
Cseq[3:8]
Dseq[2:6]
3. Почему важно нормализовать последовательность перед анализом?
AЧтобы она стала короче
BРеальные данные содержат разный регистр, пробелы, символы N — это ломает наивные счётчики
CЭто требование Python
DЧтобы зашифровать данные