Биоинформатика с Python: книга рецептов: Современные библиотеки и приложения Python для решения реальных задач вычислительной биологии [3 ed.] 9785937002013

Биоинформатика – активная область исследований, в которой используется ряд простых и сложных вычислений для извлечения ц

415 94 20MB

Russian Pages 344 [345] Year 2023

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

Биоинформатика с Python: книга рецептов: Современные библиотеки и приложения Python для решения реальных задач вычислительной биологии [3 ed.]
 9785937002013

  • Commentary
  • Publisher's PDF

Table of contents :
О авторе
О рецензентах
От издательства
Предисловие
Глава 1
Python и окружающее программное обеспечение
Установка необходимого базового программного обеспечения с помощью Anaconda
Подготовка
Как это сделать...
Дополнительно...
Установка необходимого программного обеспечения с помощью Docker
Подготовка
Как это сделать...
Смотрите также
Взаимодействие с R через rpy2
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Демонстрация R magic с Jupyter
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Глава 2
Знакомство с NumPy, pandas, Arrow и Matplotlib
Использование pandas для обработки побочных эффектов вакцин
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Устранение подводных камней при использовании pandas DataFrames
Подготовка
Как это сделать...
Дополнительно...
Уменьшение потребления памяти pandas DataFrames
Подготовка
Как это сделать…
Смотрите также
Ускорение обработки pandas с помощью Apache Arrow
Подготовка
Как это сделать...
Дополнительно...
NumPy как основа науки о данных и биоинформатики Python
Подготовка
Как это сделать…
Смотрите также
Matplotlib как инструмент создания диаграмм
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Глава 3
Секвенирование следующего поколения
Доступ в GenBank и перемещение по базам данных NCBI
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Выполнение базового анализа последовательности
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Работа с современными форматами последовательностей
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Работа с данными выравнивания
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Извлечение данных из файлов VCF
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Изучение доступности генома и фильтрация данных SNP
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Обработка данных NGS с помощью HTSeq
Подготовка
Как это сделать...
Дополнительно...
Глава 4
Продвинутый процессинг данных NGS
Подготовка массива данных для анализа
Подготовка
Как это сделать…
Использование информации о менделевских ошибках для контроля качества
Как это сделать…
Дополнительно…
Анализ данных с помощью стандартной статистики
Как это сделать…
Дополнительно…
Поиск геномных особенностей из аннотаций секвенирования
Как это сделать…
Дополнительно…
Метагеномика с QIIME 2 Python API
Подготовка
Как это сделать...
Дополнительно...
Глава 5
Работа с геномами
Технические требования
Работа с высококачественными референсными геномами
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Работа с референсными геномами низкого качества
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Перебор аннотаций генома
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Извлечение генов из референса с помощью аннотаций
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Поиск ортологов с помощью Ensembl REST API
Подготовка
Как это сделать...
Дополнительно...
Получение информации об онтологии генов из Ensembl
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Глава 6
Популяционная генетика
Управление наборами данных с помощью PLINK
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Использование sgkit для генетического анализа популяции с помощью xarray
Подготовка
Как это сделать...
Дополнительно...
Изучение набора данных с помощью sgkit
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Анализ структуры популяции
Подготовка
Как это сделать...
Смотрите также
Выполнение PCA
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Исследование структуры популяции с admixture
Подготовка
Как это сделать...
Дополнительно...
Глава 7
Филогенетика
Подготовка набора данных для филогенетического анализа
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Выравнивание генетических и геномных данных
Подготовка
Как это сделать...
Сравнение последовательностей
Подготовка
Как это сделать...
Дополнительно...
Реконструкция филогенетических деревьев
Подготовка
Как это сделать...
Дополнительно...
Рекурсивная игра с деревьями
Подготовка
Как это сделать...
Дополнительно...
Визуализация филогенетических данных
Подготовка
Как это сделать...
Дополнительно...
Глава 8
Использование Protein Data Bank
Поиск белка во множественных базах данных
Подготовка
Как это сделать...
Дополнительно
Представляем Bio.PDB
Подготовка
Как это сделать...
Дополнительно
Извлечение дополнительной информации из файла PDB
Подготовка
Как это сделать...
Вычисление молекулярных расстояний в файле PDB
Подготовка
Как это сделать...
Выполнение геометрических операций
Подготовка
Как это сделать...
Дополнительно
Анимация с PyMOL
Подготовка
Как это сделать...
Дополнительно
Парсинг файлов mmCIF с помощью Biopython
Подготовка
Как это сделать...
Дополнительно
Глава 9
Конвейеры биоинформатики
Представляем серверы Galaxy
Подготовка
Как это сделать…
Дополнительно
Доступ к Galaxy с помощью API
Подготовка
Как это сделать…
Развертывание конвейера анализа вариантов с помощью Snakemake
Подготовка
Как это сделать…
Дополнительно
Развертывание конвейера анализа вариантов с помощью Nextflow
Подготовка
Как это сделать…
Дополнительно
Глава 10
Машинное обучение в биоинформатике
Знакомство со scikit-learn на примере PCA
Подготовка
Как это сделать...
Дополнительно...
Использование кластеризации по PCA для классификации образцов
Подготовка
Как это сделать...
Дополнительно...
Изучение признаков рака молочной железы с помощью деревьев принятий решений
Подготовка
Как это сделать...
Прогнозирование диагностики рака молочной железы с использованием методов случайного леса
Подготовка
Как это сделать…
Дополнительно...
Глава 11
Параллельная обработка с Dask и Zarr
Чтение геномных данных с помощью Zarr
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Параллельная обработка данных с использованием многопроцессорности Python
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Использование Dask для обработки геномных данных на основе массивов NumPy
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Планирование задач с помощью dask.distributed
Подготовка
Как это сделать...
Дополнительно...
Смотрите также
Глава 12
Функциональное программирование в биоинформатике
Представление чистых функций
Подготовка
Как это сделать...
Дополнительно...
Представление о немутабельности
Подготовка
Как это сделать...
Дополнительно...
Избежание мутабельности для надежности шаблона проектирования
Подготовка
Как это сделать...
Дополнительно...
Использование ленивого программирования для конвейерной обработки
Подготовка
Как это сделать...
Дополнительно...
Ограничения рекурсии в Python
Подготовка
Как это сделать...
Дополнительно...
Демонстрация модуля Python functools
Подготовка
Как это сделать...
Дополнительно...
Смотрите также...
Предметный указатель

Citation preview

Тиаго Антао

Биоинформатика с Python: книга рецептов

Third edition

Bioinformatics with Python Cookbook Use modern Python libraries and applications to solve real-world computational biology problems Tiago Antao

BIRMINGHAM—MUMBAI

Третье издание

Биоинформатика с Python: книга рецептов Современные библиотеки и приложения Python для решения реальных задач вычислительной биологии Тиаго Антао

Москва, 2023

УДК 575.112 ББК 30.16 А72

Антао Т. А72 Биоинформатика с  Python: книга рецептов: Современные библиотеки и приложения Python для решения реальных задач вычислительной биологии / пер. с англ. И. Л. Люско. – М.: ДМК Пресс, 2023. – 344 с.: ил. ISBN 978-5-93700-201-3 Биоинформатика – активная область исследований, в которой используется ряд простых и сложных вычислений для извлечения ценной информации из биологических данных. Из книги вы узнаете, как управлять этими задачами с помощью языка Python. Вы рассмотрите ключевые методы секвенирования нового поколения, анализа отдельных клеток, геномики, метагеномики, а также узнаете, как применяются алгоритмы машинного обучения в биоинформатике. Книга предназначена для аналитиков в области биоинформатики, специалистов по данным, вычислительных биологов, исследователей и разработчиков Python.

УДК 575.112 ББК 30.16 First published in the English language under the title ‘Bioinformatics with Python Cookbook – Third Edition’ – (9781803236421) Все права защищены. Любая часть этой книги не может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.

ISBN (анг.) 978-1-80323-642-1 ISBN (рус.) 978-5-93700-201-3

Copyright © Packt Publishing 2022 ©  Оформление, издание, перевод, ДМК Пресс, 2023

Оглавление Об авторе............................................................................................................12 О рецензентах..................................................................................................13 От издательства...............................................................................................14 Предисловие.....................................................................................................15 Глава 1. Python и окружающее программное обеспечение....... 20 Установка необходимого базового программного обеспечения с помощью Anaconda.........................................................................................21 Подготовка.....................................................................................................21 Как это сделать..............................................................................................23 Дополнительно..............................................................................................25 Установка необходимого программного обеспечения с помощью Docker......26 Подготовка.....................................................................................................27 Как это сделать..............................................................................................27 Смотрите также.............................................................................................28 Взаимодействие с R через rpy2.........................................................................28 Подготовка.....................................................................................................28 Как это сделать..............................................................................................29 Дополнительно..............................................................................................35 Смотрите также.............................................................................................35 Демонстрация R magic с Jupyter.......................................................................36 Подготовка.....................................................................................................36 Как это сделать..............................................................................................36 Дополнительно..............................................................................................37 Смотрите также.............................................................................................38

Глава 2. Знакомство с NumPy, pandas, Arrow и Matplotlib........... 39 Использование pandas для обработки побочных эффектов вакцин.............40 Подготовка.....................................................................................................40 Как это сделать..............................................................................................40 Дополнительно..............................................................................................45 Смотрите также.............................................................................................45 Устранение подводных камней при использовании pandas DataFrames......46 Подготовка.....................................................................................................46 Как это сделать..............................................................................................47 Дополнительно..............................................................................................49 Уменьшение потребления памяти pandas DataFrames...................................49 Подготовка.....................................................................................................49

6    Оглавление Как это сделать…...........................................................................................49 Смотрите также.............................................................................................52 Ускорение обработки pandas с помощью Apache Arrow.................................53 Подготовка.....................................................................................................53 Как это сделать..............................................................................................53 Дополнительно..............................................................................................55 NumPy как основа науки о данных и биоинформатики Python.....................56 Подготовка.....................................................................................................56 Как это сделать…...........................................................................................56 Смотрите также.............................................................................................59 Matplotlib как инструмент создания диаграмм...............................................59 Подготовка.....................................................................................................60 Как это сделать..............................................................................................60 Дополнительно..............................................................................................66 Смотрите также.............................................................................................67

Глава 3. Секвенирование следующего поколения....................... 68 Доступ в GenBank и перемещение по базам данных NCBI.............................69 Подготовка.....................................................................................................70 Как это сделать..............................................................................................70 Дополнительно..............................................................................................74 Смотрите также.............................................................................................75 Выполнение базового анализа последовательности......................................75 Подготовка.....................................................................................................75 Как это сделать..............................................................................................75 Дополнительно..............................................................................................77 Смотрите также.............................................................................................77 Работа с современными форматами последовательностей...........................77 Подготовка.....................................................................................................78 Как это сделать..............................................................................................79 Дополнительно..............................................................................................85 Смотрите также.............................................................................................86 Работа с данными выравнивания....................................................................86 Подготовка.....................................................................................................87 Как это сделать..............................................................................................87 Дополнительно..............................................................................................93 Смотрите также.............................................................................................93 Извлечение данных из файлов VCF..................................................................94 Подготовка.....................................................................................................94 Как это сделать..............................................................................................95 Дополнительно..............................................................................................96 Смотрите также.............................................................................................97 Изучение доступности генома и фильтрация данных SNP............................97 Подготовка.....................................................................................................97 Как это сделать..............................................................................................99 Дополнительно............................................................................................109 Смотрите также...........................................................................................109

Оглавление    7

Обработка данных NGS с помощью HTSeq....................................................110 Подготовка...................................................................................................110 Как это сделать............................................................................................111 Дополнительно............................................................................................113

Глава 4. Продвинутый процессинг данных NGS.........................114 Подготовка массива данных для анализа......................................................114 Подготовка...................................................................................................115 Как это сделать….........................................................................................115 Использование информации о менделевских ошибках для контроля качества.....................................................................................121 Как это сделать….........................................................................................121 Дополнительно….........................................................................................125 Анализ данных с помощью стандартной статистики...................................125 Как это сделать….........................................................................................126 Дополнительно….........................................................................................130 Поиск геномных особенностей из аннотаций секвенирования..................131 Как это сделать….........................................................................................131 Дополнительно….........................................................................................133 Метагеномика с QIIME 2 Python API..............................................................133 Подготовка...................................................................................................134 Как это сделать............................................................................................135 Дополнительно............................................................................................138

Глава 5. Работа с геномами............................................................139 Технические требования.................................................................................139 Работа с высококачественными референсными геномами.........................139 Подготовка...................................................................................................140 Как это сделать............................................................................................140 Дополнительно............................................................................................145 Смотрите также...........................................................................................145 Работа с референсными геномами низкого качества..................................146 Подготовка...................................................................................................146 Как это сделать............................................................................................147 Дополнительно............................................................................................151 Смотрите также...........................................................................................152 Перебор аннотаций генома............................................................................152 Подготовка...................................................................................................152 Как это сделать............................................................................................152 Дополнительно............................................................................................154 Смотрите также...........................................................................................155 Извлечение генов из референса с помощью аннотаций..............................155 Подготовка...................................................................................................155 Как это сделать............................................................................................155 Дополнительно............................................................................................158 Смотрите также...........................................................................................158 Поиск ортологов с помощью Ensembl REST API............................................159 Подготовка...................................................................................................159

8    Оглавление Как это сделать............................................................................................159 Дополнительно............................................................................................162 Получение информации об онтологии генов из Ensembl............................162 Подготовка...................................................................................................162 Как это сделать............................................................................................163 Дополнительно............................................................................................166 Смотрите также...........................................................................................167

Глава 6. Популяционная генетика.................................................168 Управление наборами данных с помощью PLINK........................................169 Подготовка...................................................................................................170 Как это сделать............................................................................................171 Дополнительно............................................................................................175 Смотрите также...........................................................................................176 Использование sgkit для генетического анализа популяции с помощью xarray.............................................................................................176 Подготовка...................................................................................................176 Как это сделать............................................................................................176 Дополнительно............................................................................................180 Изучение набора данных с помощью sgkit....................................................180 Подготовка...................................................................................................180 Как это сделать............................................................................................180 Дополнительно............................................................................................184 Смотрите также...........................................................................................184 Анализ структуры популяции........................................................................184 Подготовка...................................................................................................184 Как это сделать............................................................................................185 Смотрите также...........................................................................................191 Выполнение PCA..............................................................................................191 Подготовка...................................................................................................191 Как это сделать............................................................................................192 Дополнительно............................................................................................194 Смотрите также...........................................................................................194 Исследование структуры популяции с admixture..........................................194 Подготовка...................................................................................................194 Как это сделать............................................................................................195 Дополнительно............................................................................................199

Глава 7. Филогенетика.....................................................................200 Подготовка набора данных для филогенетического анализа......................200 Подготовка...................................................................................................200 Как это сделать............................................................................................201 Дополнительно............................................................................................206 Смотрите также...........................................................................................206 Выравнивание генетических и геномных данных.......................................207 Подготовка...................................................................................................207 Как это сделать............................................................................................207

Оглавление    9

Сравнение последовательностей...................................................................209 Подготовка...................................................................................................209 Как это сделать............................................................................................209 Дополнительно............................................................................................214 Реконструкция филогенетических деревьев.................................................214 Подготовка...................................................................................................214 Как это сделать............................................................................................215 Дополнительно............................................................................................218 Рекурсивная игра с деревьями.......................................................................219 Подготовка...................................................................................................219 Как это сделать............................................................................................219 Дополнительно............................................................................................224 Визуализация филогенетических данных.....................................................224 Подготовка...................................................................................................224 Как это сделать............................................................................................224 Дополнительно............................................................................................229

Глава 8. Использование Protein Data Bank..................................230 Поиск белка во множественных базах данных..............................................231 Подготовка...................................................................................................231 Как это сделать............................................................................................231 Дополнительно............................................................................................235 Представляем Bio.PDB.....................................................................................235 Подготовка...................................................................................................236 Как это сделать............................................................................................236 Дополнительно............................................................................................240 Извлечение дополнительной информации из файла PDB...........................240 Подготовка...................................................................................................240 Как это сделать............................................................................................240 Вычисление молекулярных расстояний в файле PDB...................................244 Подготовка...................................................................................................244 Как это сделать............................................................................................245 Выполнение геометрических операций........................................................249 Подготовка...................................................................................................249 Как это сделать............................................................................................249 Дополнительно............................................................................................252 Анимация с PyMOL..........................................................................................252 Подготовка...................................................................................................252 Как это сделать............................................................................................253 Дополнительно............................................................................................258 Парсинг файлов mmCIF с помощью Biopython.............................................258 Подготовка...................................................................................................259 Как это сделать............................................................................................259 Дополнительно............................................................................................260

Глава 9. Конвейеры биоинформатики..........................................261 Представляем серверы Galaxy........................................................................262

10    Оглавление Подготовка...................................................................................................262 Как это сделать….........................................................................................262 Дополнительно............................................................................................264 Доступ к Galaxy с помощью API......................................................................264 Подготовка...................................................................................................264 Как это сделать….........................................................................................266 Развертывание конвейера анализа вариантов с помощью Snakemake.......272 Подготовка...................................................................................................272 Как это сделать….........................................................................................273 Дополнительно............................................................................................277 Развертывание конвейера анализа вариантов с помощью Nextflow...........278 Подготовка...................................................................................................279 Как это сделать….........................................................................................279 Дополнительно............................................................................................283

Глава 10. Машинное обучение в биоинформатике....................284 Знакомство со scikit-learn на примере PCA...................................................285 Подготовка...................................................................................................285 Как это сделать............................................................................................285 Дополнительно............................................................................................287 Использование кластеризации по PCA для классификации образцов........287 Подготовка...................................................................................................288 Как это сделать............................................................................................288 Дополнительно............................................................................................293 Изучение признаков рака молочной железы с помощью деревьев принятий решений..........................................................................................293 Подготовка...................................................................................................294 Как это сделать............................................................................................294 Прогнозирование диагностики рака молочной железы с использованием методов случайного леса.................................................297 Подготовка...................................................................................................297 Как это сделать….............................................................................................297 Дополнительно............................................................................................299

Глава 11. Параллельная обработка с Dask и Zarr.......................300 Чтение геномных данных с помощью Zarr....................................................301 Подготовка...................................................................................................301 Как это сделать............................................................................................301 Дополнительно............................................................................................306 Смотрите также...........................................................................................306 Параллельная обработка данных с использованием многопроцессорности Python.........................................................................306 Подготовка...................................................................................................307 Как это сделать............................................................................................307 Дополнительно............................................................................................308 Смотрите также...........................................................................................309

Оглавление    11

Использование Dask для обработки геномных данных на основе массивов NumPy.............................................................................309 Подготовка...................................................................................................309 Как это сделать............................................................................................310 Дополнительно............................................................................................314 Смотрите также...........................................................................................314 Планирование задач с помощью dask.distributed.........................................314 Подготовка...................................................................................................314 Как это сделать............................................................................................316 Дополнительно............................................................................................319 Смотрите также...........................................................................................319

Глава 12. Функциональное программирование в биоинформатике...........................................................................320 Представление чистых функций....................................................................321 Подготовка...................................................................................................321 Как это сделать............................................................................................321 Дополнительно............................................................................................323 Представление о немутабельности................................................................323 Подготовка...................................................................................................324 Как это сделать............................................................................................324 Дополнительно............................................................................................325 Избежание мутабельности для надежности шаблона проектирования......325 Подготовка...................................................................................................326 Как это сделать............................................................................................326 Дополнительно............................................................................................327 Использование ленивого программирования для конвейерной обработки...........................................................................328 Подготовка...................................................................................................328 Как это сделать............................................................................................328 Дополнительно............................................................................................330 Ограничения рекурсии в Python....................................................................330 Подготовка...................................................................................................331 Как это сделать............................................................................................331 Дополнительно............................................................................................333 Демонстрация модуля Python functools.........................................................333 Подготовка...................................................................................................333 Как это сделать............................................................................................333 Дополнительно............................................................................................335 Смотрите также...........................................................................................335

Предметный указатель...................................................................336

Об авторе Тиаго Антао – биоинформатик, который в  настоящее время работает в  области геномики. Бывший ученый, работавший в области информатики, Тиаго перешел в  вычислительную биологию, получив степень магистра биоинформатики на факультете естественных наук Университета Порту, Португалия, и  докторскую степень за работу по теме «Распространение лекарственноустойчивой малярии» в Ливерпульской школе тропической медицины, Великобритания. После защиты докторской диссертации Тиаго работал с наборами данных по человеку в Кембриджском университете, Великобритания, и с данными полногеномного секвенирования комаров в Оксфордском университете, Великобритания, а затем помог создать инфраструктуру биоинформатики в Университете Монтаны, США. В настоящее время он работает дата-инженером в области биотехнологии в Бостоне, штат Массачусетс. Он является одним из соавторов Biopython, основного программного пакета биоинформатики, написанного на Python.

О рецензентах Урминдер Сингх – биоинформатик, ученый в области информатики и разработчик нескольких инструментов биоинформатики с открытым исходным кодом. Его образование включает степени в области физики, информатики и вычислительной биологии, в том числе степень доктора наук по биоинформатике Университета штата Айова, США. Его разнообразные исследовательские интересы включают эволюцию новых генов, точную медицину, социогеномику, машинное обучение в медицине и разработку инструментов и алгоритмов для больших гетерогенных данных. Вы можете посетить его онлайн-страницу по адресу urmi-21.github.io. Тиффани Хо работает сотрудником по биоинформатике в Embark Veterinary. Она имеет степень бакалавра Калифорнийского университета в Дэвисе в области генетики и геномики, а также степень магистра в области селекции растений и генетики Корнельского университета.

От издательства Отзывы и пожелания Мы всегда рады отзывам наших читателей. Расскажите нам, что вы думаете об этой книге – что понравилось или, может быть, не понравилось. Отзывы важны для нас, чтобы выпускать книги, которые будут для вас максимально полезны. Вы можете написать отзыв на нашем сайте www.dmkpress.com, зайдя на страницу книги и оставив комментарий в разделе «Отзывы и рецензии». Также можно послать письмо главному редактору по адресу [email protected]; при этом укажите название книги в теме письма. Если вы являетесь экспертом в какой-либо области и заинтересованы в написании новой книги, заполните форму на нашем сайте по адресу http://dmkpress.com/ authors/publish_book/ или напишите в издательство по адресу [email protected].

Список опечаток Хотя мы приняли все возможные меры для того, чтобы обеспечить высокое качество наших текстов, ошибки все равно случаются. Если вы найдете ошибку в одной из наших книг – возможно, ошибку в основном тексте или программном коде, – мы будем очень благодарны, если вы сообщите нам о ней. Сделав это, вы избавите других читателей от недопонимания и поможете нам улучшить последующие издания этой книги. Если вы найдете какие-либо ошибки в  коде, пожалуйста, сообщите о  них главному редактору по адресу [email protected], и мы исправим это в следующих тиражах.

Нарушение авторских прав Пиратство в  интернете по-прежнему остается насущной проблемой. Издательство «ДМК Пресс» очень серьезно относится к  вопросам защиты авторских прав и лицензирования. Если вы столкнетесь в интернете с незаконной публикацией какой-либо из наших книг, пожалуйста, пришлите нам ссылку на интернет-ресурс, чтобы мы могли применить санкции. Ссылку на подозрительные материалы можно прислать по адресу [email protected]. Мы высоко ценим любую помощь по защите наших авторов, благодаря которой мы можем предоставлять вам качественные материалы.

Предисловие Биоинформатика – это активная область исследований, в которой используется ряд простых и сложных вычислений для извлечения ценной информации из биологических данных, и эта книга покажет вам, как управлять этими задачами с помощью Python. Это обновленное издание книги рецептов по биоинформатике с  Python начинается с  краткого обзора различных инструментов и  библиотек в  программном окружении Python, которые помогут вам преобразовывать, анализировать и визуализировать наборы биологических данных. По мере продвижения по главам вы познакомитесь с важнейшими методами секвенирования следующего поколения, анализа отдельных клеток, геномики, метагеномики, популяционной генетики, филогенетики и  протеомики на основе реальных примеров. Вы узнаете, как работать с лучшими конвейерными системами, такими как серверы Galaxy и Snakemake, освоите различные модули Python для функционального и  асинхронного программирования. В  числе прочего эта книга поможет вам изучить такие темы, как обнаружение SNP с использованием статистических подходов в  высокопроизводительных вычислительных средах, включая Dask и Spark, и применение алгоритмов машинного обучения в биоинформатике. К концу этой книги по биоинформатике Python вы приобретете знания для реализации новейших методов и  сред программирования, что позволит вам работать с данными биоинформатики на любом уровне.

Для кого эта книга Эта книга предназначена для аналитиков в области биоинформатики, специа­ листов по данным, вычислительных биологов, исследователей, разработчиков Python, которые хотят решать проблемы биологии и биоинформатики от среднего до высокого уровня. Приветствуется практическое знание языка программирования Python. Также не помешали бы базовые знания по биологии.

Какие темы охватывает эта книга В главе 1 «Python и окружающее программное обеспечение» рассказывается, как создать современную биоинформационную среду с  помощью Python. В  этой главе обсуждается, как развертывать программное обеспечение с  помощью Docker, взаимодействовать с R и с Jupyter Notebooks. В главе 2 «Знакомство с NumPy, pandas, Arrow и Matplotlib» представлены основные библиотеки Python для обработки данных: NumPy для обработки массивов и матриц; Pandas для работы с табличными данными; Arrow для оптимизации обработки Pandas и Matplotlib для построения диаграмм.

16    Предисловие В главе 3 «Секвенирование следующего поколения» представлены конкретные решения для работы с данными секвенирования на уровне следующего поколения. В этой главе вы узнаете, как работать с большими файлами FASTQ, BAM и VCF. Также обсуждается фильтрация данных. В главе 4 «Продвинутый процессинг данных NGS» рассматриваются расширенные методы программирования для фильтрации данных NGS. Они включают в себя использование менделевских наборов данных, которые затем анализируются стандартной статистикой. Также мы вводим метагеномный анализ. В главе 5 «Работа с геномами» рассматриваются не только высококачественные образцы, такие как геном человека, но также обсуждается, как анализировать другие, низкокачественные образцы, характерные для немодельных видов. Эта глава знакомит вас с обработкой GFF, учит анализировать информацию о геномных особенностях и обсуждает, как использовать генные онтологии. В главе 6 «Популяционная генетика» описывается, как проводить популяционно-генетический анализ эмпирических наборов данных. Например, в Python мы можем выполнять анализ основных компонентов (PCA), компьютерный индекс фиксации (FST) или диаграммы примесных структур. В главе 7 «Филогенетика» используются полные последовательности недавно секвенированных вирусов Эбола для проведения полноценного филогенетического анализа, который включает в себя реконструкцию филогенетического дерева и  сравнение последовательностей. В  этой главе обсуждаются рекурсивные алгоритмы для обработки древовидных структур. Глава 8 «Использование Protein Data Bank» посвящена обработке файлов банка данных о белках (PDB), например выполнению геометрического анализа структуры белков. В этой главе рассматривается визуализация структуры белков. В главе 9 «Конвейеры биоинформатики» представлены два типа конвейеров обработки. Первый тип конвейера – это Galaxy на основе Python, широко используемая система с  веб-интерфейсом, ориентированная в  основном на пользователей, не занимающихся программированием, хотя биоинформатикам, возможно, все же придется взаимодействовать с ней программно. Второй тип основан на snakemake и  nextflow, типе конвейера, предназначенном для программистов. Глава 10 «Машинное обучение в  биоинформатике» знакомит с  машинным обуче­нием, использующим интуитивный подход к  решению задач вычислительной биологии. В этой главе рассматриваются анализ основных компонентов, кластеризация, деревья принятия решений (Decision Trees) и  случайные леса (Random Forests). Глава 11 «Параллельная обработка с  помощью Dask и  Zarr» познакомит вас с методами работы с очень большими наборами данных и алгоритмами, требующими больших вычислительных ресурсов. В  этой главе объясняется, как использовать параллельные вычисления на многих компьютерах (в кластере или облаке). Мы также обсудим эффективное хранение биологических данных. Глава 12 «Функциональное программирование для биоинформатики» познакомит вас с  функциональным программированием. Оно позволяет разрабатывать более сложные программы на Python, которые благодаря «ленивому программированию» и немутабельности легче развертывать в параллельных средах со сложными алгоритмами.

Предисловие    17

Чтобы получить максимальную отдачу от этой книги Программное и аппаратное обеспечение, описанное в книге Python 3.9

Требования к ОС Windows, Mac OS X и Linux (предпочтительно)

Numpy, Pandas, Matplolib Biopython Dask, zarr, scikit-learn

Если вы используете цифровую версию этой книги, мы советуем вам ввести код самостоятельно или получить доступ к коду через репозиторий GitHub (ссылка доступна в следующем разделе). Это поможет вам избежать возможных ошибок, связанных с копированием и вставкой кода.

Загрузите файлы примеров кода Вы можете загрузить примеры файлов кода для этой книги с GitHub по адресу https://github.com/PacktPublishing/Bioinformatics-with-Python-Cookbook-Third-edition. В случае обновления кода он будет обновлен в существующем репозитории GitHub. У нас также есть другие пакеты кода из нашего богатого каталога книг и видео, доступных по адресу https://github.com/PacktPublishing/. Ознакомьтесь с ними!

Загрузите цветные изображения Мы также предоставляем PDF-файл с цветными изображениями скриншотов и диаграмм, использованных в  этой книге. Скачать его можно здесь: https:// packt.link/3KQQO.

Используемые соглашения В этой книге используется ряд текстовых соглашений. Код в  тексте: указывает кодовые слова в тексте, имена таблиц базы данных, имена папок, имена файлов, расширения файлов, пути, фиктивные URLадреса, пользовательский ввод и  дескрипторы Twitter. Вот пример: «call_ genotype имеет вид 56,241×1,1198,2, то есть размерные варианты, образцы, ploidy». Блок кода устанавливается следующим образом: from Bio import SeqIO genome_name = 'PlasmoDB-9.3_Pfalciparum3D7_Genome.fasta' recs = SeqIO.parse(genome_name, 'fasta') for rec in recs: print(rec.description)

18    Предисловие Когда мы хотим привлечь ваше внимание к определенной части блока кода, соответствующие строки или элементы выделяются жирным шрифтом: AgamP4_2L | organism=Anopheles_gambiae_PEST | version=AgamP4 | length=49364325 | SO=chromosome AgamP4_2R | organism=Anopheles_gambiae_PEST | version=AgamP4 | length=61545105 | SO=chromosome

Жирный: отмечает новый термин, важное слово или слова, которые вы видите на экране. Например, слова в меню или диалоговых окнах отображаются в тексте следующим образом. Вот пример: «По столбцу Chunk см. главу 11, но пока вы можете спокойно это игнорировать». Советы или важные примечания Представлены так.

Разделы В этой книге вы найдете несколько часто встречающихся заголовков (Подготовка, Как это сделать..., Как это работает..., Дополнительно и См. также). Чтобы получить четкие инструкции по завершению рецепта, используйте эти разделы следующим образом:

Подготовка В этом разделе рассказывается, чего ожидать от рецепта, и описывается, как установить нужное программное обеспечение или сделать предварительные настройки, необходимые для выполнения рецепта.

Как это сделать… Этот раздел содержит шаги, необходимые для выполнения рецепта.

Как это работает… Этот раздел обычно состоит из подробного объяснения того, что произошло в предыдущем разделе.

Дополнительно… Этот раздел содержит дополнительную информацию о рецепте, чтобы вы могли больше о нем узнать.

Смотрите также Этот раздел содержит полезные ссылки на другую полезную информацию по рецепту.

Предисловие    19

Свяжитесь с нами Отзывы наших читателей всегда приветствуются. Общая поддержка: если у вас есть вопросы по какому-либо аспекту этой книги, укажите название книги в теме сообщения и напишите нам по адресу [email protected]. Ошибки: хотя мы приложили все усилия, чтобы обеспечить точность нашего контента, ошибки могут быть. Если вы нашли ошибку в нашей книге, мы будем признательны, если вы сообщите нам об этом. Зайдите на веб-сайт www. packtpub.com/support/errata, выберите книгу, щелкните ссылку «Форма отправки сведений об ошибках» и введите данные. Пиратство: если вы столкнетесь с незаконными копиями наших произведений в любой форме в интернете, мы будем признательны, если вы сообщите нам адрес или название пиратского веб-сайта. Пожалуйста, свяжитесь с нами по адресу [email protected] со ссылкой на материал. Как стать автором: если есть тема, в которой у вас есть опыт, и вы хотите написать свою книгу или внести свой вклад в  уже написанную, посетите authors.packtpub.com.

Отзывы Пожалуйста, оставьте отзыв. После того как вы прочитали и  воспользовались этой книгой, почему бы не оставить отзыв на сайте, где вы ее приобрели? После этого потенциальные читатели смогут увидеть ваше непредвзятое мнение и использовать его для принятия решения о покупке, мы в Packt сможем понять, что вы думаете о наших продуктах, а наши авторы смогут увидеть ваши отзывы об их книге. Заранее благодарны! Для получения дополнительной информации о Packt посетите сайт packt.com.

Поделитесь своими мыслями После того как вы прочтете книгу «Биоинформатика с Python: книга рецептов», мы будем рады узнать ваше мнение! Щелкните здесь, чтобы сразу перейти на страницу обзора этой книги на Amazon и поделиться своим мнением. Ваш отзыв важен для нас и всего технического сообщества и поможет нам убедиться, что мы предоставляем контент отличного качества.

Глава 1

Python и окружающее программное обеспечение Мы начнем с установки основного программного обеспечения, необходимого для большей части этой книги. Оно включает в себя дистрибутив Python, некоторые фундаментальные библиотеки Python и  внешнее программное обеспечение для биоинформатики. Мы также рассмотрим мир за пределами Python. В  биоинформатике и  больших данных важную роль играет R; поэтому вы узнаете, как взаимодействовать с ним через rpy2, который представляет собой мост от Python к  R. Кроме того, мы изучим преимущества, которые может дать нам фреймворк IPython (через Jupyter Lab) для эффективного взаимодействия с  R. Учитывая, что управление исходным кодом с  помощью Git и  GitHub широко распространено, мы позаботимся о  том, чтобы наш сетап хорошо работал с ними. Эта глава даст вам основу для всех процессов вычислительной биологии, которые мы будем выполнять в оставшейся части книги. Поскольку у  разных пользователей разные требования, мы рассмотрим два разных подхода к  установке программного обеспечения. Один подход заключается в  использовании дистрибутива Anaconda Python (http://docs.continuum. io/anaconda/), а  другой – в  установке программного обеспечения через Docker (это метод виртуализации сервера, основанный на контейнерах, использующих одно и то же ядро операционной системы; пожалуйста, обратитесь к https://www. docker.com/). Это также установит вам Anaconda, но уже внутри контейнера. Если вы используете операционную систему Windows, мы настоятельно рекомендуем вам рассмотреть возможность поменять операционную систему или использовать Docker с помощью некоторых существующих опций в Windows. В macOS вы можете установить большую часть программного обеспечения непосредственно, хотя Docker также доступен. Обучение с использованием локального дистрибутива (Anaconda или чего-то другого) проще, чем с  Docker, но, учитывая, что управление пакетами в Python может быть сложным, образы Docker обеспечивают определенный уровень стабильности. В этой главе мы рассмотрим следующие рецепты:  установка необходимого программного обеспечения с Anaconda;  установка необходимого программного обеспечения с Docker;  взаимодействие с R через rpy2;  выполнение R magic с Jupyter.

Установка необходимого базового программного обеспечения...    21

Установка необходимого базового программного обеспечения с помощью Anaconda Прежде чем мы начнем, нам нужно установить необходимое базовое программное обеспечение. Следующие разделы познакомят вас с программным обеспечением и  шагами, необходимыми для его установки. Каждая глава и раздел могут предъявить дополнительные требования – мы укажем их, когда достигнем соответствующего момента в изложении. Альтернативный способ начать работу  – использовать рецепт Docker, после чего вас обеспечат всем нужным через контейнер Docker. Если вы уже используете другой дистрибутив Python, мы настоятельно рекомендуем вам рассмотреть Anaconda, поскольку он стал стандартом де-факто для науки о данных и биоинформатики. Также именно этот дистрибутив позволит вам установить программное обеспечение от Bioconda (https://bioconda.github.io/).

Подготовка Python можно запускать поверх разных сред. Например, вы можете использовать Python внутри виртуальной машины Java (Java Virtual Machine, JVM) (через Рython или с  .NET через IronPython). Однако здесь нас интересует не только Python, но и вся среда программного обеспечения вокруг него. Поэтому мы будем использовать стандартную реализацию CPython, так как версии JVM и .NET существуют в основном для взаимодействия с оригинальными библиотеками этих платформ. Для создания наших кодов мы будем использовать Python 3.10. Если вы начинали с Python и биоинформатики, подойдет любая операционная система. Но  здесь нас в  основном интересует промежуточное и  продвинутое применение. Таким образом, если вы используете Windows и macOS, имейте в виду, что большую часть сложного ресурсозатратного анализа придется выполнять в  Linux (вероятно, на Linux high-performance computing (высокопроизводительных вычислениях Linux) или кластере HPC). Анализ данных секвенирования следующего поколения (Next-generation sequencing, NGS) и  сложное машинное обучение в основном выполняются на кластерах Linux. Если вы работаете в  Windows, следует подумать об обновлении до Linux для вашей работы в области биоинформатики, поскольку большинство современных программ для биоинформатики не будут работать в Windows. Обратите внимание, что macOS подойдет почти для всех анализов, если только вы не  планируете использовать компьютерный кластер, который, скорее всего, будет основан на Linux. Если вы работаете в  Windows или macOS и  у вас нет простого доступа к  Linux, не  беспокойтесь. Вам на помощь придет современное программное обеспечение виртуализации (такое как VirtualBox и  Docker), которое позволит установить виртуальную Linux на вашу операционную систему. Если вы работаете с Windows и решили, что хотите перейти на оригинальный язык и не использовать Anaconda, будьте осторожны с выбором библиотек; вы, вероятно, будете в большей безопасности, если установите 32-битную версию для всего (включая сам Python).

22    Python и окружающее программное обеспечение Примечание Если вы работаете в  Windows, многие инструменты будут вам недоступны.

Биоинформатика и наука о данных развиваются с головокружительной скоростью; это не реклама, это реальность. При установке программных библиотек выбор версии может причинить проблемы. В зависимости от кода, который у  вас есть, он может не  работать с  некоторыми старыми версиями или, возможно, даже не работать с более новой версией. Будем надеяться, что любой код, который вы используете, покажет правильные соответствия, хотя это и не гарантируется. В этой книге мы установим точные версии всех программных пакетов и позаботимся о том, чтобы код работал с ними. Вполне естественно, что код может нуждаться в согласовании с другими версиями пакета. Программное обеспечение, разработанное для этой книги, доступно по адресу https://github.com/PacktPublishing/Bioinformatics-with-Python-Cookbook-Third-edition. Чтобы получить к  нему доступ, вам нужно будет установить Git. Опыт работы с ним имеет свою пользу, потому что с помощью Git разрабатывается множество программных пакетов для научных вычислений. Прежде чем правильно установить стек Python, вам необходимо установить все внешнее программное обеспечение, которое требуется для Python, с  которым вы будете взаимодействовать. Список будет варьироваться от главы к главе, и все программные пакеты будут объяснены в соответствующих главах. К счастью, со времени выхода предыдущих изданий этой книги большая часть программного обеспечения для биоинформатики стала доступна через проект Bioconda; поэтому инсталляция, как правило, не вызовет проблем. Вам нужно будет установить некоторые компиляторы и библиотеки проектирования, все они бесплатны. В Ubuntu рассмотрите возможность установки пакета build-essential (apt-get install build-essential), а для macOS подумайте о Xcode (https://developer.apple.com/xcode/). На рис. 1.1 вы найдете список наиболее важного программного обеспечения для разработки биоинформатики с помощью Python. Мы будем использовать pandas для обработки большинства табличных данных. Альтернативой может быть использование только стандартного Python. Библиотека pandas стала настолько распространенной в науке о данных, что, вероятно, имеет смысл обрабатывать с  ее помощью все табличные данные (если они помещаются в памяти). Вся наша работа будет производиться внутри проекта Jupyter, а  именно Jupyter Lab. Jupyter де-факто стал стандартом для написания скриптов интер­ активного анализа данных. К  сожалению, формат по умолчанию для Jupyter Notebooks основан на JSON. Этот формат трудно читать, трудно сравнивать, и его необходимо экспортировать, чтобы передать в обычный интерпретатор Python. Чтобы устранить эту проблему, расширим Jupyter с помощью jupytext (https://jupytext.readthedocs.io/), что позволит нам сохранять блокноты Jupyter как обычные программы Python.

Установка необходимого базового программного обеспечения...    23 Имя

Использование

URL-адрес

Цель

Project Jupyter

Все главы

https://jupyter.org/

Интерактивные вычисления

pandas

Все главы

https://pandas.pydata.org/

Обработка данных

NumPy

Все главы

http://www.numpy.org/

Обработка массивов/ матриц

SciPy

Все главы

https://www.scipy.org/

Научные вычисления

Biopython

Все главы

https://biopython.org/

Библиотека биоинформатики

seaborn

Все главы

http://seaborn.pydata.org/

Библиотека статистических диаграмм

R

Биоинформатика и статистика

https://www.r-project.org/

Язык статистических вычислений

rpy2

R-подключение

https://rpy2.readthedocs.io

Интерфейс R

PyVCF

NGS

https://pyvcf.readthedocs.io

Обработка VCF

Pysam

NGS

https://github.com/pysamdevelopers/pysam

Обработка SAM/BAM

HTSeq

NGS/геномы

https://htseq.readthedocs.io

Обработка NGS

DendroPY

Филогенетика

https://dendropy.org/

Филогенетика

PyMol

Протеомика

https://pymol.org

Молекулярная визуализация

scikit-learn

Машинное обучение

http://scikit-learn.org

Библиотека машинного обучения

Cython

Большие данные

http://cython.org/

Высокая производительность

Numba

Большие данные

https://numba.pydata.org/

Высокая производительность

Dask

Большие данные

http://dask.pydata.org

Параллельная обработка

Рис. 1.1. Таблица, показывающая различные программные пакеты, полезные в биоинформатике

Как это сделать... Для начала ознакомьтесь со следующими шагами: 1. Начните с загрузки дистрибутива Anaconda с https://www.anaconda. com/ products/individual. Мы будем использовать версию 21.05, хотя вам, вероятно, подойдет и более поздняя, если она вам доступна. Вы можете принять все параметры инсталляции по умолчанию, но можете убедиться,

24    Python и окружающее программное обеспечение что двоичные файлы conda находятся в вашем пути (не забудьте открыть новое окно, чтобы можно было обновить путь). Если у  вас есть другой дистрибутив Python, будьте осторожны с PYTHONPATH и существующими библиотеками Python. Вероятно, лучше отключить ваш PYTHONPATH. Насколько это возможно, удалите все другие версии Python и его библиотеки, установленные ранее. 2. Займемся библиотеками. Теперь мы создадим новую среду conda под названием bioinformatics_base с biopython=1.70, как показано в следующей команде: conda create -n bioinformatics_base python=3.10

3. Давайте активируем среду следующим образом: conda activate bioinformatics_base

4. Давайте добавим каналы bioconda и conda-forge в наш список источников: conda config --add channels bioconda conda config --add channels conda-forge

5. Также установим основные пакеты: conda install \ biopython==1.79 \ jupyterlab==3.2.1 \ jupytext==1.13 \ matplotlib==3.4.3 \ numpy==1.21.3 \ pandas==1.3.4 \ scipy==1.7.1

6. Теперь сохраним нашу среду, чтобы мы могли использовать ее позже для создания новых сред на других машинах или если вам нужно будеть очис­тить базовую среду. conda list –explicit > bioinformatics_base.txt

7.

Можно даже установить R из conda: conda install rpy2 r-essentials r-gridextra

Обратите внимание, что r-essentials устанавливает множество пакетов  R, включая ggplot2, который мы будем использовать позже. Кроме того, мы устанавливаем r-gridextra, так как будем использовать его в Notebook.

Установка необходимого базового программного обеспечения...    25

Дополнительно... Если вы предпочитаете не использовать Anaconda, вы сможете установить разные библиотеки Python через pip, используя любой выбранный вами дистрибутив. Вам, вероятно, понадобится довольно много компиляторов и  инструментов сборки1 – не только компиляторы C, но также C++ и Fortran. Мы не будем использовать среду, созданную на предыдущих шагах. Вместо этого мы будем использовать ее в  качестве базы для клонирования рабочих сред. Это связано с тем, что управление средой с помощью Python – даже с помощью системы пакетов conda – может причинить головную боль. Таким образом, мы создадим чистую среду, которую мы никогда не испортим и от которой мы сможем избавиться, если она станет неуправляемой. Например, представьте, что вы хотите создать среду для машинного обучения с помощью scikit-learn. Можно сделать следующее. 1. Создайте клон исходной среды: conda create -n scikit-learn --clone bioinformatics_base

2. Добавьте scikit-learn: conda activate scikit-learn conda install scikit-learn

Находясь внутри JupyterLab, мы должны открывать наши файлы jupytext с  помощью блокнота Notebook, а  не текстового редактора. Поскольку файлы jupytext имеют то же расширение, что и файлы Python – это особенность, а не ошибка, – по умолчанию JupyterLab будет использовать обычный текстовый редактор. Когда мы открываем файл jupytext, нам нужно переписать установки по умолчанию. Открыв его, щелкните правой кнопкой мыши и выберите Notebook, как показано на рис. 1.2. Наши файлы jupytext не будут сохранять графические данные, и это допустимо для данной книги. Если вы хотите иметь версию с изображениями, это возможно с помощью парных блокнотов. Для получения дополнительной информации посетите страницу Jupytext (https://github.com/mwouts/jupytext). Предупреждение Поскольку наш код предназначен для запуска внутри Jupyter, много раз в этой книге я не буду использовать print для вывода содержимого, поскольку последняя строка ячейки будет отображаться автоматически. Если вы не используете блокноты, сделайте print.

Сборка включает в себя компиляцию, компоновку и упаковку кода в пригодную для использования или исполняемую форму. – Прим. ред.

1

26    Python и окружающее программное обеспечение

Рис. 1.2. Открытие файла jupytext в Notebook

Установка необходимого программного обеспечения с помощью Docker Docker – наиболее широко используемый фреймворк для реализации виртуа­ лизации на уровне операционной системы. Эта технология позволяет вам иметь независимый контейнер: уровень, который легче, чем виртуальная машина, но все же позволяет компартментализировать программное обеспечение. Он практически изолирует все процессы, создавая ощущение, что каждый контейнер является виртуальной машиной. Docker хорошо работает на обоих краях разработки: это удобный способ настроить содержание данной книги в учебных целях, и он может стать вашей предпочтительной платформой для развертывания приложений в  сложных средах. Этот рецепт является альтернативой предыдущему рецепту. Однако для сред проектирования, рассчитанных на долгую историю, лучше всего подойдет что-то вроде предыдущего рецепта, хотя это может повлечь за собой более трудоемкую первоначальную настройку.

Установка необходимого базового программного обеспечения...    27

Подготовка Если вы используете Linux, первое, что вам нужно сделать, – это установить Docker. Самое безопасное решение – получить последнюю версию с https://www.docker.com/. Хотя в вашем дистрибутиве Linux может быть пакет Docker, он может быть слишком старым и содержать ошибки. Если у вас Windows или macOS, не отчаивайтесь; давайте посмотрим на сайт Docker. Существуют различные варианты, чтобы спасти вас, но четкой формулы нет, поскольку Docker довольно быстро продвигается на этих платформах. Для  запуска нашей 64-битной виртуальной машины необходим достаточно новый компьютер. Если у  вас возникли проблемы, перезагрузите компьютер и убедитесь, что BIOS, VT-X или AMD-V доступны. Вам понадобится по крайней мере 6 ГБ оперативной памяти, а лучше больше. Примечание Для этого потребуется скачать очень большой объем данных из интернета, поэтому убедитесь, что у вас достаточная скорость соединения с интернетом. Также будьте готовы долго ждать.

Как это сделать... Чтобы начать, выполните следующие действия. 1. Используйте такую команду в вашей оболочке Docker: docker build -t bio https://raw.githubusercontent.com/PacktPublishing/ Bioinformatics-with-Python-Cookbook-third-edition/main/docker/main/ Dockerfile

В Linux вам потребуются либо привилегии root, либо добавление в группу Docker Unix. 2. Теперь вы готовы запустить контейнер следующим образом: docker run -ti -p 9875:9875 -v YOUR_DIRECTORY:/data bio

3. Замените директорию YOUR_DIRECTORY на каталог в вашей операционной системе. Он будет использоваться вашей операционной системой хоста совместно с  контейнером Docker. Каталог YOUR_DIRECTORY будет виден в контейнере в /data, и наоборот. Установка -p 9875:9875 выставит TCP-порт контейнера 9875 на порт хост-компьютера, 9875. Убедитесь, что ваш каталог действительно виден в среде оболочки Docker, особенно в  Windows (и, возможно, в  macOS). Если нет, ознакомьтесь с официальной документацией Docker о том, как открывать каталоги. 4. Теперь вы готовы к  использованию системы. Укажите в  браузере http://localhost:9875, и вы должны получить среду Jupyter. Если это не работает в Windows, ознакомьтесь с официальной документацией Docker (https://docs.docker.com/), чтобы узнать, как открывать порты.

28    Python и окружающее программное обеспечение

Смотрите также Также стоит знать следующее:  Docker является наиболее широко используемым программным обеспечением для контейнеризации1, и в последнее время его использование значительно возросло. Подробнее об этом можно прочитать на https:// www.docker.com/;  альтернативой Docker с точки зрения безопасности является rkt, который можно найти по адресу: https://coreos.com/rkt/;  если вы не можете использовать Docker, например если у вас нет необходимых допусков, как это происходит на большинстве компьютерных кластеров, тогда давайте рассмотрим Singularity по адресу https://www. sylabs.io/singularity/.

Взаимодействие с R через rpy2 Если вам нужна какая-то определенная функциональность, но вы не можете найти ее в библиотеке Python, вам в первую очередь нужно проверить, реализована ли она в  R. Для статистических методов R  по-прежнему является наиболее полной структурой; кроме того, некоторые функции биоинформатики доступны только в R и, вероятно, предлагаются в виде пакета, принадлежащего проекту Bioconductor. Пакет rpy2 предоставляет декларативный интерфейс от Python к R. Как вы увидите, вы сможете написать очень элегантный код Python для выполнения процесса взаимодействия. Чтобы показать интерфейс (и  опробовать одну из самых распространенных структур данных R, DataFrame, и одну из самых популярных библиотек R, ggplot2), мы загрузим его метаданные из проекта «Human 1000 Genomes Project» (http://www.1000genomes.org/). Это не книга по R, но мы хотим предоставить интересные и функциональные примеры.

Подготовка Вам нужно будет получить файл метаданных из указателя последовательности «Проекта 1000 геномов». Пожалуйста, проверьте https://github.com/PacktPublishing/ Bioinformatics-with-Python-Cookbook-Third-edition/blob/main/Datasets.py и  загрузите файл sequence.index. Если вы используете Jupyter Notebook, откройте файл Chapter01/Interfacing_R.py и просто выполните команду wget сверху. Этот файл содержит информацию обо всех файлах FASTQ в проекте (мы будем использовать данные из проекта «1000 геномов человека» в  следующих главах). Он  включает в  себя файл FASTQ, идентификатор образца, исходную популяцию и  важную статистическую информацию для каждой дорожки (lane)2, такую как количество ридов (reads, чтений, прочтений) и  количество прочитанных оснований ДНК. Метод виртуализации, при котором ядро операционной системы поддерживает несколько изолированных экземпляров пространства пользователя вместо одного. – Прим. ред. 2 Lane – это физическая «дорожка» на проточной кювете, которую вводят в машину для секвенирования. Дорожки могут использоваться для разделения различных экспериментов. Вы  можете секвенировать свой образец на нескольких дорожках, но в  итоге все равно получите один файл. Или секвенировать несколько образцов на одной дорожке и затем разделить их. – Прим. ред. 1

Взаимодействие с R через rpy2    29 Чтобы настроить Anaconda, вы можете запустить следующее: conda create -n bioinformatics_r --clone bioinformatics_base conda activate bioinformatics_r conda install r-ggplot2=3.3.5 r-lazyeval r-gridextra rpy2

С помощью Docker вы можете запустить: docker run -ti -p 9875:9875 -v YOUR_DIRECTORY:/data tiagoantao/bioinformatics_r

Теперь мы можем начать.

Как это сделать... Чтобы начать, выполните следующие действия. 1. Давайте начнем с импорта: import os from IPython.display import Image import rpy2.robjects as robjects import rpy2.robjects.lib.ggplot2 as ggplot2 from rpy2.robjects.functions import SignatureTranslatedFunction import pandas as pd import rpy2.robjects as ro from rpy2.robjects import pandas2ri from rpy2.robjects import local_converter

Мы будем использовать pandas на стороне Python. R  DataFrames очень хорошо сопоставляется1 с pandas. 2. Прочитаем данные из нашего файла, используя функцию R read.delim: read_delim = robjects.r('read.delim') seq_data = read_delim ('sequence.index', header=True, stringsAsFactors=False) #In R: # seq.data 0: process_node = queue.popleft()

222    Филогенетика if process_node.taxon is not None: print('%s (%d)' % (process_node.taxon, process_node.level())) else: for child in process_node.child_nodes(): queue.append(child) print_breadth(ebolа_raxml)

Прежде чем мы объясним этот алгоритм, давайте посмотрим, насколько результат этого прогона будет отличаться от предыдущего. Для начала разберем следующую схему. Если вы напечатаете узлы в порядке поиска в глубину, то получите Y, A, X, B и C. Но если вы выполните обход в порядке поиска в ширину, вы получите X, B, C, Y и A. Порядок обхода дерева влияет на то, как посещаются узлы; чаще всего это важно. Что касается предыдущего кода, здесь мы будем использовать совершенно другой подход, так как будем выполнять итеративный алгоритм. Мы  будем использовать очередь «первым вошел  – первым вышел» (FIFO), чтобы упорядочить наши узлы. Обратите внимание, что двухстороннюю очередь Python можно использовать так же эффективно, как FIFO, а  еще по принципу «последним вошел – первым вышел» (LIFO). Это потому, что Python реализует эффективную структуру данных, когда вы работаете в обеих крайностях. Алгоритм начинается с помещения корневого узла в очередь. Пока очередь не  пуста, мы вынесем узел вперед. Если это внутренний узел, мы поместим все его дочерние узлы в очередь. Мы будем повторять предыдущий шаг, пока очередь не  станет пустой. Я призываю вас взять ручку и бумагу и посмотреть, как это работает, выполнив пример, показанный на следующей схеме. Код небольшой, но не тривиальный.

Рис. 7.3. Обход дерева; первое число указывает порядок, в котором этот узел посещается, при обходе в глубину, а второе предполагает обход в ширину

7. Вернемся к  реальному набору данных. Поскольку у  нас слишком много данных для визуализации, мы создадим урезанную версию, в  кото-

Рекурсивная игра с деревьями    223 рой удалим поддеревья с одним биологическим видом (в случае EBOV – из одной и той же вспышки заболевания). Мы также упорядочим дерево, то есть отсортируем дочерние узлы в порядке количества дочерних элементов: from copy import deepcopy simple_ebola = deepcopy(ebola_raxml) def simplify_tree(node): prefs = set() for leaf in node.leaf_nodes(): my_toks = leaf.taxon.label.split(' ') if my_toks[0] == 'EBOV': prefs.add('EBOV' + my_toks[1]) else: prefs.add(my_toks[0]) if len(prefs) == 1: print(prefs, len(node.leaf_nodes())) node.taxon = dendropy.Taxon(label=list(prefs)[0]) node.set_child_nodes([]) else: for child in node.child_nodes(): simplify_tree(child) simplify_tree(simple_ebola.seed_node) simple_ebola.ladderize() simple_ebola.write_to_path('ebola_simple.nex', 'nexus')

Мы сделаем глубокую копию структуры дерева. Поскольку наша функция и  ладдеризация1 являются деструктивными (они изменят дерево), мы захотим сохранить исходное дерево. DendroPy может подсчитать все конечные узлы (на данном этапе хорошим упражнением было бы написать функцию для выполнения этого). С  помощью этой функции мы получим все листья для определенного узла. Если они имеют тот же вид и год вспышки, что и в случае EBOV, мы удаляем все дочерние узлы, листья и внутренние узлы поддерева. Если они не относятся к одному и тому же виду, мы возвращаемся вниз до тех пор, пока этого не произойдет. В худшем случае, когда вы уже находитесь на листовом узле, алгоритм тривиально приводит к видам текущего узла. Построение лестницы. – Прим. ред.

1

224    Филогенетика

Дополнительно... Существует огромное количество литературы по информатике на тему деревьев и структур данных; если вы хотите узнать больше, в Википедии есть отличная вводная информация по адресу http://en.wikipedia. org/wiki/Tree_%28data_ structure%29. Обратите внимание, что использование функций lambda и map не поощряется в качестве диалекта Python; вы можете прочитать некоторые (старые) мнения по этому вопросу в статье Гвидо ван Россума на http://www.artima.com/weblogs/ viewpost.jsp?thread=98196. Я представил его здесь, потому что это очень распространенный диалект функционального и  рекурсивного программирования. Более распространенный диалект будет основан на представлении списков. В любом случае, функциональный диалект, основанный на использовании операций map и  reduce, является концептуальной основой для фреймворков MapReduce, и  вы можете использовать такие фреймворки, как Hadoop, Disco или Spark, для выполнения высокопроизводительных биоинформационных вычислений.

Визуализация филогенетических данных В этом рецепте мы обсудим, как визуализировать филогенетические деревья. DendroPy имеет только простые механизмы визуализации, основанные на рисовании текстовых ASCII-деревьев, зато Biopython имеет довольно богатую инфраструктуру, которую мы и будем использовать здесь.

Подготовка Это потребует от вас выполнения всех предыдущих рецептов. Помните, что у нас есть файлы для всего рода вируса Эбола, включая дерево RAxML? Кроме того, в предыдущем рецепте будет получена упрощенная родовая версия. Как обычно, вы можете найти этот контент в файле блокнота Chapter07/Visualization.py.

Как это сделать... Рассмотрим следующие шаги. 1. Давайте загрузим все филогенетические данные: from copy import deepcopy from Bio import Phylo ebola_tree = Phylo.read('my_ebola.nex', 'nexus') ebola_tree.name = 'Ebolavirus tree' ebola_simple_tree = Phylo.read('ebola_simple.nex', 'nexus') ebola_simple_tree.name = 'Ebolavirus simplified tree'

Для всех деревьев, которые мы читаем, мы изменим имя дерева, как будет напечатано позже.

Визуализация филогенетических данных    225 2. Теперь мы можем нарисовать ASCII-представление деревьев: Phylo.draw_ascii(ebola_simple_tree) Phylo.draw_ascii(ebola_tree)

Представление ASCII упрощенного дерева родов показано на следующей диаграмме. Здесь мы не будем печатать полную версию, потому что она займет несколько страниц. Но если вы запустите предыдущий код, то увидите, что он вполне читабелен.

Рис. 7.4. ASCII-представление упрощенного набора данных о вирусе Эбола

3. Bio.Phylo позволяет графически отображать деревья, используя matplotlib в качестве бэкенда: import matplotlib.pyplot as plt fig = plt.figure(figsize=(16, 22)) ax = fig.add_subplot(111) Phylo.draw(ebola_simple_tree, branch_labels=lambda c: c.branch_length if c.branch_length > 0.02 else None, axes=ax)

В этом случае мы напечатаем длины ветвей на ребрах, но удалим все длины меньше 0,02, чтобы избежать беспорядка. Результат этого действия показан на следующей диаграмме.

226    Филогенетика Упрощенное дерево вируса Эбола

длина ветви

Рис. 7.5. Версия упрощенного набора данных на основе matplotlib с добавленными длинами ветвей

4. Теперь мы построим полный набор данных, но раскрасим каждую часть дерева по-разному. Если в поддереве есть только один вид вируса, оно получит свой собственный цвет. EBOV будет иметь два цвета, то есть один для вспышки 2014 года и один для остальных, как показано ниже: fig = plt.figure(figsize=(16, 22)) ax = fig.add_subplot(111)

Визуализация филогенетических данных    227 from collections import OrderedDict my_colors = OrderedDict({ 'EBOV_2014': 'red', 'EBOV': 'magenta', 'BDBV': 'cyan', 'SUDV': 'blue', 'RESTV' : 'green', 'TAFV' : 'yellow' }) def get_color(name): for pref, color in my_colors.items(): if name.find(pref) > -1: return color return 'grey' def color_tree(node, fun_color=get_color): if node.is_terminal(): node.color = fun_color(node.name) else: my_children = set() for child in node.clades: color_tree(child, fun_color) my_children.add(child.color.to_hex()) if len(my_children) == 1: node.color = child.color else: node.color = 'grey' ebola_color_tree = deepcopy(ebola_tree) color_tree(ebola_color_tree.root) Phylo.draw(ebola_color_tree, axes=ax, label_func=lambda x: x.name.split(' ') [0][1:] if x.name is not None else None)

Это алгоритм обхода дерева, мало чем отличающийся от представленных в предыдущем рецепте. Как рекурсивный алгоритм, он работает следующим образом. Если узел представляет собой лист, он получит цвет в зависимости от

228    Филогенетика его биологического вида (или года вспышки EBOV). Если это внутренний узел и все узлы-потомки ниже него относятся к одному и тому же виду, он получит цвет этого вида; если после этого есть несколько видов, он будет окрашен в серый цвет. На самом деле цветовая функция может быть изменена и будет изменена позже. Будут использоваться только цвета краев (лейблы будут напечатаны черным цветом). Обратите внимание, что ладдеризация (сделанная в  предыдущем рецепте с помощью DendroPy) очень помогает с точки зрения четкого внешнего вида. Мы также копируем дерево рода с  помощью deep copy, чтобы раскрасить копию; помните из предыдущего рецепта, что некоторые функции обхода дерева могут менять состояние, и в этом случае мы хотим сохранить версию без какой-либо окраски. Обратите внимание на использование лямбда-функции для очистки имени, которое было изменено trimAl, как показано на следующей диаграмме: Дерево вируса Эбола

длина ветви

Рис. 7.6. Раскрашенное филогенетическое дерево с лестничной структурой и полным набором данных о вирусе Эбола

Визуализация филогенетических данных    229

Дополнительно... Визуализация дерева и графа – сложная тема; возможно, здесь визуализация дерева строгая, но не очень красивая. Одной из альтернатив DendroPy, которая имеет больше возможностей визуализации, является ETE (http://etetoolkit.org/). Общие альтернативы для построения деревьев и графов включают Cytoscape (https://cytoscape.org/) и  Gephi (http://gephi.github.io/). Если вы хотите узнать больше об алгоритмах рендеринга деревьев и графов, посетите страницу Википедии по адресу http://en.wikipedia.org/wiki/Graph_drawing, чтобы ознакомиться с введением в эту увлекательную тему. Однако будьте осторожны, чтобы не подменить содержание стилем. Например, в предыдущем издании этой книги была красивая визуализация филогенетического дерева с использованием библиотеки для визуализации графов. Хотя это явно было самое красивое изображение в той главе, оно вводило в заблуждение с точки зрения длины ветвей.

Глава 8

Использование Protein Data Bank Протеомика – это изучение белков, включая их функции и структуру. Одной из основных целей этой области является описание трехмерной структуры белков. Одним из наиболее широко известных вычислительных ресурсов в  области протеомики является Банк данных о  белках (Protein Data Bank, PDB), репозиторий со структурными данными больших биомолекул. Конечно, многие базы данных вместо этого сосредоточены на первичной структуре белка; они чем-то похожи на геномные базы данных, которые мы видели в главе 2 «Знакомство с NumPy, pandas, Arrow и Matplotlib». В этой главе мы в основном сосредоточимся на обработке данных из PDB. Мы рассмотрим, как анализировать файлы PDB, делать некоторые геометрические вычисления и  визуализировать молекулы. Мы  будем использовать старый формат файла PDB, потому что концептуально он позволяет делать большинство необходимых операций в  стабильной среде. Более новый формат mmCIF, который должен заменить формат PDB, также будет представлен в рецепте «Синтаксический анализ файлов mmCIF с помощью Biopython». Мы будем использовать Biopython и представим PyMOL для визуализации. Мы не будем обсуждать здесь молекулярный докинг1, потому что эта тема, скорее всего, больше подходит для книги по хемоинформатике. На протяжении всей этой главы мы будем использовать классический пример белка: опухолевый белок p53, белок, участвующий в регуляции клеточного цикла (например, апоптоза). Этот белок тесно связан с раком. В Сети есть много информации об этом белке. Давайте начнем с того, с  чем вы уже должны быть более знакомы: доступ к базам данных, особенно для первичной структуры белка (например, последовательности аминокислот). В этой главе мы рассмотрим следующие рецепты:  поиск белка в множественных базах данных;  представление Bio.PDB;  извлечение дополнительной информации из файла PDB;  вычисление молекулярных расстояний в файле PDB;  выполнение геометрических операций; Метод молекулярного моделирования, предсказывающий наиболее устойчивую ориентацию и конформацию лиганда и рецептора. – Прим. ред.

1

Поиск белка во множественных базах данных    231  анимация с помощью PyMOL;  анализ файлов mmCIF с помощью Biopython.

Поиск белка во множественных базах данных Прежде чем приступить к структурной биологии, рассмотрим, как мы можем получить доступ к существующим протеомным базам данных, таким как UniProt. Мы запросим у UniProt интересующий нас ген TP53 и возьмем его оттуда.

Подготовка Для доступа к данным будем использовать Biopython и REST API (мы использовали аналогичный подход в главе 5 «Работа с геномами») с библиотекой запросов requests для доступа к веб-API. API requests – это простая в использовании оболочка для веб-запросов, которую можно установить с помощью стандартных механизмов Python (например, pip и conda). Вы можете найти этот контент в файле блокнота Chapter08/Intro.py.

Как это сделать... Рассмотрим следующие шаги. 1. Во-первых, давайте определим функцию для выполнения запросов REST к UniProt следующим образом: import requests server = 'http://www.uniprot.org/uniprot' def do_request(server, ID='', **kwargs): params = '' req = requests.get('%s/%s%s' % (server, ID, params), params=kwargs) if not req.ok: req.raise_for_status() return req

2. Теперь мы можем запросить все рассмотренные гены p53: req = do_request(server, query='gene:p53 AND reviewed:yes', format='tab', columns='id,entry name,length,organism,organism-id,database(PDB),databas e(HGNC)', limit='50')

Мы запросим ген p53 и попросим просмотреть все вводы, которые проверены (например, отобраны вручную). Вывод будет в табличном формате. Мы запросим максимум 50 результатов с указанием нужных столбцов. Мы могли бы ограничить вывод только данными о геноме человека, но для этого примера давайте включим все доступные виды. 3. Давайте проверим результаты следующим образом:

232    Использование Protein Data Bank import pandas as pd import io uniprot_list = pd.read_table(io.StringIO(req.text)) uniprot_list.rename(columns={'Organism ID': 'ID'}, inplace=True) print(uniprot_list)

Мы используем pandas для простой обработки списка с разделителями табуляции и для красивой печати. Сокращенный вывод Notebook выглядит следующим образом: Имя ввода

Длина

0 Q42578

PER53_APATH

335

1 P79820

P53_ORYLA

352

2 Q7Z419

P144B_HUMAN

303

3 QTUB2 4 A7TJT7

P53_PIG SUB22_VANPO

386 442

5 P56424

P53_MACMU

393

6 Q9W679 P53_TETMU

367

7 Q9W678 P53_BARBU

369

8 Q29537

P53_CANLF

381

9 O09158

P53_CRIGR

393

10 Q8SPZ3

P53_DELLE

387

11 P79892

P53_HORSE

280

12 Q9TTA1

P53_TUPBE

393

13 P61260

P53_MACFU

393

14 P04637

P53_HUMAN

393

Ввод

Организм Arabidopsis thaliana (Резуховидка Таля) Orizias latipes (Оризия японская)

ID 3702

ПереПерекресткрестная ная ссылка ссылка (PDB) (HGNC) 1PA2;1QO4; NaN

8090

NaN

21578;

Homo sapiens (Человек)

9606

NaN

NaN

Sus scrofa (Свинья) Vanderwaltozyma polyspora (цепь ATCC22028/…) (Вандервальтозима полиспора) Macaca mulatta (Макака-резус) Tetraodon miurus (Миурус красноватый) Barbus barbus (Барбус обыкновенный) Canis lupus familiaris (Собака) Cricetulus griseus (Хомячок китайский) Delphinapterus leucas (Белуга) Equus caballus (Лошадь)

9823 436907

NaN NaN

NaN NaN

9544

NaN

NaN

94908

NaN

NaN

40830

NaN

NaN

9615

NaN

NaN

10029

NaN

NaN

9749

NaN

NaN

9796

NaN

NaN

Tupaia belangeri (Тупайя 37347 NaN малайская) Macaca fuscata fuscata 9543 NaN (Макака японская) Homo sapiens (Человек) 9606 1A1U;1AIE;1 C26;1DT7;1 GZH;1H26;1 HS5;1JSP;1K ZY;1…

Рис. 8.1. Сокращенный список видов, у которых можно найти белок TP53

NaN NaN 11998;

Поиск белка во множественных базах данных    233 4. Теперь мы можем получить идентификатор p53 человека и использовать Biopython для извлечения и анализа записи SwissProt. from Bio import ExPASy, SwissProt

p53_human = uniprot_list[ (uniprot_list.ID == 9606) & (uniprot_list['Entry name'].str.contains('P53'))] ['Entry'].iloc[0] handle = ExPASy.get_sprot_raw(p53_human) sp_rec = SwissProt.read(handle)

Затем мы используем модуль SwissProt от Biopython для парсинга записи. 9606 – таксономический код человека NCBI. Как обычно, если есть ошибка при работе в сети, это может быть проб­ лема со связью с сетью или сервером. В этом случае просто повторите попытку позже. 5. Давайте посмотрим на запись p53 следующим образом: print(sp_rec.entry_name, sp_rec.sequence_length, sp_rec. gene_name) print(sp_rec.description) print(sp_rec.organism, sp_rec.seqinfo) print(sp_rec.sequence) print(sp_rec.comments) print(sp_rec.keywords)

Результат выглядит вот так: P53_HUMAN 393 Name=TP53; Synonyms=P53; RecName: Full=Cellular tumor antigen p53; AltName: Full=Antigen NY-CO-13; AltName: Full=Phosphoprotein p53; AltName: Full=Tumor suppressor p53; Homo sapiens (Human). (393, 43653, 'AD5C149FD8106131') MEEPQSDPSVEPPLSQETFSDLWKLLPENNVLSPLPSQAMDDLMLSPDDIEQWFTED PGPDEAPRMPEAAPPVAPAPAAPTPAAPAPAPSWPLSSSVPSQKTYQGSYGFRLGF LHSGTAKSVTCTYSPALNKMFCQLAKTCPVQLWVDSTPPPGTRVRAMAIYKQSQHM TEVVRRCPHHERCSDSDGLAPPQHLIRVEGNLRVEYLDDRNTFRHSVVVPYEPPEVG SDCTTIHYNYMCNSSCMGGMNRRPILTIITLEDSSGNLLGRNSFEVRVCACPGRDRR TEEENLRKKGEPHHELPPGSTKRALPNNTSSSPQPKKKPLDGEYFTLQIRGRERFEM FRELNEALELKDAQAGKEPGGSRAHSSHLKSKKGQSTSRHKKLMFKTEGPDSD

6. Более глубокий взгляд на предыдущую запись показывает много действительно интересной информации, главным образом об особенностях, онтологии генов (Gene Ontology, GO) и  перекрестных ссылках cross_references базы данных:

234    Использование Protein Data Bank from collections import defaultdict done_features = set() print(len(sp_rec.features)) for feature in sp_rec.features: if feature[0] in done_features: continue else: done_features.add(feature[0]) print(feature) print(len(sp_rec.cross_references)) per_source = defaultdict(list) for xref in sp_rec.cross_references: source = xref[0] per_source[source].append(xref[1:]) print(per_source.keys()) done_GOs = set() print(len(per_source['GO'])) for annot in per_source['GO']: if annot[1][0] in done_GOs: continue else: done_GOs.add(annot[1][0]) print(annot)

Обратите внимание, что мы не печатаем здесь всю информацию, а только ее сводку. Мы печатаем ряд особенностей последовательности с одним примером для каждого типа, количество ссылок на внешние базы данных, а также базы данных, на которые ссылаются, и ряд вводов GO вместе с тремя примерами. В настоящее время существует 1509 особенностей, 923 внешние ссылки и 173 перехода GO только для этого белка. Вот сильно сокращенная версия вывода: Total features: 1509 type: CHAIN location: [0:393] id: PRO_0000185703 qualifiers: Key: note, Value: Cellular tumor antigen p53

Представляем Bio.PDB    235 type: DNA_BIND location: [101:292] qualifiers: type: REGION location: [0:320] qualifiers: Key: evidence, Value: ECO:0000269|PubMed:25732823 Key: note, Value: Interaction with CCAR2 [...] Cross references: 923 dict_keys(['EMBL', 'CCDS', 'PIR', 'RefSeq', 'PDB', 'PDBsum', 'BMRB', 'SMR', 'BioGRID', 'ComplexPortal', 'CORUM', 'DIP', 'ELM', 'IntAct', 'MINT', 'STRING', 'BindingDB', 'ChEMBL', 'DrugBank', 'MoonDB', 'TCDB', 'GlyGen', 'iPTMnet', 'MetOSite', 'PhosphoSitePlus', 'BioMuta', 'DMDM', 'SWISS-2DPAGE', 'CPTAC', 'EPD', 'jPOST', 'MassIVE', 'MaxQB', 'PaxDb', 'PeptideAtlas', 'PRIDE', 'ProteomicsDB', 'ABCD', 'Antibodypedia', 'CPTC', 'DNASU', 'Ensembl', 'GeneID', 'KEGG', 'MANE-Select', 'UCSC', 'CTD', 'DisGeNET', 'GeneCards', 'GeneReviews', 'HGNC', 'HPA', 'MalaCards', 'MIM', 'neXtProt', 'OpenTargets', 'Orphanet', 'PharmGKB', 'VEuPathDB', 'eggNOG', 'GeneTree', 'InParanoid', 'OMA', 'OrthoDB', 'PhylomeDB', 'TreeFam', 'PathwayCommons', 'Reactome', 'SABIO-RK', 'SignaLink', 'SIGNOR', 'BioGRID ORCS', 'ChiTaRS', 'EvolutionaryTrace', 'GeneWiki', 'GenomeRNAi', 'Pharos', 'PRO', 'Proteomes', 'RNAct', 'Bgee', 'ExpressionAtlas', 'Genevisible', 'GO', 'CDD', 'DisProt', 'Gene3D', 'IDEAL', 'InterPro', 'PANTHER','Pfam', 'PRINTS', 'SUPFAM', 'PROSITE']) Annotation SOURCES: 173 ('GO:0005813', 'C:centrosome', 'IDA:UniProtKB') ('GO:0036310', 'F:ATP-dependent DNA/DNA annealing activity', 'IDA:UniProtKB') ('GO:0006914', 'P:autophagy', 'IMP:CAFA')

Дополнительно Существует еще много баз данных с  информацией о  белках – некоторые из них упомянуты в  предыдущей записи. Вы  можете изучить этот результат, чтобы попытаться найти данные в  другом месте. Для получения подробной информации об интерфейсе REST UniProt см. http://www.uniprot.org/help/ programmatic_access.

Представляем Bio.PDB Здесь мы представим модуль PDB от Biopython для работы с  PDB. Мы  будем использовать три модели, представляющие часть белка р53. Подробнее об этих файлах и  p53 можно прочитать на http://www.rcsb.org/pdb/101/motm. do?momID=31.

236    Использование Protein Data Bank

Подготовка Вы уже должны быть знакомы со структурой данных объектов PDB: модель– цепь–остаток–атом. Хорошее объяснение часто задаваемых вопросов по структурной биоинформатике Biopython можно найти на http://biopython. org/ wiki/The_Biopython_Structural_Bioinformatics_FAQ. Вы можете найти этот контент в файле блокнота Chapter08/PDB.py. Из трех моделей, которые мы загрузим, модель 1TUP будет использоваться в остальных рецептах. Потратьте некоторое время на ее изучение, так как это поможет вам в дальнейшем.

Как это сделать... Рассмотрим следующие шаги. 1. Во-первых, давайте извлечем интересующие нас модели следующим образом: from Bio import PDB repository = PDB.PDBList() repository.retrieve_pdb_file('1TUP', pdir='.', file_format='pdb') repository.retrieve_pdb_file('1OLG', pdir='.', file_format='pdb') repository.retrieve_pdb_file('1YCQ', pdir='.', file_format='pdb')

Обратите внимание, что Bio.PDB позаботится о загрузке файлов за вас. Более того, эти загрузки будут происходить только в том случае, если локальной копии еще нет. 2. Давайте сделаем парсинг наших записей, как показано в следующем коде: parser = PDB.PDBParser() p53_1tup = parser.get_structure('P 53 - DNA Binding', 'pdb1tup.ent') p53_1olg = parser.get_structure('P 53 - Tetramerization', 'pdb1olg.ent') p53_1ycq = parser.get_structure('P 53 - Transactivation', 'pdb1ycq.ent')

Вы можете получить некоторые предупреждения о содержимом файла. Обычно это не представляет из себя проблему. 3. Теперь давайте проверим наши заголовки: def print_pdb_headers(headers, indent=0): ind_text = ' ' * indent for header, content in headers.items(): if type(content) == dict: print('\n%s%20s:' % (ind_text, header)) print_pdb_headers(content, indent + 4) print()

Представляем Bio.PDB    237 elif type(content) == list: print('%s%20s:' % (ind_text, header)) for elem in content: print('%s%21s %s' % (ind_text, '->', elem)) else: print('%s%20s: %s' % (ind_text, header, content)) print_pdb_headers(p53_1tup.header)

Заголовки анализируются синтаксически как словарь словарей. Таким образом, для их парсинга мы будем использовать рекурсивную функцию. Эта функция увеличит отступ для удобства чтения и  аннотирует списки элементов префиксом –>. Пример рекурсивных функций см. в  предыдущей главе «Филогенетика». Для более подробного обсуждения рекурсии в Python перейдите к последней главе 12 «Функциональное программирование для биоинформатики». Сокращенный вывод выглядит следующим образом: name:

tumor suppressor p53 complexed with dna

head:

antitumor protein/dna

idcode:

1TUP

deposition_date:

1995-07-11

release_date:

1995-07-11

structure_method: resolution:

x-ray diffraction 2.2

structure_reference: -> n.p.pavletich,k.a.chambers,c.o.pabo the dna-binding domain of p53 contains the four conserved regions and the major mutation hot spots genes dev. v. 7 2556 1993 issn 0890-9369 author:

Y.Cho,S.

Gorina,P.D.Jeffrey,N.P.Pavletich compound: 2: misc: molecule: dna (5'-d(*ap*tp*ap*ap*tp*tp*gp*gp*gp*cp*ap*ap*gp*tp*cp*tp*a p*gp*gp*ap*a)-3') chain: f engineered: has_missing_residues: missing_residues:

yes True

238    Использование Protein Data Bank -> {'model': None, 'res_name': 'ARG', 'chain': 'A', 'ssseq': 290, 'insertion': None} keywords: antigen p53, antitumor protein/dna complex journal: AUTH Y.CHO,S. GORINA,P.D.JEFFREY,N.P.PAVLETICHTITL CRYSTAL STRUCTURE OF A P53 TUMOR SUPPRESSOR-DNATITL 2 COMPLEX: UNDERSTANDING TUMORIGENIC MUTATIONS.REF SCIENCE57

4. Мы хотим знать содержимое каждой цепи в этих файлах; для этого давайте посмотрим на записи COMPND: print(p53_1tup.header['compound']) print(p53_1olg.header['compound']) print(p53_1ycq.header['compound'])

Это вернет все составные заголовки, напечатанные в  предыдущем коде. К сожалению, это не лучший способ получить информацию о цепях. Альтернативой может быть получение записей DBREF, но парсер Biopython в настоящее время не может получить к ним доступ. При этом использование такого инструмента, как grep, позволяет легко извлечь эту информацию. Обратите внимание, что для модели 1TUP цепи A, B и C – из белка, а цепи E и F – из ДНК. Эта информация пригодится в будущем. 5. Давайте проведем нисходящий анализ каждого файла PDB. А пока просто получим все цепи, количество остатков и атомов на цепь следующим образом: def describe_model(name, pdb): print() for model in pdb: for chain in model: print('%s - Chain: %s. Number of residues: %d. Number of atoms: %d.' % (name, chain.id, len(chain), len(list(chain.get_atoms())))) describe_model('1TUP', p53_1tup) describe_model('1OLG', p53_1olg) describe_model('1YCQ', p53_1ycq)

Мы выполним восходящий анализ в более позднем рецепте. Вот вывод для 1TUP: 1TUP - Chain: E. Number of residues: 43. Number of atoms: 442. 1TUP - Chain: F. Number of residues: 35. Number of atoms: 449. 1TUP - Chain: A. Number of residues: 395. Number of atoms: 1734. 1TUP - Chain: B. Number of residues: 265. Number of atoms: 1593. 1TUP - Chain: C. Number of residues: 276. Number of atoms: 1610.

Представляем Bio.PDB    239 1OLG - Chain: A. Number of residues: 42. Number of atoms: 698. 1OLG - Chain: B. Number of residues: 42. Number of atoms: 698. 1OLG - Chain: C. Number of residues: 42. Number of atoms: 698. 1OLG - Chain: D. Number of residues: 42. Number of atoms: 698. 1YCQ - Chain: A. Number of residues: 123. Number of atoms: 741. 1YCQ - Chain: B. Number of residues: 16. Number of atoms: 100.

6. Получим все нестандартные остатки (HETATM), за исключением воды, в модели 1TUP, как показано в следующем коде: for residue in p53_1tup.get_residues(): if residue.id[0] in [' ', 'W']: continue print(residue.id)

У нас есть три цинка, по одному в каждой из белковых цепей. 7. Давайте посмотрим на остаток: res = next(p53_1tup[0]['A'].get_residues()) print(res) for atom in res: print(atom, atom.serial_number, atom.element) p53_1tup[0]['A'][94]['CA']

Это напечатает все атомы в определенном остатке:

858 N 859 C 860 C 861 O 862 C 863 O

Обратите внимание на последнее сообщение. Оно здесь только для того, чтобы показать вам, что вы можете получить прямой доступ к  атому, разрешив модель, цепочку, остаток и, наконец, атом. 8. Теперь экспортируем фрагмент белка в файл FASTA следующим образом: from Bio.SeqIO import PdbIO, FastaIO def get_fasta(pdb_file, fasta_file, transfer_ids=None):

240    Использование Protein Data Bank fasta_writer = FastaIO.FastaWriter(fasta_file) fasta_writer.write_header() for rec in PdbIO.PdbSeqresIterator(pdb_file): if len(rec.seq) == 0: continue if transfer_ids is not None and rec.id not in transfer_ids: continue print(rec.id, rec.seq, len(rec.seq)) fasta_writer.write_record(rec) get_fasta(open('pdb1tup.ent'), open('1tup.fasta', 'w'), transfer_ ids=['1TUP:B']) get_fasta(open('pdb1olg.ent'), open('1olg.fasta', 'w'), transfer_ids=['1OLG:B']) get_fasta(open('pdb1ycq.ent'), open('1ycq.fasta', 'w'), transfer_ ids=['1YCQ:B'])

Если вы осмотрите цепочку белков, то увидите, что они одинаковы в каждой модели, поэтому мы экспортируем только одну. В случае 1YCQ мы экспортируем наименьшую, потому что самая большая не  связана с p53. Как видите, здесь мы используем Bio.SeqIO, а не Bio.PDB.

Дополнительно Парсер PDB несовершенный. Полноценный парсер вряд ли скоро появится, так как сообщество переходит на формат mmCIF. Хотя будущее за форматом mmCIF (http://mmcif.wwpdb.org/), файлы PDB все еще существуют. Концептуально многие операции аналогичны после того, как вы сделали парсинг файла.

Извлечение дополнительной информации из файла PDB Здесь мы продолжим изучение структуры записи, созданной Bio.PDB из файлов PDB.

Подготовка Для получения общей информации о  моделях PDB, которые мы используем, обратитесь к  предыдущему рецепту. Вы  можете найти этот контент в  файле блокнота Chapter08/Stats.py.

Как это сделать... Мы начнем, выполняя следующие шаги. 1. Во-первых, давайте получим 1TUP следующим образом:

Извлечение дополнительной информации из файла PDB    241 from Bio import PDB repository = PDB.PDBList() parser = PDB.PDBParser() repository.retrieve_pdb_file('1TUP', pdir='.', file_format='pdb') p53_1tup = parser.get_structure('P 53', 'pdb1tup.ent')

2. Затем извлеките некоторую статистику, связанную с атомом: from collections import defaultdict atom_cnt = defaultdict(int) atom_chain = defaultdict(int) atom_res_types = defaultdict(int) for atom in p53_1tup.get_atoms(): my_residue = atom.parent my_chain = my_residue.parent atom_chain[my_chain.id] += 1 if my_residue.resname != 'HOH': atom_cnt[atom.element] += 1 atom_res_types[my_residue.resname] += 1 print(dict(atom_res_types)) print(dict(atom_chain)) print(dict(atom_cnt))

Это напечатает информацию о типе остатка атома, количестве атомов в цепи и количестве на элемент следующим образом: {' DT': 257, ' DC': 152, ' DA': 270, ' DG': 176, 'HOH': 384, 'SER': 323, 'VAL': 315, 'PRO': 294, 'GLN': 189, 'LYS': 135, 'THR': 294, 'TYR': 288, 'GLY': 156, 'PHE': 165, 'ARG': 561, 'LEU': 336, 'HIS': 210, 'ALA': 105, 'CYS': 180, 'ASN': 216, 'MET': 144, 'TRP': 42, 'ASP': 192, 'ILE': 144, 'GLU': 297, ' ZN': 3} {'E': 442, 'F': 449, 'A': 1734, 'B': 1593, 'C': 1610} {'O': 1114, 'C': 3238, 'N': 1001, 'P': 40, 'S': 48, 'ZN': 3}

Обратите внимание, что предыдущее количество остатков – это не точное количество остатков, а количество раз, когда упоминается определенный тип остатка (оно суммируется с количеством атомов, а не остатков). Обратите внимание на остатки воды (W), нуклеотидов (DA, DC, DG и  DT) и цинка (ZN), которые добавляются к остаткам аминокислот. 3. Теперь посчитаем количество типов на остаток и количество остатков на цепь:

242    Использование Protein Data Bank res_types = defaultdict(int) res_per_chain = defaultdict(int) for residue in p53_1tup.get_residues(): res_types[residue.resname] += 1 res_per_chain[residue.parent.id] +=1 print(dict(res_types)) print(dict(res_per_chain))

Вывод следующий: {' DT': 13, ' DC': 8, 45, 'PRO': 42, 'GLN': 'PHE': 15, 'ARG': 51, 27, 'MET': 18, 'TRP':

' DA': 13, ' DG': 8, 'HOH': 384, 'SER': 54, 'VAL': 21, 'LYS': 15, 'THR': 42, 'TYR': 24, 'GLY': 39, 'LEU': 42, 'HIS': 21, 'ALA': 21, 'CYS': 30, 'ASN': 3, 'ASP': 24, 'ILE': 18, 'GLU': 33, ' ZN': 3}

{'E': 43, 'F': 35, 'A': 395, 'B': 265, 'C': 276}

4. Мы также можем получить границы последовательности атомов: import sys def get_bounds(my_atoms): my_min = [sys.maxsize] * 3 my_max = [-sys.maxsize] * 3 for atom in my_atoms: for i, coord in enumerate(atom.coord): if coord < my_min[i]: my_min[i] = coord if coord > my_max[i]: my_max[i] = coord return my_min, my_max chain_bounds = {} for chain in p53_1tup.get_chains(): print(chain.id, get_bounds(chain.get_atoms())) chain_bounds[chain.id] = get_bounds(chain.get_atoms()) print(get_bounds(p53_1tup.get_atoms()))

Набор атомов может быть целой моделью, цепью, остатком или любым интересующим вас подмножеством. В  этом случае мы будем печатать границы для всех цепей и всей модели. Цифры не передают это так интуитивно, поэтому мы сделаем это немного более графическим.

Извлечение дополнительной информации из файла PDB    243 5. Чтобы получить представление о размере каждой цепи, диаграмма, очевидно, более информативна, чем числа в предыдущем коде: import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D fig = plt.figure(figsize=(16, 9)) ax3d = fig.add_subplot(111, projection='3d') ax_xy = fig.add_subplot(331) ax_xy.set_title('X/Y') ax_xz = fig.add_subplot(334) ax_xz.set_title('X/Z') ax_zy = fig.add_subplot(337) ax_zy.set_title('Z/Y') color = {'A': 'r', 'B': 'g', 'C': 'b', 'E': '0.5', 'F': '0.75'} zx, zy, zz = [], [], [] for chain in p53_1tup.get_chains(): xs, ys, zs = [], [], [] for residue in chain.get_residues(): ref_atom = next(residue.get_iterator()) x, y, z = ref_atom.coord if ref_atom.element == 'ZN': zx.append(x) zy.append(y) zz.append(z) continue xs.append(x) ys.append(y) zs.append(z) ax3d.scatter(xs, ys, zs, color=color[chain.id]) ax_xy.scatter(xs, ys, marker='.', color=color[chain.id]) ax_xz.scatter(xs, zs, marker='.', color=color[chain.id]) ax_zy.scatter(zs, ys, marker='.', color=color[chain.id]) ax3d.set_xlabel('X') ax3d.set_ylabel('Y') ax3d.set_zlabel('Z') ax3d.scatter(zx, zy, zz, color='k', marker='v', s=300) ax_xy.scatter(zx, zy, color='k', marker='v', s=80)

244    Использование Protein Data Bank ax_xz.scatter(zx, zz, color='k', marker='v', s=80) ax_zy.scatter(zz, zy, color='k', marker='v', s=80) for ax in [ax_xy, ax_xz, ax_zy]: ax.get_yaxis().set_visible(False) ax.get_xaxis().set_visible(False)

Существует множество инструментов молекулярной визуализации. Конечно, мы обсудим PyMOL позже. Однако для простой визуализации вполне достаточно matplotlib. Наиболее важным моментом в matplotlib является то, что она стабильна и ее очень легко интегрировать в надежный программный код. На диаграмме ниже мы построили пространственное распределение цепей, где ДНК выделена серым цветом, а белковые цепи – разными цветами. Мы также сделали плоские проекции (X/Y, X/Z и Z/Y) в левой части следующей диаграммы.

Рис. 8.2. Пространственное распределение белковых цепей – основной рисунок представляет собой трехмерное представление, а левые диаграммы – двумерные виды (X/Y, X/Z и Z/Y)

Вычисление молекулярных расстояний в файле PDB Здесь мы найдем атомы, более близкие к трем атомам цинка в модели 1TUP. Мы рассмотрим несколько расстояний до этих атомов цинка. Воспользуемся этой возможностью, чтобы обсудить производительность алгоритмов.

Подготовка Вы можете найти этот контент в файле блокнота Chapter08/Distance.py.

Вычисление молекулярных расстояний в файле PDB    245

Как это сделать... Рассмотрим следующие шаги. 1. Давайте загрузим нашу модель следующим образом: from Bio import PDB repository = PDB.PDBList() parser = PDB.PDBParser() repository.retrieve_pdb_file('1TUP', pdir='.', file_format='pdb') p53_1tup = parser.get_structure('P 53', 'pdb1tup.ent')

2. Теперь мы получим наши атомы цинка, с  которыми будем проводить сравнения позже: zns = []for atom in p53_1tup.get_atoms(): if atom.element == 'ZN': zns.append(atom) for zn in zns: print(zn, zn.coord)

Вы должны увидеть три атома цинка. 3. Теперь давайте определим функцию для получения расстояния между одним атомом и набором других атомов следующим образом: import math def get_closest_atoms(pdb_struct, ref_atom, distance): atoms = {} rx, ry, rz = ref_atom.coord for atom in pdb_struct.get_atoms(): if atom == ref_atom: continue x, y, z = atom.coord my_dist = math.sqrt((x - rx)**2 + (y - ry)**2 + (z - rz)**2) if my_dist < distance: atoms[atom] = my_dist return atoms

Мы получаем координаты для нашего референсного атома, а затем перебираем желаемый список сравнения. Если атом находится достаточно близко, он добавляется в список return. 4. Теперь вычислим атомы рядом с нашими атомами цинка, расстояние до которых для нашей модели может составлять до 4 Å (ангстрем):

246    Использование Protein Data Bank for zn in zns: print() print(zn.coord) atoms = get_closest_atoms(p53_1tup, zn, 4) for atom, distance in atoms.items(): print(atom.element, distance, atom.coord)

Здесь мы показываем результат для первого цинка, включая элемент, расстояние и координаты: [58.108 23.242 57.424] C 3.4080117696286854 [57.77 21.214 60.142] S 2.3262243799594877 [57.065 21.452 58.482] C 3.4566537492335123 [58.886 20.867 55.036] C 3.064120559761192 [58.047 22.038 54.607] N 1.9918273537290707 [57.755 23.073 55.471] C 2.9243719601324525 [56.993 23.943 54.813] C 3.857729198122736 [61.148 25.061 55.897] C 3.62725094648044 [61.61 24.087 57.001] S 2.2789209624943494 [60.317 23.318 57.979] C 3.087214470667822 [57.205 25.099 59.719]

S 2.2253158446520818 [56.914 25.054 57.917] У нас всего три атома цинка, так что количество вычислений довольно сильно сокращается. Однако представьте, что у нас их больше или что мы делаем попарное сравнение всех атомов в наборе (помните, что число сравнений растет квадратично с  количеством атомов в  парном случае). Хотя наш случай небольшой, спрогнозировать варианты несложно, а дополнительные сравнения занимают много времени. Мы скоро вернемся к этому. 5. Посмотрим, сколько атомов мы получим при увеличении расстояния: for distance in [1, 2, 4, 8, 16, 32, 64, 128]: my_atoms = [] for zn in zns: atoms = get_closest_atoms(p53_1tup, zn, distance my_atoms. append(len(atoms)) print(distance, my_atoms)

Результат выглядит следующим образом: 1 [0, 0, 0] 2 [1, 0, 0]

Вычисление молекулярных расстояний в файле PDB    247 4 [11, 11, 12] 8 [109, 113, 106] 16 [523, 721, 487] 32 [2381, 3493, 2053] 64 [5800, 5827, 5501] 128 [5827, 5827, 5827]

6. Как мы видели ранее, этот конкретный случай имеет не очень большую цену вычисления, но давайте все же рассчитаем время: import timeit nexecs = 10 print(timeit.timeit('get_closest_atoms(p53_1tup, zns[0], 4.0)', 'from_main_import get_closest_atoms, p53_1tup, zns', number=nexecs) / nexecs * 1000)

Здесь мы будем использовать модуль timeit, чтобы выполнить эту функцию 10 раз, а  затем вывести результат в  миллисекундах. Мы  передаем функцию как строку и передаем еще одну строку с необходимым импортом, чтобы эта функция работала. В  блокноте вы, скорее всего, знаете о  функции  %timeit magic и  о том, как она делает нашу жизнь намного проще в этом случае. На машине, на которой тестировался код, это занимает примерно 40 миллисекунд. Очевидно, что на вашем компьютере вы получите несколько иные результаты. 7. Можем ли мы сделать лучше? Давайте рассмотрим другую функцию distance, как показано в следующем коде: def get_closest_alternative(pdb_struct, ref_atom, distance): atoms = {} rx, ry, rz = ref_atom.coord for atom in pdb_struct.get_atoms(): if atom == ref_atom: continue x, y, z = atom.coord if abs(x - rx) > distance or abs(y - ry) > distance or abs(z rz) > distance: continue my_dist = math.sqrt((x - rx)**2 + (y - ry)**2 + (z - rz)**2) if my_dist < distance: atoms[atom] = my_dist return atoms

248    Использование Protein Data Bank Итак, мы берем исходную функцию и добавляем очень упрощенное if с расстояниями. Причина этого в том, что цена вычисления квадратного корня и, возможно, операции с плавающей запятой очень высока, поэтому мы постараемся их избежать. Однако для всех атомов, находящихся ближе целевого расстояния в любом измерении, эта функция будет иметь большую цену. 8. Теперь посчитаем время: print(timeit.timeit('get_closest_alternative(p53_1tup, zns[0], 4.0)', 'from_main_import get_closest_alternative, p53_1tup, zns', number=nexecs) / nexecs * 1000)

На той же машине, которую мы использовали в предыдущем примере, это занимает 16 миллисекунд, что означает, что это примерно в три раза быстрее. 9. Однако всегда ли это лучше? Давайте сравним цены вычисления на разных расстояниях следующим образом: print('Standard') for distance in [1, 4, 16, 64, 128]: print(timeit.timeit('get_closest_atoms(p53_1tup, zns[0], distance)', 'from __main__ import get_closest_atoms, p53_1tup, zns, distance', number=nexecs) / nexecs * 1000) print('Optimized') for distance in [1, 4, 16, 64, 128]: print(timeit.timeit('get_closest_alternative(p53_1tup, zns[0], distance)', 'from_main_import get_closest_alternative, p53_1tup, zns, distance', number=nexecs) / nexecs * 1000)

Результат показан в нижеприведенном выводе: Standard 85.08649739999328 86.50681579999855 86.79630599999655 96.95437099999253 96.21982420001132 Optimized 30.253444099980698 32.69531210000878 52.965772600009586 142.53310030001103 141.26269519999823

Выполнение геометрических операций    249 Обратите внимание, что цена вычисления стандартной версии в основном постоянна, тогда как оптимизированная версия варьируется в зависимости от расстояния до ближайших атомов; чем больше расстояние, тем больше случаев, которые нужно будет вычислять с использованием дополнительной if плюс квадратный корень, что делает цену функции еще более высокой. Гораздо важным моментом здесь является то, что вы, скорее всего, можете кодировать функции, которые более эффективны, используя интеллектуальные ярлыки вычислений, но комплексная цена может качественно измениться. В предыдущем случае я предполагаю, что вторая функция более эффективна для всех реалистичных и  интересных случаев, когда вы пытаетесь найти ближайшие атомы. Однако вы должны быть осторожны при разработке собственных версий оптимизированных алгоритмов.

Выполнение геометрических операций Теперь мы выполним вычисления с информацией о геометрии, включая вычисление центра масс цепей и целых моделей.

Подготовка Вы можете найти этот контент в файле блокнота Chapter08/Mass.py.

Как это сделать... Рассмотрим следующие шаги. 1. Во-первых, давайте получим данные: from Bio import PDB repository = PDB.PDBList() parser = PDB.PDBParser() repository.retrieve_pdb_file('1TUP', pdir='.', file_format='pdb') p53_1tup = parser.get_structure('P 53', 'pdb1tup.ent')

2. Затем вызовем тип остатков, которые у нас есть, с помощью следующего кода: my_residues = set() for residue in p53_1tup.get_residues(): my_residues.add(residue.id[0]) print(my_residues)

Итак, у нас есть H_ ZN (цинк) и W (вода), которые являются типами HETATM; подавляющее большинство является стандартными атомами PDB. 3. Давайте вычислим массы для всех цепей, атомов цинка и воды, используя следующий код:

250    Использование Protein Data Bank def get_mass(atoms, accept_fun=lambda atom: atom.parent.id[0] != 'W'): return sum([atom.mass for atom in atoms if accept_fun(atom)]) chain_names = [chain.id for chain in p53_1tup.get_chains()] my_mass = np.ndarray((len(chain_names), 3)) for i, chain in enumerate(p53_1tup.get_chains()): my_mass[i, 0] = get_mass(chain.get_atoms()) my_mass[i, 1] = get_mass(chain.get_atoms(), accept_fun=lambda atom: atom.parent.id[0] not in [' ', 'W']) my_mass[i, 2] = get_mass(chain.get_atoms(), accept_fun=lambda atom: atom.parent.id[0] == 'W') masses = pd.DataFrame (my_mass, index=chain_names, columns=['No Water', 'Zincs', 'Water']) print(masses)

Функция get_mass возвращает массу всех атомов в списке, который проходит через функцию критерия приемлемости. Здесь критерий приемлемости по умолчанию включает отсутствие остатка воды. Затем мы вычисляем массу для всех цепей. У нас есть три версии: только аминокислоты, цинк и  вода. Цинк не дает ничего, кроме обнаружения одного атома в цепи в этой модели. Результат выглядит следующим образом: Нет воды

Цинк

Вода

E

6068.04412

0.00

351.9868

F

6258.20442

0.00

223.9916

A

20548.26300

65.39

3167.8812

B

20368.18840

65.39

1119.9580

C

20466.22540

65.39

1279.9520

Рис. 8.3. Масса для всех белковых цепочек

4. Рассчитаем геометрический центр и центр масс модели следующим образом: def get_center(atoms, weight_fun=lambda atom: 1 if atom.parent.id[0] != 'W' else 0): xsum = ysum = zsum = 0.0 acum = 0.0 for atom in atoms:

Выполнение геометрических операций    251 x, y, z = atom.coord weight = weight_fun(atom) acum += weight xsum += weight * x ysum += weight * y zsum += weight * z return xsum / acum, ysum / acum, zsum / acum print(get_ center(p53_1tup.get_atoms())) print(get_center(p53_1tup.get_atoms(), weight_fun=lambda atom: atom.mass if atom.parent. id[0] != 'W' else 0))

Во-первых, мы определяем взвешенную функцию для получения координат центра. Функция по умолчанию будет рассматривать все атомы как равные, если они не являются остатками воды. Затем мы вычисляем геометрический центр и центр масс, переопределяя весовую функцию со значением каждого атома, равным его массе. Геометрический центр вычисляется независимо от его молекулярной массы. Например, вы можете захотеть вычислить центр масс белка без цепочек ДНК. 5. Вычислим центр масс и геометрический центр каждой цепи следующим образом: my_center = np.ndarray((len(chain_names), 6)) for i, chain in enumerate(p53_1tup.get_chains()): x, y, z = get_center(chain.get_atoms()) my_center[i, 0] = x my_center[i, 1] = y my_center[i, 2] = z x, y, z = get_center(chain.get_atoms(), weight_fun=lambda atom: atom. mass if atom.parent.id[0] != 'W' else 0) my_center[i, 3] = x my_center[i, 4] = y my_center[i, 5] = z weights = pd.DataFrame (my_center, index=chain_names, columns=['X', 'Y', 'Z', 'X (Mass)', 'Y (Mass)', 'Z (Mass)']) print(weights)

Результат показан здесь:

252    Использование Protein Data Bank X

Y

Z

X (масса)

Y (масса)

Z (масса)

E

49.727231

32.744879

81.253417

49.708513

32.759725

81.207395

F

51.982368

33.843370

81.578795

52.002223

33.820064

81.6254394

A

72.990763

28.825429

56.714012

72.822668

28.810327

56.716117

B

67.810026

12.624435

88.656590

67.729100

12.724130

88.545659

C

38.221565

–5.010494

88.293141

38.169364

–4.915395

88.166711

Рис. 8.4. Центр масс и геометрический центр каждой белковой цепи

Дополнительно Хотя эта книга не  основана на методе определения структуры белка, важно помнить, что методы рентгеновской кристаллографии не  могут обнаружить атомы водорода, поэтому вычисление массы остатков может основываться на очень неточных моделях; для получения дополнительной информации обратитесь к http://www.umass.edu/microbio/chime/pe_beta/pe/protexpl/help_hyd.htm.

Анимация с PyMOL Здесь мы создадим видео модели p53 1TUP. Для этого будем использовать биб­ лиотеку визуализации PyMOL. Мы начнем нашу анимацию с перемещения по модели p53 1TUP, а  затем увеличим масштаб; по мере увеличения масштаба мы меняем стратегию рендеринга, чтобы вы могли глубже заглянуть в модель. Вы  можете найти пример видео, которое будете создавать, по адресу https:// odysee.com/@Python:8/protein_video:8.

Подготовка Этот рецепт будет представлен в виде скрипта Python, а не как Notebook. В основном это связано с тем, что вывод не является интерактивным, а представляет собой набор (последовательность) файлов изображений, которые потребуют дальнейшего процессинга. Вам нужно будет установить PyMOL (http://www.pymol.org). В Debian, Ubuntu или Linux вы можете применить команду apt-get install pymol. Если вы используете Conda, я предлагаю не применять его, так как зависимости будут легко устранены  – кроме того, вы будете устанавливать только 30-дневную пробную версию, требующую лицензии, тогда как указанная выше версия имеет полностью открытый исходный код. Если вы не используете Debian или Linux, я предлагаю вам установить версию с открытым исходным кодом, доступную для вашей операционной системы. PyMOL – это скорее интерактивная программа, чем библиотека Python, поэтому я настоятельно рекомендую вам поиграть с ней, прежде чем переходить к рецепту. Это может быть занятно! Код для этого рецепта доступен в репозитории GitHub в виде скрипта вместе с файлом Notebook этой главы в Chapter08. В этом рецепте мы будем использовать файл PyMol_Movie.py.

Анимация с PyMOL    253

Как это сделать... Рассмотрим следующие шаги. 1. Давайте инициализируем и  извлечем нашу модель PDB и  подготовим визуализацию следующим образом: import pymol from pymol import cmd #pymol.pymol_argv = ['pymol', '-qc'] # Quiet / no GUI pymol.finish_ launching() cmd.fetch('1TUP', async=False) cmd.disable('all') cmd.enable('1TUP') cmd.hide('all') cmd.show('sphere', 'name zn')

Обратите внимание, что строка pymol_argv отключает код. При первом выполнении вы можете закомментировать это и увидеть пользовательский интерфейс. Для рендеринга анимированного видео это пригодится (как мы скоро увидим). Как библиотеку PyMOL довольно сложно использовать. Например, после импорта вам нужно вызвать finish_launching. Затем мы получаем наш файл PDB. Далее следует набор команд PyMOL. Многие веб-руководства для интер­ активного использования могут быть весьма полезными для понимания того, что происходит. Здесь мы включим все модели для просмотра, скроем все (поскольку вид по умолчанию состоит из линий, а этого недостаточно), а затем сделаем атомы цинка видимыми как сферы. На данном этапе это полоса цинка, все остальное невидимо. 2. Для рендеринга нашей модели мы будем использовать три сцены следующим образом: cmd.show('surface', 'chain A+B+C') cmd.show('cartoon', 'chain E+F') cmd.scene('S0', action='store', view=0, frame=0, animate=-1) cmd.show('cartoon') cmd.hide('surface') cmd.scene('S1', action='store', view=0, frame=0, animate=-1) cmd.hide('cartoon', 'chain A+B+C') cmd.show('mesh', 'chain A') cmd.show('sticks', 'chain A+B+C') cmd.scene('S2', action='store', view=0, frame=0, animate=-1)

254    Использование Protein Data Bank Нам нужно определить две сцены. Одна сцена соответствует тому, как мы двигаемся по белку (на поверхности, поэтому она непрозрачна), а другая соответствует тому, как мы ныряем внутрь (на основе анимации). ДНК всегда изображается в виде анимации. Мы также определяем третью сцену, когда уменьшаем масштаб в конце. Белок будет представлен в виде палочек, и мы добавим сетку к цепи А, чтобы связь с ДНК стала более ясной. 3. Давайте определим базовый параметр нашего видео следующим образом: cmd.set('ray_trace_frames', 0) cmd.mset(1, 500)

Мы определяем алгоритм рей-трейсинга1 по умолчанию. Эта строка не обязательно должна быть там, но попробуйте увеличить число до 1, 2 или 3 и будьте готовы долго ждать. Вы можете использовать 0, только если у вас включен интерфейс OpenGL (с GUI), поэтому для этой быстрой версии вам потребуется включить графический интерфейс GUI (pymol_argv следует комментировать как есть). Затем мы сообщаем PyMOL, что у нас будет 500 кадров. 4. В  первых 150 кадрах мы перемещаемся, используя начальную сцену. Мы  немного перемещаемся по модели, а  затем приближаемся к  ДНК, используя следующий код: cmd.frame(0) cmd.scene('S0') cmd.mview() cmd.frame(60) cmd.set_view((-0.175534308, -0.331560850, -0.926960170, 0.541812420, 0.753615797, -0.372158051, 0.821965039, -0.567564785, 0.047358301, 0.000000000, 0.000000000, -249.619018555, 58.625568390, 15.602619171, 77.781631470, 196.801528931, 302.436492920, -20.000000000)) 1

Алгоритм расчета изображения для создания реалистичного освещения, отражений и теней, обеспечивающего очень высокий уровень реализма. – Прим. ред.

Анимация с PyMOL    255 cmd.mview() cmd.frame(90) cmd.set_view((-0.175534308, -0.331560850, -0.926960170, 0.541812420, 0.753615797, -0.372158051, 0.821965039, -0.567564785, 0.047358301, -0.000067875, 0.000017881, -249.615447998, 54.029174805, 26.956727982, 77.124832153, 196.801528931, 302.436492920, -20.000000000)) cmd.mview() cmd.frame(150) cmd.set_view((-0.175534308, -0.331560850, -0.926960170, 0.541812420, 0.753615797, -0.372158051, 0.821965039, -0.567564785, 0.047358301, -0.000067875, 0.000017881, -55.406421661, 54.029174805, 26.956727982, 77.124832153, 2.592475891, 108.227416992, -20.000000000)) cmd.mview()

Определяем три точки; первые две приближаются к  ДНК, а  последняя точка входит. Мы  получаем координаты (все их численные значения), используя PyMOL в  интерактивном режиме, перемещаясь с  помощью мыши и клавиатуры и используя команду get_view, которая вернет координаты, которые вы можете скопировать и вставить. Первый кадр выглядит следующим образом:

256    Использование Protein Data Bank

Рис. 8.5. Кадр 0 и сцена DS0

5. Теперь изменим сцену, готовясь войти внутрь белка: cmd.frame(200) cmd.scene('S1') cmd.mview()

На следующем скриншоте показано текущее положение.

Рис. 8.6. Кадр 200 рядом с молекулой ДНК и сценой S1

6. Двигаемся внутрь белка и меняем сцену в конце с помощью следующего кода: cmd.frame(350) cmd.scene('S1') cmd.set_view((0.395763457, -0.173441306,

Анимация с PyMOL    257 0.901825786, 0.915456235, 0.152441502, -0.372427106, -0.072881661, 0.972972929, 0.219108686, 0.000070953, 0.000013039, -37.689743042, 57.748500824, 14.325904846, 77.241867065, -15.123448372, 90.511535645, -20.000000000)) cmd.mview() cmd.frame(351) cmd.scene('S2')

cmd.mview() Теперь мы полностью внутри, как показано на следующем скриншоте.

Рис. 8.7. Кадр 350 – сцена S1 на грани, где она меняется на S2

7. Наконец, мы позволяем PyMOL вернуться в  исходное положение, а  затем воспроизводим анимацию, сохраняем и выходим: cmd.frame(500) cmd.scene('S2') cmd.mview() cmd.mplay() cmd.mpng('p53_1tup') cmd.quit()

258    Использование Protein Data Bank Это создаст 500 графических растровых файлов PNG с  префиксом p53_1tup. Вот кадр, где анимация приближается к концу (450):

Рис. 8.8. Кадр 450 и сцена S2

Дополнительно Видео для YouTube было сгенерировано с  использованием ffmpeg в  Linux со скоростью воспроизведения 15 кадров в секунду следующим образом: ffmpeg -r 15 -f image2 -start_number 1 -i "p53_1tup%04d.png" example.mp4

Существует множество приложений, которые можно использовать для создания видео из последовательности изображений. PyMOL может генерировать формат видео MPEG, но это требует установки дополнительных библиотек. Система визуализации PyMOL была создана для интерактивного применения из ее консоли (которую можно расширить в Python). Использование ее наоборот (импорт из Python без графического интерфейса) может быть сложным и  разочаровывающим. PyMOL запускает отдельные потоки для рендеринга изображений, которые работают асинхронно. Например, это означает, что ваш код может находиться не в той позиции, где находится средство визуализации. Я поместил еще один скрипт под названием PyMol_Intro.py в репозиторий GitHub; вы увидите, что второй вызов PNG начнется до завершения первого. Попробуйте код скрипта, посмотрите, как он будет себя вести по вашим ожиданиям и как он будет вести себя на самом деле. Существует много хорошей документации по PyMOL с  точки зрения GUI на http://www. pymolwiki.org/index.php/MovieSchool. Это отличная отправная точка, если вы хотите снимать фильмы, а http://www.pymolwiki.org – кладезь информации.

Парсинг файлов mmCIF с помощью Biopython Формат файла mmCIF – скорее всего, формат будущего. У Biopython пока нет полного функционала для работы с ним, но мы посмотрим на то, что есть на данный момент.

Парсинг файлов mmCIF с помощью Biopython    259

Подготовка Поскольку Bio.PDB не может автоматически загружать файлы mmCIF, вам необходимо получить файл белка и  переименовать его в  1tup.cif. Это можно найти по адресу https://github.com/PacktPublishing/Bioinformatics-with-PythonCookbook-Third-Edition/blob/master/Datasets.py под 1TUP.cif. Вы можете найти этот контент в файле блокнота Chapter08/mmCIF.py.

Как это сделать... Рассмотрим следующие шаги. 1. Делаем парсинг файла1. Мы используем парсер MMCIF вместо парсера PDB: from Bio import PDB parser = PDB.MMCIFParser() p53_1tup = parser.get_structure('P53', '1tup.cif')

2. Проверим следующие цепи: def describe_model(name, pdb): print() for model in p53_1tup: for chain in model: print('%s - Chain: %s. Number of residues: %d. Number of atoms: %d.' % (name, chain.id, len(chain), len(list(chain.get_atoms())))) describe_model('1TUP', p53_1tup)

Вывод будет таким: 1TUP - Chain: E. Number of residues: 43. Number of atoms: 442. 1TUP - Chain: F. Number of residues: 35. Number of atoms: 449. 1TUP - Chain: A. Number of residues: 395. Number of atoms: 1734. 1TUP - Chain: B. Number of residues: 265. Number of atoms: 1593. 1TUP - Chain: C. Number of residues: 276. Number of atoms: 1610.

3. Многие поля в анализируемой структуре недоступны, но их все же можно получить с  помощью словаря более низкого уровня, как показано ниже:

Парсинг, или «разбор», – это синтаксический анализ файла. В ходе синтаксического анализа исходный текст преобразуется в структуру данных, обычно в дерево, которое отражает синтаксическую структуру входной последовательности и хорошо подходит для дальнейшей обработки. – Прим. ред.

1

260    Использование Protein Data Bank mmcif_dict = PDB.MMCIF2Dict.MMCIF2Dict('1tup.cif') for k, v in mmcif_dict.items(): print(k, v) print()

К сожалению, этот список велик и требует некоторой последующей обработки, чтобы понять его, но зато он доступен.

Дополнительно У вас по-прежнему есть вся информация о модели из файла mmCIF, предоставленного Biopython, поэтому парсер по-прежнему весьма полезен. Мы можем ожидать большего развития парсера mmCIF, чем парсера PDB. Для этого существует библиотека Python, которую разработчики PDB предоставили по адресу http://mmcif.wwpdb.org/docs/sw-examples/python/html/index.html.

Глава 9

Конвейеры биоинформатики Конвейеры (pipelines) являются основой любой среды обработки данных. Обработка данных никогда не является единой задачей. Многие конвейеры реализованы посредством специальных скриптов. Это можно сделать полезным способом, но во многих случаях они не соответствуют нескольким фундаментальным установкам, главным образом воспроизводимости, ремонтопригодности и расширяемости. В биоинформатике можно найти три основных типа конвейерной системы:  фреймворки, такие как Galaxy (https://usegalaxy.org), ориентированные на пользователей, т. е. предоставляющие простые в  использовании пользовательские интерфейсы и скрывающие большую часть базового механизма;  рабочие процессы программирования – ориентированы на интерфейсы кода, которые, хотя и являются общими, происходят из области биоинформатики. Два примера: Snakemake (https://snakemake.readthedocs.io/) и Nextflow (https://www.nextflow.io/);  полностью универсальные системы рабочих процессов, такие как Apache Airflow (https://airflow.incubator.apache.org/), которые используют менее ориентированный на данные подход к  управлению рабочими процессами. В этой главе мы обсудим Galaxy, который особенно важен для специалистов по биоинформатике и поддерживает пользователей, которые менее вовлечены в программирование своих собственных решений. Хотя вы, возможно, не являетесь типичным пользователем этих конвейерных систем, вам, возможно, все же придется их организовать. К нашему счастью, Galaxy предоставляет интерфейсы API, которым мы будем уделять основное внимание. Мы также обсудим Snakemake и Nextflow как общие инструменты рабочего процесса с программными интерфейсами, родившимися в области биоинформатики. Рассмотрим оба инструмента, так как они наиболее распространены в  данной области. Мы  решим похожую задачу биоинформатики, используя как Snakemake, так и Nextflow. Попробуем оба фреймворка и, надеюсь, сможем определиться, кто из них наш фаворит. Код этих рецептов представлен не  в  виде блокнотов, а  в виде скриптов Python, доступных в директории Chapter09 репозитория книги. В этой главе вы найдете рецепты для следующего:  представление серверов Galaxy;  доступ к Galaxy с помощью API;

262    Конвейеры биоинформатики  разработка конвейера анализа вариантов посредством Snakemake;  разработка конвейера анализа вариантов с помощью Nextflow.

Представляем серверы Galaxy Galaxy (https://galaxyproject.org/tutorials/g101/)  – это система с  открытым исходным кодом, которая позволяет пользователям, обычно не занимающимся вычислениями, заниматься вычислительной биологией. Это наиболее широко используемая и удобная в использовании конвейерная система. Galaxy может быть установлен на сервер любым пользователем; в интернете есть множест­ во серверов с  публичным доступом, флагманом среди них является http:// usegalaxy.org. В следующих рецептах мы сосредоточимся на программной стороне Galaxy: взаимодействие с  использованием Galaxy API и  разработка инструмента Galaxy для расширения его функциональности. Прежде чем начать, вам настоя­тельно рекомендуется обратиться к Galaxy в качестве пользователя. Вы можете сделать это, создав бесплатный аккаунт на http://usegalaxy.org, и немного поиграться с ним. Рекомендуется достичь уровня понимания, подразумевающего знание рабочих процессов.

Подготовка В этом рецепте мы сделаем локальную установку сервера Galaxy с  помощью Docker. Таким образом, нам потребуется локальная установка Docker. Уровень сложности зависит от операционной системы: легкий в Linux, средний в macOS и средний или сложный в Windows. Эта установка рекомендуется для следующих двух рецептов, но вы также можете использовать существующие общедоступные серверы. Обратите внимание, что интерфейсы публичных серверов могут меняться со временем, поэтому то, что работает сегодня, может не работать завтра. Инструкции по использованию общедоступных серверов для следующих двух рецептов доступны в разделе «Смотрите также».

Как это сделать… Рассмотрим следующие шаги. Предполагается, что у вас есть командная строка с поддержкой Docker. 1. Сначала мы извлекаем образ Galaxy Docker с помощью следующей команды: docker pull bgruening/galaxy-stable:20.09

Эта команда вызовет прекрасный образ Docker Galaxy от Бьорна Грюнинга. Используйте лейбл 20.09, как показано в команде выше; что-нибудь более свежее может сломать этот рецепт и следующий за ним тоже. 2. Создайте каталог в вашей системе. В этом каталоге будут храниться текущие выходные данные контейнера Docker во время выполнения.

Представляем серверы Galaxy    263 Примечание Контейнеры Docker являются временными по отношению к дисковому пространству. Это означает, что при остановке контейнера все изменения на диске будут потеряны. Это можно решить, смонтировав тома с хоста на Docker, как на следующем шаге. Все содержимое смонтированных томов сохранится.

3. Теперь мы можем запустить образ с помощью следующей команды: docker run -d -v YOUR_DIRECTORY:/export -p 8080:80 -p 8021:21 bgruening/ galaxy-stable:20.09

Замените YOUR_DIRECTORY полным путем к  каталогу, созданному на шаге 2. Если предыдущая команда не удалась, убедитесь, что у вас есть разрешение на запуск Docker. Это будет варьироваться в зависимости от операционной системы. 4. Проверьте содержимое YOUR_DIRECTORY. При первом запуске образа будут созданы все файлы, необходимые для постоянного выполнения при запуске Docker. Это означает поддержку пользовательских баз данных, наборов данных и рабочих процессов. Укажите в  браузере http://localhost:8080. Если вы получаете какие-либо ошибки, подождите несколько секунд. Вы  должны увидеть следующий экран:

Рис. 9.1. Домашняя страница Galaxy Docker

5. Теперь войдите в систему (см. верхнюю панель), используя комбинацию имени пользователя и пароля по умолчанию: admin и password. 6. В верхнем меню выберите User, а внутри выберите Preferences. 7. Теперь выберите Manage API Key. Не меняйте ключ API. Цель предыдущего упражнения состоит в том, чтобы вы знали, где находится ключ API. В реальных сценариях вам нужно будет

264    Конвейеры биоинформатики перейти на этот экран, чтобы получить ключ. Просто обратите внимание на API key: fakekey. Между прочим, в обычных ситуациях это будет хеш MD5. Итак, на данном этапе у  нас установлен сервер со следующими учетными данными (по умолчанию): пользователь как admin, пароль как password и API key как fakekey. Точка доступа – localhost:8080.

Дополнительно Порядок, каким образом образ Бьорна Грюнинга будет использоваться в этой главе, довольно прост; в конце концов, это книга не по системному администрированию или DevOps, а по программированию. Если вы посетите https://github.com/bgruening/docker-galaxy-stable, то увидите, что сущест­ вует бесконечное количество способов настройки образа, и все они хорошо документированы. Наш простой подход здесь работает для наших целей разработки. Если вы не хотите устанавливать Galaxy на свой локальный компьютер, то можете использовать общедоступный сервер, такой как https://usegalaxy.org, для выполнения следующего рецепта. Это не на 100 % надежно, так как сервисы со временем меняются, но, скорее всего, этот номер пройдет. Выполните следующие действия. 1. Создайте аккаунт на общедоступном сервере (https://usegalaxy.org или другом). 2. Следуйте предыдущим инструкциям для доступа к ключу API. 3. В следующем рецепте вам придется заменить хост, пользователя, пароль и ключ API.

Доступ к Galaxy с помощью API Хотя основным вариантом использования Galaxy является простой в применении веб-интерфейс, он также предоставляет REST API для программного доступа. Существуют интерфейсы на нескольких языках, например поддержка Python доступна от BioBlend (https://bioblend.readthedocs.io). Здесь мы собираемся разработать скрипт, который загрузит файл BED в Galaxy и вызовет инструмент для его преобразования в формат GFF. Мы загрузим файл с помощью FTP-сервера Galaxy.

Подготовка Если вы не  прошли предыдущий рецепт, пожалуйста, прочитайте соответствующий раздел «Дополнительно». Код был протестирован на локальном сервере, как это было подготовлено в предыдущем рецепте, поэтому может потребоваться некоторая настройка, если вы запускаете его на общедоступном сервере. Наш код должен будет пройти аутентификацию на сервере Galaxy, чтобы выполнить необходимые операции. Поскольку безопасность является важным вопросом, этот рецепт не будет абсолютно наивным в ее отношении. Наш скрипт будет настроен через файл YAML, например:

Доступ к Galaxy с помощью API    265 rest_protocol: http server: localhost rest_port: 8080 sftp_port: 8022 user: admin password: password api_key: fakekey

Наш скрипт не примет этот файл как обычный текст, но потребует его шифрования. При этом в нашем плане безопасности есть большая дыра: мы будем использовать HTTP (вместо HTTPS), что означает, что пароли будут передаваться по сети в открытом виде. Очевидно, это плохое решение, но соображения пространства ограничивают наши возможности (особенно в предыдущем рецепте). Для действительно безопасных решений потребуется HTTPS. Нам понадобится скрипт, который берет файл YAML и генерирует зашифрованную версию: import base64 import getpass from io import StringIO import os from ruamel.yaml import YAML from cryptography.fernet import Fernet from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC password = getpass.getpass('Please enter the password:').encode() salt = os.urandom(16) kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, backend=default_backend()) key = base64.urlsafe_b64encode(kdf.derive(password)) fernet = Fernet(key) with open('salt', 'wb') as w: w.write(salt)

266    Конвейеры биоинформатики yaml = YAML() content = yaml.load(open('galaxy.yaml', 'rt', encoding='utf-8')) print(type(content), content) output = StringIO() yaml.dump(content, output) print ('Encrypting:\n%s' % output.getvalue()) enc_output = fernet.encrypt(output.getvalue().encode()) with open('galaxy.yaml.enc', 'wb') as w: w.write(enc_output)

Предыдущий файл можно найти в Chapter09/pipelines/galaxy/encrypt.py в репозитории GitHub. Вам нужно будет ввести пароль для шифрования. Предыдущий код не имеет отношения к Galaxy: он читает файл YAML и шифрует его с помощью пароля, предоставленного пользователем. Он использует шифрование модуля cryptography, а  также ruaml.yaml для обработки YAML. Выводятся два файла: зашифрованный файл YAML и  файл salt для шифрования. По соображениям безопасности файл salt не должен быть общедоступным. Этот подход к защите учетных данных далек от изощренности; он в основном иллюстрирует то, что вы должны быть осторожны со своим кодом при работе с  токенами аутентификации. В  интернете гораздо больше примеров жестко запрограммированных учетных данных безопасности.

Как это сделать… Рассмотрим следующие шаги, которые можно найти в Chapter09/pipelines/galaxy/api.py. 1. Начнем с расшифровки нашего конфигурационного файла. Нам нужно указать пароль: import base64 from collections import defaultdict import getpass import pprint import warnings

from ruamel.yaml import YAML

from cryptography.fernet import Fernet from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes

Доступ к Galaxy с помощью API    267 from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC import pandas as pd Import paramiko from bioblend.galaxy import GalaxyInstance pp = pprint.PrettyPrinter() warnings.filterwarnings('ignore') # explain above, and warn with open('galaxy.yaml.enc', 'rb') as f: enc_conf = f.read() password = getpass.getpass('Please enter the password:').encode() with open('salt', 'rb') as f: salt = f.read() kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, backend=default_backend()) key = base64.urlsafe_b64encode(kdf.derive(password)) fernet = Fernet(key) yaml = YAML() conf = yaml.load(fernet.decrypt(enc_conf).decode())

Последняя строка суммирует все: модуль YAML загрузит конфигурацию из расшифрованного файла. Обратите внимание, что мы также читаем salt, чтобы иметь возможность расшифровать файл. 2. Теперь мы получим все переменные конфигурации, подготовим URLадрес сервера и  укажем имя истории Galaxy, которую будем создавать (bioinf_example): server = conf['server'] rest_protocol = conf['rest_protocol'] rest_port = conf['rest_port'] user = conf['user'] password = conf['password'] ftp_port = int(conf['ftp_port']) api_key = conf['api_key']

268    Конвейеры биоинформатики rest_url = '%s://%s:%d' % (rest_protocol, server, rest_port) history_name = 'bioinf_example'

3. Наконец, мы можем подключиться к серверу Galaxy: gi = GalaxyInstance(url=rest_url, key=api_key) gi.verify = False

4. Теперь мы перечислим все доступные histories: histories = gi.histories print('Existing histories:') for history in histories.get_histories(): if history['name'] == history_name: histories.delete_history(history['id']) print(' - ' + history['name']) print()

При первом выполнении вы получите безымянную историю, но при других исполнениях вы также получите bioinf_example, который на этом этапе мы удалим, чтобы начать с чистого листа. 5. После этого мы создаем историю bioinf_example: ds_history = histories.create_history(history_name)

Если вы хотите, то можете проверить в  веб-интерфейсе, и  вы найдете там новую историю. 6. Сейчас мы собираемся загрузить файл; для этого требуется SFTPсоединение. Файл поставляется с этим кодом: print('Uploading file') transport = paramiko.Transport((server, sftp_port)) transport.connect(None, user, password) sftp = paramiko.SFTPClient.from_transport(transport) sftp.put('LCT.bed', 'LCT.bed') sftp.close() transport.close()

7. Теперь мы скажем Galaxy загрузить файл с FTP-сервера во внутреннюю базу данных: gi.tools.upload_from_ftp('LCT.bed', ds_history['id'])

Доступ к Galaxy с помощью API    269 8. Подытожим содержание нашей истории: def summarize_contents(contents): summary = defaultdict(list) for item in contents: summary['íd'].append(item['id']) summary['híd'].append(item['hid']) summary['name'].append(item['name']) summary['type'].append(item['type']) summary['extension']. append(item['extension']) return pd.DataFrame.from_dict(summary) print('History contents:') pd_contents = summarize_contents(contents) print(pd_contents) print()

У нас есть только один ввод: íd

0

híd

name

f2db41e1fa331b3e

type extension

1

LCT.bed

file

9. Давайте проверим метаданные для нашего файла BED: print('Metadata for LCT.bed') bed_ds = contents[0] pp.pprint(bed_ds) print()

Результат состоит из следующего: {'create_time': '2018-11-28T21:27:28.952118', 'dataset_id': 'f2db41e1fa331b3e', 'deleted': False, 'extension': 'auto', 'hid': 1, 'history_content_type': 'dataset', 'history_id': 'f2db41e1fa331b3e', 'id': 'f2db41e1fa331b3e', 'name': 'LCT.bed', 'purged': False,

auto

270    Конвейеры биоинформатики 'state': 'queued', 'tags': [], 'type': 'file', 'type_id': 'dataset-f2db41e1fa331b3e', 'update_time': '2018-11-28T21:27:29.149933', 'url': '/api/histories/f2db41e1fa331b3e/contents/f2db41e1fa331b3e', 'visible': True}

10. Обратим внимание на существующие на сервере инструменты и получим метаданные о них: print('Metadata about all tools') all_tools = gi.tools.get_tools() pp.pprint(all_tools) print()

Это распечатает длинный список инструментов. 11. Теперь давайте получим некоторую информацию о нашем инструменте: bed2gff = gi.tools.get_tools(name='Convert BED to GFF') [0] print("Converter metadata:") pp.pprint(gi.tools.show_tool(bed2gff['id'], io_details=True, link_ details=True)) print()

Имя инструмента было доступно на предыдущем шаге. Обратите внимание, что мы получаем первый элемент списка, поскольку теоретически может быть установлено более одной версии инструмента. Сокращенный вывод выглядит таким образом: {'config_file': '/galaxy-central/lib/galaxy/datatypes/converters/bed_to_ gff_converter.xml', 'id': 'CONVERTER_bed_to_gff_0', 'inputs': [{'argument': None, 'edam': {'edam_data': ['data_3002'], 'edam_formats': ['format_3003']}, 'extensions': ['bed'], 'label': 'Choose BED file', 'multiple': False, 'name': 'input1', 'optional': False, 'type': 'data',

Доступ к Galaxy с помощью API    271 'value': None}], 'labels': [], 'link': '/tool_runner?tool_id=CONVERTER_bed_to_gff_0', 'min_width': -1, 'model_class': 'Tool', 'name': 'Convert BED to GFF', 'outputs': [{'edam_data': 'data_1255', 'edam_format': 'format_2305', 'format': 'gff', 'hidden': False, 'model_class': 'ToolOutput', 'name': 'output1'}], 'panel_section_id': None, 'panel_section_name': None, 'target': 'galaxy_main', 'version': '2.0.0'}

12. Наконец, давайте запустим инструмент для преобразования нашего файла BED в GFF: def dataset_to_param(dataset): return dict(src='hda', id=dataset['id']) tool_inputs = { 'input1': dataset_to_param(bed_ds) } gi.tools.run_tool(ds_history['id'], bed2gff['id'], tool_inputs=tool_ inputs)

Параметры инструмента можно проверить на предыдущем шаге. Если вы зайдете в веб-интерфейс, то увидите примерно следующее:

272    Конвейеры биоинформатики

Рис. 9.2. Проверка результатов работы нашего скрипта через веб-интерфейс Galaxy

Таким образом, мы получили доступ к Galaxy, используя его REST API.

Развертывание конвейера анализа вариантов с помощью Snakemake Galaxy в основном ориентирован на пользователей, не особенно вовлеченных в программирование. Знание того, как с ним обращаться, даже если вы предпочитаете более удобную для программиста среду, важно из-за его распространенности. Замечательно то, что существует API для взаимодействия с Galaxy. Но если вам нужен конвейер, более удобный для программиста, есть много доступных альтернатив. В данной главе мы исследуем два широко используемых конвейера, удобных для программистов: snakemake и Nextflow. В этом рецепте мы рассматриваем snakemake. Snakemake реализован на Python и имеет много общего с ним. При этом его основным источником вдохновения является Makefile, структура, используемая почтенной системой сборки make. Здесь мы разработаем мини-конвейер анализа вариантов с  помощью snakemake. Цель здесь не в том, чтобы правильно понять научную часть – мы рассмотрим это в других главах, – а в том, чтобы увидеть, как создавать конвейеры, используя snakemake. Наш мини-конвейер загрузит данные HapMap, сделает их подвыборку в 1 %, произведет простой PCA и напечатает результат.

Подготовка Вам понадобится Plink 2, установленный вместе с snakemake. Чтобы отобразить стратегии исполнения, вам также понадобится Graphviz для печати результата. Определим следующие задачи. 1. Загрузка данных. 2. Их распаковка. 3. Подвыборка их в 1 %. 4. Вычисление PCA на подвыборке в 1 %. 5. Построение диаграмм PCA.

Развертывание конвейера анализа вариантов с помощью Snakemake    273 Наш рецепт конвейера будет состоять из двух частей: фактического кодирования конвейера в snakemake и скрипта поддержки на Python. Код snakemake для этого можно найти в  Chapter09/snakemake/Snakefile, тогда как скрипт поддержки Python находится в Chapter09/snakemake/plot_pca.py.

Как это сделать… 1. Первая задача – загрузка данных: from snakemake.remote.HTTP import RemoteProvider as HTTPRemoteProvider HTTP = HTTPRemoteProvider() download_root = "https://ftp.ncbi.nlm.nih.gov/hapmap/genotypes/hapmap3_r3" remote_hapmap_map = f"{download_root}/plink_format/hapmap3_r3_b36_fwd. consensus.qc.poly.map.gz" remote_hapmap_ped = f"{download_root}/plink_format/hapmap3_r3_b36_fwd. consensus.qc.poly.ped.gz" remote_hapmap_rel = f"{download_root}/relationships_w_pops_041510.txt" rule plink_download: input: map=HTTP.remote(remote_hapmap_map, keep_local=True), ped=HTTP.remote(remote_hapmap_ped, keep_local=True), rel=HTTP.remote(remote_hapmap_rel, keep_local=True) output: map="scratch/hapmap.map.gz", ped="scratch/hapmap.ped.gz", rel="data/relationships.txt" shell: "mv {input.map} {output.map};" "mv {input.ped} {output.ped};" "mv {input.rel} {output.rel}"

Язык Snakemake зависит от Python, как вы можете видеть с самых первых строк, которые легко понять, зная Python. Главное здесь – это «rule» (rule engine). У этого «движка правил» есть набор вводных потоков, которые в нашем случае обрабатываются через HTTP.remote, поскольку мы имеем дело с удаленно размещенными файлами, за которыми следует вывод. Мы помещаем два файла (те, которые еще не сжаты) в рабочий каталог scratch и один в каталог data. Наконец, наш код конвейера представляет собой простой скрипт оболочки, который перемещает загруженные файлы HTTP в их окончательное местоположение. Обратите внимание, как скрипт оболочки относится к вводам и выводам.

274    Конвейеры биоинформатики 2. С помощью этого скрипта загрузка файлов очень проста. Запустите в командной строке следующее: snakemake -c1 data/relationships.txt

Это говорит snakemake, что вы хотите материализовать data/ relationships.txt. Мы будем использовать одно ядро, -c1. Поскольку это результат действия plink_download движка правил, rule engine будет запущен (разве что файл уже недоступен – в этом случае snakemake ничего не сделает). Вот сокращенная версия вывода: Building DAG of jobs... Using shell: /usr/bin/bash Provided cores: 1 (use --cores to define parallelism) Rules claiming more threads will be scaled down. Job stats: job

count

min threads

max threads

-------------- ------- ------------- ------------plink_download

1

1

1

total

1

1

1

Select jobs to execute... [Mon Jun 13 18:54:26 2022] rule plink_download: input: ftp.ncbi.nlm.nih.gov/hapmap/ge [...] output: [..], data/relationships.txt jobid: 0 reason: Missing output files: data/relationships.txt resources: tmpdir=/tmp Downloading from remote: [...]relationships_w_pops_041510.txt Finished download. [...] Finished job 0. 1 of 1 steps (100%) done

Snakemake предоставляет некоторую информацию о том, какие задания будут выполняться, и запускает их. 3. Теперь, когда у нас есть данные, давайте посмотрим на rule их распаковки: PLINKEXTS = ['ped', 'map'] rule uncompress_plink:

Развертывание конвейера анализа вариантов с помощью Snakemake    275 input: "scratch/hapmap.{plinkext}.gz" output: "data/hapmap.{plinkext}" shell: "gzip -dc {input} > {output}"

Наиболее интересной особенностью здесь является то, что мы можем указать несколько файлов для загрузки. Обратите внимание, как список PLINKEXTS преобразуется в коде в отдельные элементы plinkext. Вы можете выполнить это, запросив вывод из rule engine. 4. Теперь давайте сделаем подвыборку наших данных в 1 %: rule subsample_1p: input: "data/hapmap.ped", "data/hapmap.map" output: "data/hapmap1.ped", "data/hapmap1.map" run: shell(f"plink2 --pedmap {input[0][:-4]} --out{output[0][:-4]} --thin 0.01 --geno 0.1 --export ped")

Новый контент находится в последних двух строках: мы не используем script, а запускаем run. Это сообщает snakemake, что выполнение основано на Python с несколькими доступными дополнительными функциями. Здесь мы видим функцию оболочки, которая выполняет скрипт оболочки. Строка представляет собой f-строку Python – обратите внимание на ссылку на snakemake input и выходные переменные output в строке. Вы можете поместить сюда более сложный код Python, например итерировать входные данные. Совет Здесь мы предполагаем, что Plink доступен, так как мы предварительно установили его, но snakemake предоставляет некоторые функции для работы с  зависимостями. В  частности, правила snakemake могут быть аннотированы файлом YAML, указывающим на зависимости conda.

276    Конвейеры биоинформатики 5. Теперь, когда у  нас есть подвыборка данных, давайте вычислим PCA. В этом случае мы будем использовать внутреннюю структуру PCA Plink для выполнения вычислений: rule plink_pca: input: "data/hapmap1.ped", "data/hapmap1.map" output: "data/hapmap1.eigenvec", "data/hapmap1.eigenval" shell: "plink2 --pca --file data/hapmap1 -out data/hapmap1"

6. Как и в большинстве конвейерных систем, snakemake строит направленный ациклический граф (DAG) операций для выполнения. В любой момент вы можете попросить snakemake представить вам DAG того, что вы будете выполнять для создания вашего запроса. Например, чтобы сгенерировать PCA, используйте следующее: snakemake --dag data/hapmap1.eigenvec | dot -Tsvg > bio.svg

Это сгенерирует такую схему:

Рис. 9.3. Граф DAG для вычисления PCA

Развертывание конвейера анализа вариантов с помощью Snakemake    277 7. Наконец, давайте сгенерируем rule plot для PCA: rule plot_pca: input: "data/hapmap1.eigenvec", "data/hapmap1.eigenval" output: "pca.png" script: "./plot_pca.py"

Rule plot вводит новый тип исполнения – script. В этом случае для обработки rule вызывается внешний скрипт Python. 8. Наш скрипт Python для создания диаграммы выглядит следующим образом: import pandas as pd eigen_fname = snakemake.input[0] if snakemake.input[0]. endswith('eigenvec') else snakemake.input[1] pca_df = pd.read_csv(eigen_fname, sep='\t') ax = pca_df.plot.scatter(x=2, y=3, figsize=(16, 9)) ax.figure. savefig(snakemake.output[0])

Скрипт Python имеет доступ к объекту snakemake. Этот объект раскрывает контент правила: обратите внимание, как мы используем input для получения данных PCA и output для создания изображения. 9. Наконец, код для создания грубой диаграммы выглядит, как показано на рис. 9.4.

Дополнительно Предыдущий рецепт был создан для работы на простой конфигурации snakemake. Есть много других способов построения rule в snakemake. Самая важная проблема, которую мы не  обсуждали,  – это тот факт, что snakemake может выполнять код во многих различных средах, от локального компьютера (как в нашем случае) до локальных кластеров и облака. Было бы неразумно просить о чем-то большем, чем использовать локальный компьютер для опробования snakemake, но не  забывайте, что snakemake может управлять сложными вычислительными средами. Помните, что snakemake, реализованный на Python, концептуально основан на make. Это субъективный анализ, чтобы решить, нравится ли вам разработка (snake)make. Для изучения альтернативного подхода рассмотрите следующий рецепт, в котором используется Nextflow.

278    Конвейеры биоинформатики

Рис. 9.4. Очень грубый PCA, созданный конвейером Snakemake

Развертывание конвейера анализа вариантов с помощью Nextflow В биоинформатике есть два основных игрока на рынке конвейерных фреймворков: snakemake и Nextflow. Они обеспечивают функциональность конвейера, но имеют разные подходы к разработке. Snakemake основан на Python, но его язык и  философия исходят из инструмента make, используемого для компиляции сложных программ со взаимосвязями. Nextflow основан на Java (точнее, реализован на Groovy – языке, который работает на самом верху виртуальной машины Java) и  имеет свой собственный предметно-ориентированный язык (DSL) для реализации конвейеров. Основная цель этого рецепта (и предыдущего рецепта) – дать вам представление о Nextflow, чтобы вы могли сравнить его со snakemake и выбрать тот, который лучше соответствует вашим потребностям. Совет Существует много точек зрения на то, как оценивать конвейерную систему. Здесь мы представляем перспективу, основанную на языке, используемом для описания конвейера. Однако есть и другие подходы, которые следует учитывать при выборе конвейерной системы. Например, насколько хорошо это поддерживает вашу среду выполнения (такую как локальный клас­ тер или облако), поддерживает ли он ваши инструменты (или позволяет легко разрабатывать расширения для работы с новыми инструментами) и обеспечивает ли он хорошее восстановление и функции мониторинга?

Развертывание конвейера анализа вариантов с помощью Nextflow    279 В этом рецепте мы разработаем конвейер с Nextflow, который обеспечивает ту же функциональность, которую мы реализовали с помощью snakemake, что позволяет провести справедливое сравнение с точки зрения разработки конвейера. Цель здесь не в том, чтобы правильно понять научную часть – мы рассмотрим это в других главах, – а в том, чтобы увидеть, как создавать конвейеры с помощью snakemake. Наш мини-конвейер будет загружать данные HapMap, выполнять их подвыборку до 1 %, делать простой PCA и превращать их в диаграммы.

Подготовка Вам понадобится Plink 2, установленный вместе с Nextflow. Сам Nextflow требует некоторого программного обеспечения из пространства Java: особенно Java Runtime Environment и Groovy. Определим следующие задачи. 1. Загрузка данных. 2. Их распаковка. 3. Подвыборка в 1 %. 4. Вычисление PCA на подвыборке в 1 %. 5. Распечатка диаграммы PCA. Код Nextflow для этого можно найти в Chapter09/nextflow/pipeline.nf.

Как это сделать… 1. Первая задача – загрузка данных: nextflow.enable.dsl=2 download_root = "https://ftp.ncbi.nlm.nih.gov/hapmap/genotypes/hapmap3_ r3" process plink_download { output: path 'hapmap.map.gz' path 'hapmap.ped.gz' script: """ Wget $download_root/plink_format/hapmap3_r3_b36_fwd.consensus. qc.poly.map.gz -O hapmap.map.gz wget $download_root/plink_format/hapmap3_r3_b36_fwd.consensus. qc.poly.ped.gz -O hapmap.ped.gz """ }

280    Конвейеры биоинформатики Помните, что базовым языком для конвейера является не Python, а Groovy, поэтому синтаксис будет немного отличаться, например использованием фигурных скобок для блоков или игнорированием отступов. Мы создаем процесс (строительный блок конвейера в Nextflow) с именем plink_download, который загружает файлы Plink. Он  определяет только выводы. Первым выводом будет hapmap.map.gz, а  второй будет hapmap.ped.gz. Этот процесс будет иметь два выходных канала (еще одна концепция Nextflow, похожая на поток), которые могут быть использованы другим процессом. Код процесса по умолчанию представляет собой bash-скрипт. Важно отметить, как скрипт выводит файлы с именами, которые синхронизированы с разделом вывода. Также посмотрите, как мы ссылаемся на переменные, определенные в конвейере (в нашем случае это download_root). 2. Давайте теперь определим процесс для использования каналов с файлами HapMap и их распаковки: process uncompress_plink { publishDir 'data', glob: '*', mode: 'copy' input: path mapgz path pedgz output: path 'hapmap.map' path 'hapmap.ped' script: """ gzip -dc $mapgz > hapmap.map gzip -dc $pedgz > hapmap.ped """ }

В этом процессе следует отметить три момента: теперь у нас есть пара входных данных (помните, что у нас есть пара выходных данных из предыдущего процесса). Наш скрипт теперь также обращается к  входным переменным ($mapgz и $pedgz). Наконец, мы публикуем вывод с помощью publishDir. Поэтому любые неопубликованные файлы будут храниться только временно. 3. Давайте укажем первую версию рабочего процесса, которая загружает и распаковывает файлы: workflow { plink_download | uncompress_plink }

Развертывание конвейера анализа вариантов с помощью Nextflow    281 4. Мы можем выполнить рабочий процесс, запустив в оболочке следующее: nextflow run pipeline.nf -resume

Флаг resume в конце гарантирует, что конвейер продолжится с того шага, который уже был завершен. Шаги при локальном выполнении сохраняются в рабочем каталоге. 5. Если мы удалим каталог work, нам не  нужно будет загружать файлы HapMap, если они уже были опубликованы. Поскольку это находится за пределами рабочего каталога work и, следовательно, не  отслеживается напрямую, нам нужно изменить рабочий процесс, чтобы отслеживать данные в опубликованном каталоге: workflow { ped_file = file('data/hapmap.ped') map_file = file('data/hapmap.map') if (!ped_file.exists() | !map_file.exists()) { link_download | uncompress_plink } }

Есть альтернативные способы сделать это, но я  хотел представить немного кода Groovy, так как иногда вам может понадобиться писать код на Groovy. Как вы скоро увидите, есть способы использовать код Python. 6. Теперь нам нужно выполнить подвыборку данных: process subsample_1p { input: path 'hapmap.map' path 'hapmap.ped' output: path 'hapmap1.map' path 'hapmap1.ped' script: """ plink2 --pedmap hapmap --out hapmap1 --thin 0.01 –geno 0.1 --export ped """ }

7. Давайте теперь вычислим PCA с помощью Plink:

282    Конвейеры биоинформатики process plink_pca { input: path 'hapmap.map' path 'hapmap.ped' output: path 'hapmap.eigenvec' path 'hapmap.eigenval' script: """ plink2 --pca --pedmap hapmap -out hapmap """ }

8. Наконец, построим PCA: process plot_pca { publishDir '.', glob: '*', mode: 'copy' input: path 'hapmap.eigenvec' path 'hapmap.eigenval' output: path 'pca.png' script: """ #!/usr/bin/env python import pandas as pd pca_df = pd.read_csv('hapmap.eigenvec', sep='\t') ax = pca_df.plot.scatter(x=2, y=3, figsize=(16, 9)) ax.figure.savefig('pca. png') """ }

Новая особенность этого кода заключается в  том, что мы указываем bash-скрипт с помощью shebang-оператора (#!), который позволяет нам вызывать внешний язык скриптов для обработки данных. Вот наш последний рабочий процесс:

Развертывание конвейера анализа вариантов с помощью Nextflow    283 workflow { ped_file = file('data/hapmap.ped') map_file = file('data/hapmap.map') if (!ped_file.exists() | !map_file.exists()) { plink_download | uncompress_plink | subsample_1p | plink_pca | plot_pca } else { subsample_1p(Channel.fromPath('data/hapmap.map'), Channel.fromPath('data/hapmap.ped')) | plink_pca | plot_pca } }

Мы либо загружаем данные, либо используем уже загруженные данные. Хотя существуют и  другие диалекты для разработки полного рабочего процесса, я  хотел бы, чтобы вы обратили внимание на то, как мы используем subsample_1p, когда файлы доступны; мы можем явно передать процессу два канала. 9. Мы можем запустить конвейер и запросить HTML-отчет о выполнении: nextflow run pipeline.nf -with-report report/report.html

Отчет довольно исчерпывающий и позволит вам определить самые затратные части конвейера с  разных точек зрения, независимо от того, связаны ли они со временем, памятью, задействованием CPU или вводом-выводом.

Дополнительно Это был простой вводный пример Nextflow, который, как мы надеемся, позволит вам получить представление о фреймворке, особенно для того, чтобы вы могли сравнить его со snakemake. Nextflow имеет гораздо больше функций, и мы рекомендуем вам посетить его веб-сайт. Как и в случае со snakemake, самая важная проблема, которую мы не обсуждали, – это тот факт, что Nextflow может выполнять код во многих различных средах, от локального компьютера, локальных кластеров до облака. Ознакомьтесь с  документацией Nextflow, чтобы узнать, какие вычислительные среды поддерживаются в настоящее время. Каким бы важным ни был базовый язык, Groovy с  Nextflow и  Python со snakemake, обязательно сравните другие факторы. Это сравнение должно включить в себя не только то, где могут выполняться оба конвейера (локально, в кластере или в облаке), но и функциональную конструкцию фреймворка, поскольку они используют совершенно разные парадигмы.

Глава 10

Машинное обучение в биоинформатике Машинное обучение1 используется в самых разных отраслях науки, и вычислительная биология не является исключением. В этой области машинное обучение имеет бесчисленное множество приложений, скорее всего, самым старым и самым известным является использование анализа основных компонентов (Principal Components Analysis, PCA) для изучения структуры популяции с использованием геномики. Есть много других приложений, куда это должно распространиться, поскольку это растущая, постоянно расширяющаяся область. В этой главе мы собираемся представить вам концепции машинного обучения с точки зрения биоинформатики. Учитывая, что машинное обучение  – очень сложная тема, которой легко можно было бы посвятить целую книгу, здесь мы намерены использовать интуитивный подход, который позволит вам в общих чертах понять, как некоторые методы машинного обучения могут быть полезны для решения биологических проблем. Если вы найдете эти методы полезными, то поймете основные концепции и сможете перейти к более подробной литературе. Если вы используете Docker и поскольку все библиотеки в этой главе необходимы для анализа данных, их все можно найти в образе Docker tiagoantao/ bioinformatics_ml. В этой главе мы рассмотрим следующие рецепты:  знакомство с scikit-learn на примере PCA;  использование кластеризации по PCA для классификации образцов;  изучение признаков рака молочной железы с  помощью деревьев принятий решений (Decision Trees);  прогнозирование исходов рака молочной железы с  помощью методов случайного леса (Random Forests).

Машинное обучение – это отрасль искусственного интеллекта и компьютерных наук, которая фокусируется на использовании данных и алгоритмов для имитации того, как люди учатся, постепенно повышая точность решения. Его характерной чертой является не  прямое решение задачи, а  обучение за счёт использования решений множества сходных задач. – Прим. ред.

1

Знакомство со scikit-learn на примере PCA    285

Знакомство со scikit-learn на примере PCA PCA  – это статистическая процедура, которая используется для уменьшения размерности ряда переменных до меньшего линейно некоррелированного подмножества. В главе 6 мы видели реализацию PCA, основанную на использовании внешнего приложения. В этом рецепте мы реализуем тот же PCA для популяционной генетики, но будем использовать библиотеку scikit-learn. Scikit-learn – один из фундаментальных инструментов библиотеки для машинного обучения Python, и данный рецепт является введением в эту библиотеку. PCA – это форма машинного обучения без учителя – мы не предоставляем информацию о классе образца. Мы обсудим контролируемые техники в других рецептах этой главы. Напоминаем, что мы вычислим PCA для 11 человеческих популяций из проекта HapMap.

Подготовка Вам потребуется запустить первый рецепт из главы 6, чтобы сгенерировать PLINKфайл hapmap10_auto_ noofs_ld_12 (с аллелями, записанными как 1 и 2). С точки зрения популяционной генетики, для получения надежного PCA нам нужны LDобрезанные маркеры. Мы не будем рисковать, используя здесь потомство, потому что это, скорее всего, исказит результат. Для нашего рецепта потребуется биб­ лиотека pygenomics, которую можно установить с помощью следующей команды: pip install pygenomics

Код находится в записной книжке Chapter10/PCA.py.

Как это сделать... Рассмотрим следующие шаги. 1. Начинаем с загрузки метаданных для наших образцов. В нашем случае мы будем загружать данные человеческой популяции, к  которой принадлежит каждый образец: import os from sklearn.decomposition import PCA import numpy as np from genomics.popgen.pca import plot f = open('../Chapter06/relationships_w_pops_041510.txt') ind_pop = {} f.readline() # header for l in f: toks = l.rstrip().split('\t') fam_id = toks[0] ind_id = toks[1]

286    Машинное обучение в биоинформатике pop = toks[-1] ind_pop['/'.join([fam_id, ind_id])] = pop f.close()

2. Теперь получим порядок индивидов вместе с номером SNP, которые мы будем обрабатывать: f = open('../Chapter06/hapmap10_auto_noofs_ld_12.ped') ninds = 0 ind_order = [] for line in f: ninds += 1 toks = line[:100].replace(' ', '\t').split('\t') fam_id = toks[0] ind_id = toks[1] ind_order.append('%s/%s' % (fam_id, ind_id)) nsnps = (len(line.replace(' ', '\t').split('\t')) - 6) // 2 f.close()

3. Создаем массив, который будет подаваться на PCA: pca_array = np.empty((ninds, nsnps), dtype=int) print(pca_array.shape) f = open('../Chapter06/hapmap10_auto_noofs_ld_12.ped') for ind, line in enumerate(f): snps = line.replace(' ', '\t').split('\t')[6:] for pos in range(len(snps) // 2): a1 = int(snps[2 * pos]) a2 = int(snps[2 * pos]) my_code = a1 + a2 - 2 pca_array[ind, pos] = my_code f.close()

4. Наконец, мы вычисляем PCA до восьми компонентов. Затем получаем 8-мерные координаты для всех образцов, используя метод transform. my_pca = PCA (n_components=8) my_pca.fit(pca_array) trans = my_pca.transform(pca_array)

5. Наконец, строим диаграмму PCA:

Использование кластеризации по PCA для классификации образцов    287 sc_ind_comp = {} for i, ind_pca in enumerate(trans): sc_ind_comp[ind_order[i]] = ind_pca plot.render_pca_eight(sc_ind_comp, cluster=ind_pop)

Рис. 10.1. Диаграммы PC1–PC8 для нашего набора данных, созданные scikit-learn

Дополнительно... Для публикации в научных журналах я бы рекомендовал использовать рецепт из главы 6 просто потому, что он основан на опубликованном и высоко оцененном методе. При этом результаты этого кода качественно схожи, а данные группируются очень похожим образом (инверсия направления по вертикальной оси, если вы сравните ее с рисунком в главе 6, не имеет значения при интерпретации диаграммы PCA).

Использование кластеризации по PCA для классификации образцов PCA в геномике позволяет нам увидеть, как образцы кластеризуются. Во многих случаях индивиды из одной и той же популяции будут находиться в одной и той же области диаграммы. Но мы хотели бы пойти дальше и предсказать, куда с точки зрения популяции должны попасть вновь рассматриваемые индивиды. Для этого мы начнем с данных PCA, так как они уменьшают размерность, упрощая работу с данными, а затем применим алгоритм кластеризации K-средних1, чтобы предсказать, куда попадают новые образцы. Мы будем ис Метод кластеризации, основанный на минимизации суммарного квадратичного отклонения образцов относительно центров кластеров. – Прим. ред.

1

288    Машинное обучение в биоинформатике пользовать тот же набор данных, что и в рецепте выше. Мы будем использовать все наши образцы, за исключением одного, для обучения алгоритма, а затем сделаем предсказание, куда попадает оставшийся образец. Кластеризация K-средних может быть примером контролируемого алгоритма (алгоритма с супервизором, учителем). В этих типах алгоритмов нам нужен обучающий набор данных, чтобы алгоритм мог учиться. После обучения алгоритма он сможет предсказывать определенный результат для новых образцов. В нашем случае мы надеемся, что сможем предсказать популяцию, куда он попадет. Предупреждение Текущий рецепт призван служить мягким введением в  алгоритмы контролируемого обучения и концепции, лежащие в их основе. Способ, которым мы обучаем алгоритм, далек от оптимального. Проблема правильного обучения такого алгоритма будет упомянута в  последнем рецепте этой главы.

Подготовка Мы будем использовать те же данные, что и в предыдущем рецепте. Код этого рецепта можно найти в Chapter10/Clustering.py.

Как это сделать... Давайте рассмотрим следующие шаги. 1. Начнем с загрузки информации о популяции – это похоже на то, что мы делали в предыдущем рецепте: import os import matplotlib.pyplot as plt from sklearn.cluster import KMeans from sklearn.decomposition import PCA import numpy as np from genomics.popgen.pca import plot f = open('../Chapter06/relationships_w_pops_041510.txt') ind_pop = {} f.readline() # header for l in f: toks = l.rstrip().split('\t') fam_id = toks[0] ind_id = toks[1]

Использование кластеризации по PCA для классификации образцов    289 pop = toks[-1] ind_pop['/'.join([fam_id, ind_id])] = pop f.close() f = open('../Chapter06/hapmap10_auto_noofs_ld_12.ped') ninds = 0 ind_order = [] for line in f: ninds += 1 toks = line[:100].replace(' ', '\t').split('\t') # for speed fam_id = toks[0] ind_id = toks[1] ind_order.append('%s/%s' % (fam_id, ind_id)) nsnps = (len(line.replace(' ', '\t').split('\t')) - 6) // 2 print (nsnps) f.close()

2. Теперь мы загружаем все образцы данных SNP в массив NumPy: all_array = np.empty((ninds, nsnps), dtype=int) f = open('../Chapter06/hapmap10_auto_noofs_ld_12.ped') for ind, line in enumerate(f): snps = line.replace(' ', '\t').split('\t')[6:] for pos in range(len(snps) // 2): a1 = int(snps[2 * pos]) a2 = int(snps[2 * pos]) my_code = a1 + a2 - 2 all_array[ind, pos] = my_code f.close()

3. Разделим массив на два набора данных, а именно: обучающий пример со всеми индивидами, кроме одного, и  случай для тестирования алгоритма с одним индивидом: predict_case = all_array[-1, :] pca_array = all_array[:-1,:] last_ind = ind_order[-1] last_ind, ind_pop[last_ind]

290    Машинное обучение в биоинформатике Наш тестовый случай – человек Y076/NA19124, который, как мы знаем, принадлежит к популяции народа йоруба. 4. Теперь мы вычисляем PCA для обучающего набора, который будем использовать для кластеризации K-средних: my_pca = PCA (n_components=2) my_pca.fit(pca_array) trans = my_pca.transform(pca_array) sc_ind_comp = {} for i, ind_pca in enumerate(trans): sc_ind_comp[ind_order[i]] = ind_pca plot.render_pca(sc_ind_comp, cluster=ind_pop)

Вот вывод, который будет полезен для проверки результатов кластеризации:

Рис. 10.2. PC1 и PC2 с популяциями, отмеченными разными цветами

5. Прежде чем мы начнем вычислять кластеризацию K-средних, давайте напишем функцию для построения поверхности кластеризации при запуске алгоритма: def plot_kmeans_pca(trans, kmeans): x_min, x_max = trans[:, 0].min() - 1, trans[:, 0].max() + 1 y_min, y_max = trans[:, 1].min() - 1, trans[:, 1].max() + 1 mesh_x, mesh_y = np.meshgrid(np.arange(x_min, x_max, 0.5), np.arange(y_min, y_max, 0.5))

Использование кластеризации по PCA для классификации образцов    291 k_surface = kmeans.predict(np.c_[mesh_x.ravel(), mesh_y.ravel()]). reshape(mesh_x.shape) fig, ax = plt.subplots(1,1, dpi=300) ax.imshow( k_surface, origin="lower", cmap=plt.cm.Pastel1, extent=(mesh_x.min(), mesh_x.max(), mesh_y.min(), mesh_y.max()), ) ax.plot(trans[:, 0], trans[:, 1], "k.", markersize=2) ax.set_ title("KMeans clustering of PCA data") ax.set_xlim(x_min, x_max) ax.set_ylim(y_min, y_max) ax.set_xticks(()) ax.set_yticks(()) return ax

6. Теперь настроим алгоритм по нашим образцам. Поскольку у нас 11 популяций, мы будем обучать по 11 кластерам: kmeans11 = KMeans(n_clusters=11).fit(trans) plot_kmeans_pca(trans, kmeans11)

Вот результат:

Рис. 10.3. Поверхность кластеров для 11 кластеров

Если вы сравните с  рисунком здесь, то можете интуитивно увидеть, что клас­теризация не имеет большого смысла: она не очень хорошо сопоставляется с известными популяциями. Можно подумать, что этот алгоритм кластеризации с 11 кластерами не очень хорош.

292    Машинное обучение в биоинформатике Совет В scikit-learn реализовано много других алгоритмов кластеризации, и в некоторых сценариях они могут работать лучше, чем метод K-средних. Вы можете найти эти алгоритмы на https://scikit-learn. org/ stable/modules/clustering.html. Тем не менее сомнительно, что в данном конкретном случае какая-либо альтернатива для 11 кластеров будет работать намного лучше.

7. Хотя кажется, что кластеризация K-средних не может разделить 11 популяций, возможно, она все же может дать некоторые прогнозы, если мы используем другое количество кластеров. Просто взглянув на диаграмму, мы видим четыре отдельных сгустка. Каков был бы результат, если бы мы использовали четыре кластера? kmeans4 = KMeans(n_clusters=4).fit(trans) plot_kmeans_pca(trans, kmeans4)

Вот результат:

Рис. 10.4. Поверхность кластеров при анализе четырех кластеров

Четыре группы теперь разделены достаточно явно. Но  имеют ли они интуитивный смысл? Если это так, мы можем использовать этот метод кластеризации. И на самом деле они имеют смысл. Кластер слева состоит из африканского населения, верхний – кластер европейцев, а нижний – кластер жителей Восточной Азии. Средний является наиболее загадоч-

Изучение признаков рака молочной железы с помощью деревьев...    293 ным, поскольку он содержит как гуджаратских, так и мексиканских потомков, но эта смесь исходит от PCA и не вызвана самой кластеризацией. 8. Давайте посмотрим, как работает предсказание для единственного случая, который мы пропустили: pca_predict = my_pca.transform([predict_case]) kmeans4.predict(pca_predict)

Прогнозируется, что наш образец окажется в  кластере 1. Теперь нам нужно копнуть немного глубже. 9. Давайте выясним, что означает кластер 1. Берем из обучающей выборки последнюю особь, тоже йоруба, и смотрим, к какому кластеру он отнесен: last_train = ind_order[-2] last_train, ind_pop[last_train]

kmeans4.predict(trans)[0] Это действительно кластер 1, поэтому наше предсказание оказалось верным.

Дополнительно... Стоит повторить, что мы пытаемся достичь интуитивного понимания, что такое машинное обучение. На этом этапе вы должны иметь представление о том, что вы можете получить от контролируемого обучения, а также пример использования алгоритма кластеризации. О процедуре обучения этого алгоритма можно сказать гораздо больше, и мы частично раскроем это в последнем рецепте.

Изучение признаков рака молочной железы с помощью деревьев принятий решений1

Одна из первых проблем, с которой мы сталкиваемся, когда получаем набор данных, – решить, с чего начать наш анализ. В самом начале довольно часто возникает чувство растерянности по поводу того, что делать в первую очередь. Здесь мы представим исследовательский подход, основанный на деревьях решений (decision trees). Большое преимущество деревьев решений заключается в том, что они дают нам правила, по которым они же и строятся, что позволяет нам получить изначальное представление о том, что же происходит с нашими данными. В этом примере мы будем использовать набор данных о генетических особенностях пациентов с  раком молочной железы. Набор данных с  699 входными полями данных (входами) включает такую информацию, как толщина сгустка, разброс размера клеток или тип хроматина. Выходом является либо доброкачественная, либо злокачественная опухоль. Функции кодируются значениями от 0 до 10. Более подробную информацию о проекте можно найти на http://archive.ics.uci.edu/ml/datasets/breast+cancer+wisconsin+%28diagnostic%29. Деревья принятия решений являются одним из наиболее эффективных инструментов интеллектуального анализа данных и предсказательной аналитики, которые позволяют решать задачи классификации и регрессии. – Прим. ред.

1

294    Машинное обучение в биоинформатике

Подготовка Мы собираемся загрузить данные вместе с документацией: wget http://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancerwisconsin/breast-cancer-wisconsin.data wget http://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancerwisconsin/breast-cancer-wisconsin.names

Файл данных отформатирован как файл CSV. Информацию о  содержании можно найти во втором загруженном файле. Код этого рецепта можно найти в Chapter10/Decision_Tree.py.

Как это сделать... Сделаем следующие шаги. 1. Первое, что мы делаем, – это удаляем небольшую часть лиц, имеющих неполные данные: import numpy as np import matplotlib.pyplot as plt import pandas as pd from sklearn import tree f = open('breast-cancer-wisconsin.data') w = open('clean.data', 'w') for line in f: if line.find('?') > -1: continue w.write(line) f.close() w.close()

Совет В этом случае достаточно удалить лиц с неполными данными, потому что они составляют небольшую часть набора данных, а мы проводим только исследовательский анализ. Для случаев с большим количеством отсутствующих данных или когда мы пытаемся сделать что-то более строгое, вам придется использовать методы для работы с учетом отсутствующих данных, которые мы не будем здесь рассматривать.

2. Теперь мы собираемся прочитать данные, дав имена всем столбцам: column_names = [ 'sample_id', 'clump_thickness', 'uniformity_cell_size',

Изучение признаков рака молочной железы с помощью деревьев...    295 'uniformity_cell shape', 'marginal_adhesion', 'single_epithelial_cell_size', 'bare_nuclei', 'bland_chromatin', 'normal_nucleoli', 'mitoses', 'class' ] samples = pd.read_csv('clean.data', header=None, names=column_names, index_col=0)

3. Теперь мы отделим особенности от результата и перекодируем результат, используя 0 и 1: training_input = samples.iloc[:,:-1] target = samples.iloc[:,-1].apply(lambda x: 0 if x == 2 else 1)

4. Далее создадим дерево решений на основе этих данных с максимальной глубиной 3: clf = tree.DecisionTreeClassifier(max_depth=3) clf.fit(training_input, target)

5. Давайте начнем с  того, что посмотрим, какие особенности являются наиболее важными: importances = pd.Series( clf.feature_importances_ * 100, index=training_input.columns).sort_values(ascending=False) importances

Ниже приведены особенности, ранжированные по важности: uniformity_cell_size 83.972870 uniformity_cell shape 7.592903 bare_nuclei 4.310045 clump_thickness 4.124183 marginal_adhesion 0.000000 single_epithelial_cell_size 0.000000 bland_chromatin 0.000000 normal_nucleoli 0.000000 mitoses 0.000000

Помните, что это всего лишь предварительный анализ. В следующем рецепте мы постараемся произвести более надежное ранжирование. Причина, по которой нижние функции равны нулю, заключается в том, что мы запросили максимальную глубину 3, и в этом случае возможно, что не все особенности учтены.

296    Машинное обучение в биоинформатике 6. Мы можем сделать нативный анализ точности нашей реализации: 100 * clf.score(training_input, target)

Получаем эффективность 96  %. Мы  не должны тестировать алгоритм с его собственным обучающим набором, поскольку он довольно цикличен. Мы вернемся к этому в следующем рецепте. 7. Наконец, давайте построим дерево решений: fig, ax = plt.subplots(1, dpi=300) tree.plot_tree(clf,ax=ax, feature_names=training_input.columns, class_ names=['Benign', 'Malignant'])

Это приводит к следующему результату:

Рис. 10.5. Дерево решений для набора данных по раку молочной железы

Для начала посмотрим на корневой узел: у него критерий uniformity_cell_ shape < 2.5 и классификация клеток как доброкачественных. Главной особенностью разбиения дерева является разброс размера клеток. Классификация их как доброкачественных в верхнем узле исходит просто из того факта, что большинство образцов в наборе данных являются доброкачественными. Теперь посмотрим на правый узел от корня: в нем 265 образцов, большинство из которых злокачественные и с критериями uniformity_cell_shape < 2.5. Эти правила позволяют получить первоначальное представление о том, что может управлять набором данных. Деревья решений не очень точны, поэтому используйте их только в качестве первого шага.

Прогнозирование диагностики рака молочной железы с использованием...    297

Прогнозирование диагностики рака молочной железы с использованием методов случайного леса Теперь мы собираемся предсказать результаты для некоторых пациентов, используя метод Random Forest. Случайный лес – это метод ансамблевого обучения (он будет использовать несколько других алгоритмов машинного обучения), который использует множество деревьев решений для получения надежных выводов о данных. Мы собираемся использовать тот же пример, что в  предыдущем рецепте: генетические особенности и постановка диагноза – рак молочной железы. У этого рецепта две основные цели: познакомить вас с методом случайного леса и вопросами, касающимися тренинга алгоритмов машинного обучения.

Подготовка Код этого рецепта можно найти в Chapter10/Random_Forest.py.

Как это сделать… Рассмотрим код. 1. Начинаем, как и в предыдущем рецепте, с избавления от образцов с недостающей информацией: import pandas as pd import numpy as np import pandas as pd from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.tree import export_graphviz f = open('breast-cancer-wisconsin.data') w = open('clean.data', 'w') for line in f: if line.find('?') > -1: continue w.write(line) f.close() w.close()

2. Теперь загружаем очищенные данные: column_names = [ 'sample_id', 'clump_thickness', 'uniformity_cell_size', 'uniformity_cell shape', 'marginal_adhesion', 'single_epithelial_cell_size', 'bare_nuclei',

298    Машинное обучение в биоинформатике 'bland_chromatin', 'normal_nucleoli', 'mitoses', 'class' ] samples = pd.read_csv('clean.data', header=None, names=column_names, index_col=0) samples

3. Разделяем загруженные данные на особенности и результаты: training_input = samples.iloc[:, :-1] target = samples.iloc[:, -1]

4. Создаем классификатор и устанавливаем его параметры: clf = RandomForestClassifier(max_depth=3, n_estimators=200) clf.fit(training_input, target)

Самый важный параметр здесь – это n_estimators: мы просим построить лес из 200 деревьев. 5. Теперь ранжируем особенности по степени важности: importances = pd.Series( clf.feature_importances_ * 100, index=training_input.columns).sort_values(ascending=False) importances

Вот результат: uniformity_cell_size 30.422515 uniformity_cell shape 21.522259 bare_nuclei 18.410346 single_epithelial_cell_size 10.959655 bland_chromatin 9.600714 clump_thickness 3.619585 normal_nucleoli 3.549669 marginal_adhesion 1.721133 mitoses 0.194124

Результат недетерминирован, а это означает, что у вас могут получиться разные результаты. Также обратите внимание, что случайный лес дает совсем другие числа, чем дерево решений предыдущего рецепта. Этого следовало ожидать, поскольку дерево решений представляет собой единую оценку, в которой лес имеет вес 200 деревьев и является более надежным.

Прогнозирование диагностики рака молочной железы с использованием...    299 6. Мы можем оценить точность этого результата: clf.score(training_input, target)

Я получаю результат 97,95 %. Вы можете получить немного другое значение, поскольку алгоритм является стохастическим. Как мы уже говорили в предыдущем рецепте, получение оценки точности из тренингового набора довольно циклично и является далеко не лучшей практикой. 7. Чтобы иметь более реалистичное представление о точности алгоритма, нам нужно разделить наши данные на две части – обучающую и тестовую выборки: for test_size in [0.01, 0.1, 0.2, 0.5, 0.8, 0.9, 0.99]: X_train, X_test, y_train, y_test = train_test_split(trainning_input, target, test_size=test_size) tclf = RandomForestClassifier(max_depth=3) tclf.fit(X_train, y_train) score = tclf.score(X_test, y_test) print(f'{1 - test_size:.1%} {score:.2%}')

Вывод следующий (помните, что вы получите другие значения): 99.0% 71.43% 90.0% 94.20% 80.0% 97.81% 50.0% 97.66% 20.0% 96.89% 10.0% 94.80% 1.0% 92.02%

Если вы делаете обучение только с  1  % данных, то получаете точность всего лишь 71 %, тогда как если вы обучаете с большим количеством данных, точность превысит 90 %. Обратите внимание, что точность не увеличивается монотонно с  размером обучающей выборки. Принятие решения о  размере тренингового набора  – сложное дело с  различными факторами, вызывающими неожиданные побочные эффекты.

Дополнительно... Мы лишь немного коснулись тренинга и тестирования алгоритмов машинного обучения. Например, контролируемые наборы данных обычно делятся на 3, а  не на 2 (обучение, тестирование и  перекрестная проверка). Есть еще много вопросов, которые вам нужно рассмотреть, чтобы обучить свой алгоритм и многие другие типы алгоритмов. В этой главе мы попытались развить вашу базовую интуицию для понимания машинного обучения, но это не более чем отправная точка, если вы собираетесь следовать по такому пути.

Глава 11

Параллельная обработка с Dask и Zarr Наборы данных биоинформатики растут с  экспоненциальной скоростью. Стратегии анализа данных, основанные на стандартных инструментах, таких как Pandas, предполагают, что наборы данных могут помещаться в  памяти компьютера (хотя и с некоторой возможностью для внешнего анализа) или что одна машина способна эффективно обрабатывать все данные. К  сожалению, это нереально для многих современных наборов данных. В этой главе мы представим две библиотеки, способные работать с  очень большими наборами данных и ресурсозатратными вычислениями:  Dask – это библиотека, позволяющая делать параллельные вычисления, базу которых можно расширить от одного компьютера до очень больших облачных и кластерных сред. Dask предоставляет интерфейсы, похожие на Pandas и NumPy, позволяя вам работать с большими наборами данных, разбросанными по многим компьютерам;  Zarr – это библиотека, в которой хранятся сжатые и разбитые на фрагменты многомерные массивы. Как мы увидим, эти массивы адаптированы для работы с очень большими наборами данных, обрабатываемыми в больших компьютерных кластерах, и в то же время могут обрабатывать данные на одном компьютере, если это необходимо. Наши рецепты познакомят вас с этими передовыми библиотеками на примере данных о геномике комаров. Вы должны смотреть на этот код как на отправную точку, чтобы встать на путь обработки больших наборов данных. Параллельная обработка больших наборов данных – сложная тема, и это только начало, а не конец вашего пути. Поскольку все эти библиотеки являются фундаментальными для анализа данных, их все можно найти в образе Docker, если вы используете его, на tiagoantao/bioinformatics_dask. В этой главе мы рассмотрим следующие рецепты:  чтение геномных данных с помощью Zarr;  параллельная обработка данных с  помощью многопроцессорной обработки Python;  использование Dask для обработки геномных данных на основе массивов NumPy;  планирование задач с помощью dask.distributed.

Чтение геномных данных с помощью Zarr    301

Чтение геномных данных с помощью Zarr Zarr (https://zarr.readthedocs.io/en/stable/) хранит данные на основе массива, такие как NumPy, в иерархической структуре на диске и в облачном хранилище. Структуры данных, используемые Zarr для представления массивов, не только очень компактны, но и допускают параллельное чтение и запись, как мы увидим в  следующих рецептах. В  этом рецепте мы будем читать и  обрабатывать геномные данные из проекта «Anopheles 1000 Genomes» («1000 геномов комара Anopheles gambiae») (https://malariagen.github.io/ vector-data/ag3/download.html). Здесь мы будем делать последовательную обработку, чтобы облегчить знакомство с Zarr; в следующем рецепте произведем параллельную обработку. Наш проект будет вычислять пропуски (missingness) для всех геномных позиций, секвенированных из одной хромосомы.

Подготовка Данные Anopheles 1000 Genomes доступны на Google Cloud Platform (GCP). Чтобы загрузить данные из GCP, вам понадобится gsutil, что доступно по адресу https://cloud.google.com/storage/docs/gsutil_install. После установки gsutil загрузите данные (~ 2 гигабайта (ГБ)) с помощью следующих строк кода: mkdir -p data/AG1000G-AO/ gsutil -m rsync -r \ -x '.*/calldata/(AD|GQ|MQ)/.*' \ gs://vo_agam_release/v3/snp_genotypes/all/AG1000G-AO/ \ data/AG1000G-AO/ > /dev/null

Мы загружаем подвыборку образцов из проекта. После загрузки данных код для их обработки можно найти в Chapter11/Zarr_Intro.py.

Как это сделать... Рассмотрим следующие шаги, чтобы начать. 1. Давайте начнем с проверки структуры, доступной внутри файла Zarr: import numpy as np import zarr mosquito = zarr.open('data/AG1000G-AO') print(mosquito.tree())

Мы начинаем с  открытия файла Zarr (как мы скоро увидим, на самом деле это может быть ненастоящим файлом). После этого печатаем дерево имеющихся внутри него данных:

302    Параллельная обработка с Dask и Zarr / ├── 2L │ └── calldata │ └── GT (48525747, 81, 2) int8 ├── 2R │ └── calldata │ └── GT (60132453, 81, 2) int8 ├── 3L │ └── calldata │ └── GT (40758473, 81, 2) int8 ├── 3R │ └── calldata │ └── GT (52226568, 81, 2) int8 ├── X │ └── calldata │ └── GT (23385349, 81, 2) int8 └── samples (81,) |S24

Файл Zarr содержит пять массивов: четыре соответствуют хромосомам комара – 2L, 2R, 3L, 3R и X (Y не включен) – и один содержит список из 81 образца, включенного в файл. В последний массив включены имена образцов – в этом файле у нас 81 образец. Данные хромосомы состоят из 8-битных целых чисел (int8), а имена образцов представляют собой строки. 2. Теперь давайте изучим данные для хромосомы 2L. Начнем с базовой информации: gt_2l = mosquito['/2L/calldata/GT'] gt_2l

Вот результат:

У нас есть массив из 4852547 однонуклеотидных полиморфизмов (SNP) для 81 образца. Для каждого SNP и образца у нас есть 2 аллеля. 3. Давайте теперь проверим, как хранятся данные: gt_2l.info

Вывод выглядит следующим образом:

Чтение геномных данных с помощью Zarr    303 Name : /2L/calldata/GT Type : zarr.core.Array Data type : int8 Shape : (48525747, 81, 2) Chunk shape : (300000, 50, 2) Order : C  Read-only : False Compressor : Blosc (cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0) Store type : zarr.storage.DirectoryStore No. bytes : 7861171014 (7.3G) No. bytes stored : 446881559 (426.2M) Storage ratio : 17.6 Chunks initialized : 324/324

Здесь многое предстоит распаковать, но сейчас мы сосредоточимся на типе хранилища, сохраненных байтах и коэффициенте хранения. Значением Store type является zarr.storage.DirectoryStore, поэтому данные находятся не в одном файле, а внутри каталога. Необработанный размер данных составляет 7.3 ГБ! Но Zarr использует сжатый формат, который уменьшает размер до 426.2 мегабайта (МБ). Это означает степень сжатия 17.6. 4. Давайте посмотрим, каким образом данные хранятся внутри каталога. Если вы просмотрите содержимое каталога AG1000G-AO, то найдете следующую структуру: . ├── 2L │ └── calldata │ └── GT ├── 2R │ └── calldata │ └── GT ├── 3L │ └── calldata │ └── GT ├── 3R │ └── calldata │ └── GT ├── samples

304    Параллельная обработка с Dask и Zarr └── X └── calldata └── GT

5. Если вы просмотрите содержимое 2L/calldata/GT, то найдете множество файлов, кодирующих массив: 0.0.0 0.1.0 1.0.0 ... 160.0.0 160.1.0

В каталоге 2L/calldata/GT находится 324 файла. Помните из предыдущего шага, что у нас есть параметр Chunk shape со значением (300000, 50, 2). Zarr разбивает массив на фрагменты, или чанки (chunks), – их легче обрабатывать в памяти, чем загружать весь массив. Каждый фрагмент имеет 30000×50×2 элементов. Учитывая, что у нас есть 48 525 747 однонуклеа­ тидных полиморфизмов SNP, нам нужно 162 чанка, чтобы представить количество SNP (48 525 747 / 300 000 = 161,75), а затем умножить его на 2 для количества образцов (81 образец / 50 на чанк = 1,62). Следовательно, мы получаем 162×2 чанков/файлов. Совет Разбиение на фрагменты, «чанкинг»  – это метод, широко используемый для работы с  данными, которые невозможно полностью загрузить в память за один проход. Его используют многие другие библиотеки, такие как Pandas или Zarr. Позже мы увидим пример с Zarr. Очень важно помнить о методе чанкинга, поскольку он применяется во многих случаях, подразумевающих использование больших данных (big data).

6. Прежде чем мы загрузим данные библиотеки Zarr для обработки, давайте создадим функцию для вычисления некоторых параметров базовой геномной статистики чанка. Мы рассчитаем количество пропусков, количество предковых гомозигот и количество гетерозигот: def calc_stats(my_chunk): num_miss = np.sum(np.equal(my_chunk[:,:,0], -1), axis=1) num_anc_hom = np.sum( np.all([ np.equal(my_chunk[:,:,0], 0),

Чтение геномных данных с помощью Zarr    305 np.equal(my_chunk[:,:,0], my_chunk[:,:,1])], axis=0), axis=1) num_het = np.sum( np.not_equal( my_chunk[:,:,0], my_chunk[:,:,1]), axis=1) return num_miss, num_anc_hom, num_het

Если вы посмотрите на предыдущую функцию, то заметите, что в  ней нет ничего, связанного с Zarr: это просто код NumPy. Zarr имеет очень легкий интерфейс прикладного программирования (API), который предоставляет большую часть данных внутри NumPy, что делает его довольно простым в использовании, если вы знаете NumPy. 7. Наконец, давайте пройдемся по нашим данным, то есть пройдемся по чанкам, чтобы вычислить нашу статистику: complete_data = 0 more_anc_hom = 0 total_pos = 0 for chunk_pos in range(ceil(max_pos / chunk_pos_size)): start_pos = chunk_pos * chunk_pos_size end_pos = min(max_pos + 1, (chunk_pos + 1) * chunk_pos_size) my_chunk = gt_2l[start_pos:end_pos, :, :] num_samples = my_chunk.shape[1] num_miss, num_anc_hom, num_het = calc_stats(my_chunk) chunk_complete_ data = np.sum(np.equal(num_miss, 0)) chunk_more_anc_hom = np.sum(num_anc_hom > num_het) complete_data += chunk_complete_data more_anc_hom += chunk_more_anc_hom total_pos += (end_pos - start_pos) print(complete_data, more_anc_hom, total_pos)

Большая часть кода отвечает за управление чанками и  использует арифметические действия для определения того, к какой части массива обращаться. Важной частью с точки зрения готовых данных Zarr является строка my_chunk = gt_2l[start_pos:end_pos, :, :]. Когда вы делите на части массив Zarr, он автоматически возвращает массив NumPy.

306    Параллельная обработка с Dask и Zarr Совет Будьте очень осторожны с  объемом данных, которые вы помещаете в память. Помните, что большинство массивов Zarr окажутся значительно больше, чем доступная вам память, поэтому если вы попытае­ тесь загрузить их, ваше приложение и, возможно, даже ваш компьютер зависнет. Например, если вы выполните all_data = gt_2l[:, :, :], вам потребуется около 8 ГБ свободной памяти для загрузки – как мы видели ранее, размер данных составляет 7,3 ГБ.

Дополнительно... Zarr имеет гораздо больше функций, чем те, что представлены здесь, и хотя мы еще рассмотрим иные в  следующих рецептах, есть некоторые возможности, о которых вам следует знать, кроме того. Например, Zarr – одна из единственных библиотек, допускающих параллельную запись данных. Вы также можете изменить внутренний формат представления Zarr. Как мы видели здесь, Zarr может очень эффективно сжимать данные – это стало возможным благодаря использованию библиотеки Blosc (https://www. blosc.org/). Вы можете изменить внутренний алгоритм сжатия данных Zarr благодаря гибкости Blosc.

Смотрите также Существуют форматы, альтернативные Zarr, например Иерархический формат данных 5 (Hierarchical Data Format 5, HDF5) (https://en.wikipedia.org/ wiki/Hierarchical_Data_Format) и  Сетевой формат обмена данными (Network Common Data Form, NetCDF) (https://en.wikipedia.org).  /вики/NetCDF). Хотя они более распространены за пределами области биоинформатики, они менее функциональны – например, отсутствие возможности одновременной записи.

Параллельная обработка данных с использованием многопроцессорности Python При работе с большим количеством данных одна из стратегий состоит в их параллельной обработке – это позволяет использовать всю доступную мощность центрального процессора (CPU), учитывая, что современные машины имеют много ядер. В теоретическом лучшем случае, если ваша машина имеет восемь ядер, вы можете получить восьмикратное увеличение производительности, если будете делать параллельную обработку данных. К сожалению, типичный код Python использует только одно ядро. При этом Python имеет встроенную возможность применения всей доступной мощности CPU; на самом деле Python предоставляет для этого несколько разных способов. В  этом рецепте мы будем использовать встроенный модуль multiprocessing. Представленное здесь решение хорошо работает на одном компьютере, если набор данных помещается в  память, но если вы хотите расширить базу вы-

Параллельная обработка данных с использованием многопроцессорности...    307 числений до кластера или облака, вам следует рассмотреть Dask, который мы представим в следующих двух рецептах. Наша цель здесь снова будет состоять в  том, чтобы вычислить некоторую статистику пропусков и гетерозиготности.

Подготовка Мы будем использовать те же данные, что и в предыдущем рецепте. Код этого рецепта можно найти в Chapter11/MP_Intro.py.

Как это сделать... Для начала выполните следующие действия. 1. Мы будем использовать ту же самую функцию, что и в предыдущем рецепте, для расчета статистики – она в значительной степени основана на NumPy: import numpy as np import zarr def calc_stats(my_chunk): num_miss = np.sum(np.equal(my_chunk[:,:,0], -1), axis=1) num_anc_hom = np.sum( np.all([ np.equal(my_chunk[:,:,0], 0), np.equal(my_chunk[:,:,0], my_chunk[:,:,1])], axis=0), axis=1) num_het = np.sum( np.not_equal( my_chunk[:,:,0], my_chunk[:,:,1]), axis=1) return num_miss, num_anc_hom, num_het

2. Теперь получим доступ к данным о комарах: mosquito = zarr.open('data/AG1000G-AO') gt_2l = mosquito['/2L/calldata/GT']

3. Хотя мы используем одну и ту же функцию для расчета статистики, наш подход будет отличаться для разных данных из нашего набора. Сначала мы вычисляем все интервалы, для которых будем вызывать calc_stats. Интервалы будут выбраны таким образом, чтобы идеально соответствовать разделению вариантов на чанки: chunk_pos_size = gt_2l.chunks[0] max_pos = gt_2l.shape[0]

308    Параллельная обработка с Dask и Zarr intervals = [] for chunk_pos in range(ceil(max_pos / chunk_pos_size)): start_pos = chunk_pos * chunk_pos_size end_pos = min(max_pos + 1, (chunk_pos + 1) * chunk_pos_size) intervals.append((start_pos, end_pos))

Важно, чтобы наш список интервалов был связан с разбивкой на чанки на диске. Вычисление будет эффективным до тех пор, пока это отображение будет максимально точным. 4. Теперь мы собираемся выделить код для вычисления каждого интервала в функции. Это важно, так как модуль multiprocessing будет многократно выполнять эту функцию для каждого создаваемого им процесса: def compute_interval(interval): start_pos, end_pos = interval my_chunk = gt_2l[start_pos:end_pos, :, :] num_samples = my_chunk.shape[1] num_miss, num_anc_hom, num_het = calc_stats(my_chunk) chunk_complete_ data = np.sum(np.equal(num_miss, 0)) chunk_more_anc_hom = np.sum(num_anc_hom > num_het) return chunk_complete_data, chunk_more_anc_hom

5. Теперь мы, наконец, собираемся запустить наш код на нескольких ядрах: with Pool() as p: print(p) chunk_returns = p.map(compute_interval, intervals) complete_data = sum(map(lambda x: x[0], chunk_returns)) more_anc_hom = sum(map(lambda x: x[1], chunk_returns)) print(complete_data, more_anc_hom)

Первая строка создает контекстный менеджер, используя объект multiprocessing.Pool. Объект Pool по умолчанию создает несколько процессов с  номерами os.cpu_count(). Он  предоставляет функцию map, которая будет вызывать нашу функцию compute_interval для всех созданных процессов. Каждый вызов будет забирать один из интервалов.

Дополнительно... Этот рецепт представляет собой небольшое введение в параллельную обработку с помощью Python без необходимости использования внешних библиотек. При этом он представляет собой самый важный строительный блок для одновременной параллельной обработки с Python.

Использование Dask для обработки геномных данных...    309 Из-за того, как в  Python реализовано управление потоками, многопоточность не является жизнеспособной альтернативой реальной параллельной обработке. Чистый код Python не может выполняться параллельно с использованием многопоточности. Некоторые библиотеки, которые вы можете использовать (обычно это относится к NumPy), могут применять все процессоры CPU даже при выполнении последовательного фрагмента кода. Убедитесь, что при использовании внешних библиотек вы не перегружаете ресурсы процессора: это происходит, когда у вас есть несколько процессов, а базовые библиотеки также используют много ядер.

Смотрите также  О  модуле multiprocessing можно говорить и  дальше. Вы  можете начать со стандартной документации по адресу https://docs.python.org/3/library/ multiprocessing.html.  Чтобы понять, почему многопоточность на основе Python не использует все ресурсы CPU, прочитайте о  глобальной блокировке интерпретатора (GIL) по адресу https://realpython.com/python-gil/.

Использование Dask для обработки геномных данных на основе массивов NumPy Dask  – это библиотека, обеспечивающая расширенную параллельную обработку, которую можно расширить от одного компьютера до очень больших клас­теров или облачных расчетов. Она также предоставляет возможность обрабатывать наборы данных, объем которых превышает размер памяти. Может предоставлять интерфейсы, похожие на обычные библиотеки Python, такие как NumPy, Pandas или scikit-learn. Мы собираемся повторить некоторое количество примеров из предыдущих рецептов, а  именно вычислить недостающие SNP в  нашем наборе данных. Мы будем использовать интерфейс, похожий на NumPy, предлагаемый Dask. Прежде чем мы начнем, имейте в  виду, что семантика Dask сильно отличается от таких библиотек, как NumPy или Pandas: это «ленивая» библиотека. Например, когда вы указываете вызов, эквивалентный, скажем, np.sum, вы на самом деле не вычисляете сумму, а добавляете задачу, которая в будущем в конечном итоге ее вычислит. Перейдем к рецепту, чтобы было понятнее.

Подготовка Мы собираемся переразделить данные Zarr на чанки совершенно другим способом. Причина, по которой мы это делаем, заключается в том, что мы можем визуализировать диаграммы задач во время подготовки нашего алгоритма. Диаграммы задач с пятью операциями легче визуализировать, чем графы задач с сотнями узлов. В практических целях вам не следует делать чанкинг на такие маленькие фрагменты, как мы делаем здесь. На  самом деле все будет в  порядке, если вы вообще не  будете делать речанкинг этого набора данных вообще. Мы делаем это только с целью визуализации:

310    Параллельная обработка с Dask и Zarr import zarr mosquito = zarr.open('data/AG1000G-AO/2L/calldata/GT') zarr.array( mosquito, chunks=(1 + 48525747 // 4, 81, 2), store='data/rechunk')

В итоге мы получим очень большие чанки, и  хотя это хорошо для нашей цели визуализации, они могут оказаться слишком большими, чтобы помес­ титься в памяти. Код этого рецепта можно найти в Chapter11/Dask_Intro.py.

Как это сделать... 1. Давайте сначала загрузим данные и проверим размер DataFrame: import numpy as np import dask.array as da mosquito = da.from_zarr('data/rechunk') mosquito

Вот вывод, если вы выполняете это внутри Jupyter:

Рис. 11.1. Вывод Jupyter для массива Dask, суммирующий наши данные

Полный массив занимает 7.32 ГБ. Самым важным числом является размер чанка: 1.83 ГБ. Каждому рабочему узлу (worker) потребуется память, достаточная для обработки чанка. Помните, что мы используем только такое минимальное количество чанков, чтобы иметь возможность построить диаграмму задачи. Из-за больших размеров чанков мы получаем всего четыре чанка. Мы пока ничего не грузили в память: просто указали, что хотим в итоге это сделать. Мы создаем задачу на построение диаграммы, а не выполняем ее – по крайней мере, на данный момент.

Использование Dask для обработки геномных данных...    311 2. Давайте посмотрим, какие задачи нам нужно выполнить, чтобы загрузить данные: mosquito.visualize()

Вот результат:

Рис. 11.2. Задачи, которые необходимо выполнить для загрузки нашего массива Zarr

Таким образом, у нас есть четыре задачи для выполнения, по одной для каждого чанка. 3. Теперь давайте посмотрим на функцию для вычисления пропусков на чанк: def calc_stats(variant): variant = variant.reshape(variant.shape[0] // 2, 2) misses = np.equal(variant, -1) return misses

Функция для каждого чанка будет работать с массивами NumPy. Обратите внимание на разницу: код, который мы используем для работы в основном цикле, работает с массивами Dask, но на уровне чанка данные представлены в виде массива NumPy. Следовательно, чанки должны помещаться в памяти, поскольку этого требует NumPy. 4. Позже, когда мы на самом деле будем использовать функцию, нам понадобится двумерный (2D) массив. Учитывая, что массив трехмерный (3D), нам нужно будет изменить форму массива: mosquito_2d = mosquito.reshape( mosquito.shape[0], mosquito.shape[1] * mosquito.shape[2]) mosquito_2d.visualize()

312    Параллельная обработка с Dask и Zarr Вот граф задачи в его нынешнем виде:

Рис. 11.3. Граф задачи по загрузке геномных данных и изменению формы их массива

Операция reshape выполняется на уровне dask.array, а  не на уровне NumPy, поэтому она просто добавляет узлы в граф задачи. Выполнение задачи до сих пор не началось. 5. Теперь давайте подготовимся к выполнению функции – то есть к добавлению задач в наш граф задач – по всему нашему набору данных. Есть много способов сделать это; здесь мы собираемся использовать функцию apply_along_axis, которую предоставляет dask.array и которая основана на одноименной функции из NumPy: max_pos = 10000000 stats = da.apply_along_axis( calc_stats, 1, mosquito_2d[:max_pos,:], shape=(max_pos,), dtype=np.int64) stats.visualize()

Пока мы будем изучать только первый миллион позиций. Как вы можете видеть на графе задач, Dask достаточно умна, чтобы добавить операцию только к чанку, участвующему в вычислении.

Использование Dask для обработки геномных данных...    313

Рис. 11.4. Полный граф задачи, включая статистические вычисления

6. Помните, что до сих пор мы еще ничего не вычисляли. Пришло время выполнить граф задачи на самом деле: stats = stats.compute()

Это запустит вычисление. Как именно выполняются вычисления, мы обсудим в следующем рецепте.

314    Параллельная обработка с Dask и Zarr Предупреждение Из-за размера чанка этот код может привести к  зависанию вашего компьютера. Вы будете в безопасности как минимум с 16 ГБ памяти. Помните, что вы можете использовать чанки меньшего размера, и вы должны использовать чанки меньшего размера. Мы  использовали такие размеры чанков, чтобы иметь возможность генерировать диаграммы задач, показанные ранее (в противном случае они, возможно, имели бы сотни узлов и были бы невыполнимыми).

Дополнительно... Мы не тратили здесь время на обсуждение стратегий оптимизации кода для Dask – это была бы отдельная книга. Для очень сложных алгоритмов вам потребуется дополнительно изучить передовой опыт. Dask предоставляет интерфейсы, аналогичные другим известным библиотекам Python, таким как Pandas или scikit-learn, которые можно использовать для параллельной обработки. Вы  также можете использовать его для общих алгоритмов, которые не основаны на существующих библиотеках.

Смотрите также  Лучшей отправной точкой для знакомства с Dask является сама документация Dask, особенно https://docs.dask.org/en/latest/best-practices.html.

Планирование задач с помощью dask.distributed Dask чрезвычайно гибок с  точки зрения выполнения: мы можем выполнять задачи локально, в научном кластере или в облаке. За эту гибкость приходится платить: задачи необходимо параметризовать. Существует несколько альтернатив для настройки планировщика и исполнителя Dask, но наиболее универсальным является dask.distributed, так как он может управлять различными типами инфраструктуры. Поскольку я не могу предположить, что у вас есть доступ к кластеру или облаку, такому как Amazon Web Services (AWS) или GCP, мы будем настраивать вычисления на вашем локальном компьютере, но помните, что вы можете настроить dask.distributed на самых разных типах платформы. Здесь мы снова вычислим простую статистику по вариантам проекта «Anopheles 1000 Genomes».

Подготовка Прежде чем начнем с  dask.distributed, мы должны отметить, что у  Dask есть планировщик по умолчанию, который оперативно может меняться в зависимости от целевой библиотеки. Например, вот планировщик для нашего примера NumPy: import dask from dask.base import get_scheduler

Планирование задач с помощью dask.distributed    315 import dask.array as da mosquito = da.from_zarr('data/AG1000G-AO/2L/calldata/GT') print(get_scheduler(c ollections=[mosquito]). ­_module _)

Вывод будет следующим: dask.threaded

Здесь Dask использует многопоточный планировщик. Это имеет смысл для массива NumPy: реализация NumPy сама по себе многопоточная (настоящая многопоточная с параллельной обработкой). Мы не хотим, чтобы множество процессов выполнялось, когда базовая библиотека работает параллельно самой себе. Если бы у вас был Pandas DataFrame, Dask, скорее всего, выбрала бы многопроцессорный планировщик. Поскольку Pandas не является параллельным, для Dask имеет смысл работать параллельно самой. Хорошо, теперь, когда мы убрали эту помеху, давайте вернемся к подготовке нашей среды. В dask.distributed есть централизованный планировщик и набор рабочих узлов, или воркеров, и нам нужно их запустить. Запустите этот код в командной строке, чтобы запустить планировщик: dask-scheduler --port 8786 --dashboard-address 8787

Мы можем запустить воркеры на той же машине, что и планировщик, вот так: dask-worker --nprocs 2 –nthreads 1 127.0.0.1:8786

Я указал два процесса с одним потоком на процесс. Это разумно для кода NumPy, но фактическая конфигурация будет зависеть от вашей рабочей нагрузки (и  будет совершенно другой, если вы работаете в  кластере или облаке). Совет На самом деле вам не нужно запускать весь процесс вручную, как это сделал здесь я. Здесь это сделано потому, что dask.distributed запус­ тит что-то для вас, на самом деле не  оптимизированное для вашей рабочей нагрузки, – если вы не подготовите систему самостоятельно (подробности см. в  следующем разделе). Но  я  хотел дать вам представление о возможных трудностях, поскольку во многих случаях вам придется настраивать инфраструктуру самостоятельно.

Опять же, мы будем использовать данные из первого рецепта. Обязательно загрузите и подготовьте его, как описано в разделе «Подготовка». Мы не будем использовать переразбитую на чанки часть  – мы сделаем это в  нашем коде Dask в следующем разделе. Наш код доступен в Chapter11/Dask_distributed.py.

316    Параллельная обработка с Dask и Zarr

Как это сделать... Для начала выполните следующие действия. 1. Начнем с подключения к планировщику, который мы создали ранее: import numpy as np import zarr import dask.array as da from dask.distributed import Client client = Client('127.0.0.1:8786') client

Если вы используете Jupyter, то получите хороший вывод, обобщающий конфигурацию, созданную вами в части «Подготовка» этого рецепта:

Рис. 11.5. Сводная информация о вашей среде выполнения с dask.distributed

Здесь вы заметите ссылку на панель инструментов. Планировщик dask. distributed предоставляет панель управления в режиме реального времени через интернет, которая позволяет отслеживать состояние вычислений. Чтобы найти его, укажите в  браузере http://127.0.0.1:8787/ или просто перейдите по ссылке, указанной на рис. 11.5. Поскольку мы до сих пор не производили никаких вычислений, инфор-

Планирование задач с помощью dask.distributed    317 мационная панель в  основном пуста. Обязательно изучите множество вкладок наверху.

Рис. 11.6. Начальное состояние панели мониторинга dask.distributed

2. Давайте загрузим данные. Более строго, давайте подготовим граф задач для загрузки данных: mosquito = da.from_zarr('data/AG1000G-AO/2L/calldata/GT') mosquito

Вот вывод на Jupyter:

Рис. 11.7. Сводка исходного массива Zarr для хромосомы 2L

3. Чтобы облегчить визуализацию, снова разобьем набор на чанки. У  нас также будет один чанк для второго измерения, то есть образцов. Это свя-

318    Параллельная обработка с Dask и Zarr зано с тем, что для нашего вычисления пропусков требуются все образцы, и  в нашем конкретном случае не  имеет особого смысла иметь два чанка на образец: mosquito = mosquito.rechunk((mosquito.shape[0]//8, 81, 2))

Напоминаем, что у нас очень большие чанки, и у вас могут возникнуть проблемы с памятью. Если они возникнут, вы можете запустить задачу с исходными чанками. Просто визуализация будет нечитаемой. 4. Прежде чем мы продолжим, попросим Dask не только сделать переразбивку на чанки, но и подготовить ее результаты в воркерах: mosquito = mosquito.persist()

Вызов persist гарантирует, что данные доступны в воркерах. На следующем скриншоте вы можете найти панель инструментов где-то в середине вычислений. Вы можете найти, какие задачи выполняются на каждом узле, сводку о выполненных и предстоящих задачах, а также байты, сохраненные для каждого воркера. Следует отметить концепцию переноса на диск (см. верхний левый угол экрана). Если памяти для всех чанков недостаточно, они будут временно записаны на диск.

Рис. 11.8. Состояние панели мониторинга при выполнении функции сохранения для повторного разбиения массива на чанки

5. Теперь давайте посчитаем статистику. Для последнего рецепта мы будем использовать другой подход – попросим Dask применить функцию к каждому чанку: def calc_stats(my_chunk): num_miss = np.sum( np.equal(my_chunk[0][0][:,:,0], -1),

Планирование задач с помощью dask.distributed    319 axis=1) return num_miss stats = da.blockwise( calc_stats, 'i', mosquito, 'ijk', dtype=np.uint8) stats.visualize()

Помните, что каждый чанк  – это не  экземпляр dask.array, а  массив NumPy, поэтому код работает с массивами NumPy. Вот текущий граф задач. Нет никаких операций по загрузке данных, так как функция, выполненная ранее, уже это сделала:

Рис. 11.9. Вызовы функции calc_stats для чанков, начинающихся с сохраненных данных

6. Наконец, мы можем получить наши результаты: stat_results = stats.compute()

Дополнительно... Об интерфейсе dask.distributed можно сказать гораздо больше. Здесь мы представили основные концепции его архитектуры и приборной панели. Планировщик dask.distributed предоставляет асинхронный интерфейс на основе стандартного async – модуля Python. Поскольку эта глава носит ввод­ ный характер, мы не будем его рассматривать, но вам рекомендуется ознакомиться с ним.

Смотрите также  Вы  можете начать с  документации dask.distributed по адресу https:// distributed. dask.org/ru/stable/.  Во многих случаях вам потребуется развернуть свой код в кластере или облаке. Ознакомьтесь с документацией по развертыванию ресурсов на разных платформах: https://docs.dask. org/en/latest/deploying.html.  После того как вы освоите содержание, следующим шагом будет изуче­ ние асинхронных вычислений в  Python. Ознакомьтесь с  https://docs. python.org/3/library/asyncio-task.html.

Глава 12

Функциональное программирование в биоинформатике Python – это язык с  широкими возможностями, который позволяет вам выражать вычисления различными способами. Иногда его называют объектно ориентированным (ОО) языком: конечно, вы можете писать код на диалекте ОО, но вы также можете использовать и другие стили. Большая часть кода на Python написана в  императивном стиле: нет структурирования по иерархии классов, типичного для объектно-ориентированного подхода, и большинство изменений кода утверждают, что, например, если вы пишете x = x + 1, вы меняете состояние переменной х. Если вы пишете сложный код, особенно код, который требует параллельной обработки, императивные и  объектно ориентированные подходы будут иметь некоторые ограничения. Для простых скриптов, которые выполняются на одной машине, императивный и объектно ориентированный стили подойдут хорошо, но биоинформатика – это предприятие, работающее с большими данными, и вам часто придется расширять и расширять свою вычислительную базу, так как требуется обрабатывать слишком много информации, а многие алгоритмы являются вычислительно тяжелыми. Функциональное программирование весьма полезно для сложных и параллельных задач, которые становятся все более распространенными в  биоинформатике. Многие современные архитектуры для высокопроизводительного анализа данных основаны на идеях функционального программирования, например парадигма MapReduce, первоначально разработанная в Google и на таких платформах, как Apache Spark. Эти идеи также напрямую применимы к Dask и даже к тому, чтобы сделать последовательный код более надежным. Функциональное программирование основано на функциях, чистой оценке функций и избегании изменчивости. В этой главе мы собираемся применить очень практичный подход к  представлению этих концепций с  примерами их использования в типичных приложениях биоинформатики. К концу у вас должно сложиться базовое представление о функциональном программировании, но прежде всего вы поймете его полезность и применимость. Если вы используете Docker, все необходимые для анализа данных библиотеки можно найти в образе Docker tiagoantao/bioinformatics_base.

Представление чистых функций    321 В этой главе мы рассмотрим следующие рецепты:  представление чистых функций;  понятие немутабельности1;  избежание мутабельности для надежности шаблона проектирования;  использование «ленивого программирования» для конвейерной обработки;  ограничения рекурсии в Python;  демонстрация functools, модуля Python.

Представление чистых функций Чистые функции имеет пару важных свойств: при одних и тех же входных данных они производят такие же выходные данные и не имеют побочных эффектов (не изменяют глобальные переменные и не выполняют I/O). В этом рецепте мы представим несколько простых примеров, чтобы прояснить их полезность. Нас больше всего – но не исключительно – интересует второе свойство: отсутствие побочных эффектов. В следующих рецептах будет ясно, почему чистые функции могут быть весьма полезными. Мы собираемся разработать очень простой пример, в  котором подсчитываем гены, секвенированные для каждого образца. У  нас будет база данных в текстовом файле со счетчиками генов. Например, мы могли бы секвенировать LCT и TP53 в одном образце и LCT, MRAP2 и POMC в другом. Общее количество будет следующим: TP53: 1, LCT: 2, MRPA2: 1 и  POMC: 1. Мы  будем использовать файл CSV, который можно легко прочитать с помощью Pandas, или сам модуль CSV.

Подготовка Мы будем использовать простой файл CSV в качестве нашей небольшой базы данных. Код этого рецепта можно найти в  Chapter12/Pure.py. Файл базы данных – это my_genes.csv, а  в my_genes.csv.base сохранено исходное состояние на случай, если вам понадобится вернуться к исходному состоянию базы данных.

Как это сделать... Для начала рассмотрим следующие шаги. 1. Начнем с  создания двух простых функций для загрузки и  сохранения данных. Нам также нужно некоторое время, чтобы восстановить исходную базу данных: import shutil import pandas as pd

Неизменяемый тип  – это тип, который не  позволяет изменять значения свойств или полей объекта после его создания. Немутабельность может быть полезной, если требуется обеспечить потокобезопасность типа или если существует зависимость от хеш-кода, остающегося неизменным в хеш-таблице. – Прим. ред.

1

322    Функциональное программирование в биоинформатике def restore_db(file_name): shutil.copyfile(f'{file_name}.base', file_name) def load(file_name): df = pd.read_csv(file_name).set_index ('gene') return dict(df['count']) def save(dict_db, file_name): pd.Series(dict_db).to_csv( file_name, index_label='gene', header=['count'])

Мы используем Pandas, чтобы позаботиться о  постоянстве. Это очень простой пример; вы могли бы также использовать только модуль csv. 2. Мы рассмотрим три альтернативные функции, чтобы сообщить о генах, обнаруженных в образце; вот первая: def add_sample_csv(gene_list): gene_count = load('my_genes.csv') for gene in gene_list: gene_count[gene]=gene_count(gene,0)+1 save(gene_count, 'my_genes.csv')

Эта функция автоматически сохраняет данные с новым образцом. У ней есть побочный эффект чтения и  записи файла, так что это не  чистая функция. 3. Вот второй вариант для указания генов в образце: def add_sample_global_dict(gene_list): global gene_count for gene in gene_list: gene_count[gene] = gene_count.get(0) + 1

Эта функция использует глобальную переменную из модуля и обновляет ее, что является побочным эффектом. Мы не будем использовать эту функцию, но она приведена как плохой пример функции с  побочными эффектами. Мы должны избегать глобальных переменных. 4. Вот вариант чистой функции: def add_sample_dict(dict_db, gene_list): for gene in gene_list: dict_db[gene] = dict_db.get(0) + 1 return dict_db

Представление о немутабельности    323 Эта функция изменяет один из своих параметров, dict_db, однако, как мы увидим в следующем рецепте, это не лучшая практика с точки зрения функционального программирования. Тем не менее она всегда возвращает один и тот же результат для одинакового вывода и не имеет побочных эффектов, так что это будет хороший предварительный подход. 5. Представьте, что мы сейчас запускаем следующий код: add_sample_csv(['MC4R', 'TYR'])

Но если мы запустим его 10 раз по ошибке, каковы будут последствия? Мы  бы завысили количество обоих генов в  9 раз, искажая содержимое нашей базы данных с логической точки зрения. 6. В качестве альтернативы рассмотрите следующее: add_sample_dict(gene_count, ['MC4R', 'TYR'])

Если вы запустите этот код 10 раз, что вы получите в результате? Он все равно будет ошибочным (поскольку мы изменяем gene_count), но, по крайней мере, этот результат еще не зафиксирован на диске. Этот диалект был бы намного удобнее при проведении исследовательского анализа данных – вы могли бы запускать его, зная, что никакие внешние данные не  будут повреждены. В  следующем рецепте мы увидим альтернативу, позволяющую сделать повторный запуск кода менее проблематичным.

Дополнительно... В следующих нескольких рецептах мы на практических примерах увидим, почему чистые функции могут быть очень полезными. Но есть один конкретный случай, который было бы громоздко объяснять на примере, и поэтому мы собираемся представить его в теории здесь. Чистые функции значительно упрощают распараллеливание кода. Представьте себе структуру для выполнения распределенных вычислений, такую как Dask или Spark. Такая структура должна быть готова иметь дело с потенциальным случаем, когда аппаратное обеспечение выходит из строя, и код необходимо переместить и, возможно, повторить его исполнение в другом месте. Если ваш код записывается на диск (в качестве примера побочного эффекта) каждый раз при запуске, распределенным фреймворкам гораздо сложнее восстановиться. Если ваш код не имеет побочных эффектов, распределенная среда может повторить вашу функцию, не  заботясь о  согласованности данных. Действительно, многие распределенные фреймворки не позволяют иметь побочные эффекты в вашем распараллеливаемом коде. Они также могут не одоб­ рить изменчивость структуры данных, что мы сейчас и обсудим.

Представление о немутабельности Другой общей чертой функционального программирования является то, что структуры данных обычно неизменяемы (немутабельны). Эту концепцию сложно обойти, если вы привыкли к императивному программированию – идее программирования без объектов, состояние которых меняется с течением времени.

324    Функциональное программирование в биоинформатике Здесь мы увидим простой пример того, как заставить функцию из предыдущего рецепта работать неизменяемо: то есть, чтобы никакие объекты не изменялись и если нам нужно передать новую информацию, мы создаем новые. Этот рецепт даст краткую презентацию немутабельности с точки зрения структуры данных. В каком-то смысле это будет стандартное изложение, которое можно найти в большинстве книг. Тем не менее наше главное соображение состоит в том, чтобы обсудить мутабельность как шаблон проектирования кода, тему следующего рецепта. Но для этого нам нужно сначала понять немутабельность. Мы рассмотрим две функции: одна изменяет структуры данных, а другая – нет. Это будет сделано в контексте примера, которому мы следовали в предыдущих рецептах данной главы.

Подготовка Мы будем использовать те же данные, что и в предыдущем рецепте. Код этого рецепта можно найти в Chapter12/Mutability.py.

Как это сделать... Следуйте изложенным ниже шагам. 1. Из предыдущего рецепта у нас есть функция, которая изменяет вводный словарь: def add_sample_dict(dict_db, gene_list): for gene in gene_list: dict_db.get(gene,0) +1

Если вы вызовете этот код, скажем, с  add_sample_dict(gene_count, ['DEPP']), пока нет выходных параметров, gene_count вашего словаря будет мутировать, чтобы добавить 1 к гену DEPP. Если вы запустите эту функцию много раз, что типично при исследовательском анализе данных, вы насажаете в свой словарь ошибок. 2. Сравните предыдущую реализацию со следующим кодом: def add_sample_new_dict(dict_db, gene_list): my_dict_db = dict(dict_db) for gene in gene_list: my_dict_db[gene] = my_dict_db.get(0) + 1 return my_dict_db

В этом случае мы копируем dict_db в  новый словарь my_dict_db и  добавляем дополнительный список генов. Создание копии существующего словаря требует затрат памяти и времени, но исходный словарь, dict_db, не изменяется. 3. Если вы используете эту реализацию, можете быть уверены, что ваши вводные параметры никогда не изменятся: new_gene_count = add_sample_new_dict(gene_count, ['DEPP'])

Избежание мутабельности для надежности шаблона проектирования    325 Количество gene_count не  изменяется, а  new_gene_count  – это совершенно новый словарь. Вы можете повторять выполнение кода столько раз, сколько хотите, не  беспокоясь об изменениях в  словаре в  результате каждого выполнения.

Дополнительно... Этот простой рецепт представляет собой пример функции, которая не изменяет свои параметры, не  мутирует. Сейчас мы находимся в  ситуации, когда можем выполнить эту функцию столько раз, сколько захотим,  – по ошибке или намеренно – без каких-либо последствий для остального кода. Это очень полезно, когда мы тестируем код или проводим исследовательский анализ данных, так как нам не нужно беспокоиться о побочных эффектах. Это также облегчает работу распределенных исполнителей, таких как Dask или Spark, поскольку им не нужно беспокоиться о проверке состояния существующих объектов: они фиксированы. Здесь также есть важные уроки для общей разработки программного обес­ печения, даже если вы не используете распределенных исполнителей. Это то, что мы собираемся исследовать в следующем рецепте.

Избежание мутабельности для надежности шаблона проектирования В предыдущем рецепте была представлена концепция немутабельных структур данных. В  этом рецепте мы собираемся обсудить шаблон проектирования, который до самого конца позволяет избежать постоянной мутации базы данных в вашем коде. С точки зрения псевдокода, большинство приложений в длинном скрипте работают следующим образом: Do computation 1 Write computation 1 to disk or database Do computation 2 Write computation 2 to disk or database …. Do computation n Write computation n to disk or database

Здесь мы представим альтернативный подход и обсудим, почему он в целом лучше с точки зрения устойчивости: Do computation 1 Write computation 1 to temporary storage Do computation 2 Write computation 2 to temporary storage ...

326    Функциональное программирование в биоинформатике Do computation n Write computation n to temporary storage Take all temporary data and write it to definitive disk and database

Сначала мы покажем код для обоих подходов, а затем обсудим, почему для сложных и  изощренных скриптов последний подход в  большинстве случаев является лучшим. Мы будем использовать тот же пример, что и в предыдущих рецептах: будем сообщать данные о генах, обнаруженных в разных образцах.

Подготовка Мы будем использовать те же данные, что и в предыдущем рецепте. Код этого рецепта можно найти в Chapter12/Persistence1.py и Chapter12/Persistence2.py.

Как это сделать... Давайте настроим оба решения вместе с некоторым общим кодом. 1. Оба решения по-прежнему используют функции загрузки и сохранения: import pandas as pd

def restore_db(file_name): shutil.copyfile(f'{file_name}.base', file_name) def load(file_name): df = pd.read_csv(file_name).set_index ('gene') return dict(df['count']) def save(dict_db, file_name): pd.Series(dict_db).to_csv( file_name, index_label='gene', header=['count'])

2. Первый вариант, когда мы сохраняемся по ходу дела, таков: def add_sample_csv(gene_list): gene_count = load('my_genes.csv') for gene in gene_list: gene_count[gene]=gene_count(gene,0)+1 save(gene_count, 'my_genes.csv')

add_sample_csv(['MC4R', 'TYR']) add_sample_csv(['LCT', 'HLA-A']) add_sample_csv(['HLA-B', 'HLA-C'])

Избежание мутабельности для надежности шаблона проектирования    327 Каждый раз, когда мы добавляем образец, мы обновляем основную базу данных. Хотя это решение не очень эффективно с точки зрения вводавывода, здесь речь не об этом – на самом деле большинство шаблонов проектирования такого типа, как правило, более эффективны с  точки зрения ввода-вывода, чем то, что мы собираемся рассмотреть дальше. 3. Второй вариант, когда мы сохраняем окончательные данные в конце, таков: def add_sample_new_dict(dict_db, gene_list): my_dict_db = dict(dict_db) # next recipe for gene in gene_list: dict_db.get(gene,0) +1 return my_dict_db gene_count = load('my_genes.csv') gene_count = add_sample_new_dict(gene_count, ['MC4R', 'TYR']) gene_count = add_sample_new_dict(gene_count, ['LCT', 'HLA-A']) gene_count = add_sample_new_dict(gene_count, ['HLA-B', 'HLA-C']) save(gene_count, 'my_genes.csv')

В этом случае мы обновляем только основную базу данных в самом конце. Мы используем словарь в памяти для хранения промежуточных данных.

Дополнительно... Итак, зачем это делать? Решение, которое задерживает запись окончательных данных, во всяком случае более сложное, поскольку нам потенциально нужно сохранять частичные результаты по ходу вычисления и управлять ими (что мы делаем со словарем gene_count в этом случае – простой пример). На самом деле это так, но в коде могут быть баги, дисковое пространство может переполниться, а компьютеры в реальном мире иногда ломаются, поэтому с прагматической точки зрения запись промежуточных данных является более важным соображением. Представьте, что ваше первое решение по какой-то причине перестает работать в середине выполнения. База данных находится в неизвестном состоянии, так как вы не знаете, где был код. Вы просто не можете перезапустить код с нуля, так как часть его уже могла быть выполнена. Итак, вы должны проверить, в каком состоянии находится база данных, увидеть, где остановился код, и частично запустить то, чего не хватает, или найти способ откатить ту часть, которая была выполнена. Это и громоздко, и может привести к ошибкам. Теперь представьте, что при выполнении второго подхода возникает проб­ лема: вам не нужно беспокоиться о промежуточном состоянии данных, потому что оно не влияет на вашу конечную базу данных и будет полностью пересчитано при втором выполнении. Вы можете просто повторно запустить код, не беспокоясь о несовместимых состояниях. Единственным исключением является самая последняя строка, но она не только будет встречаться реже, но вы

328    Функциональное программирование в биоинформатике также можете сосредоточить все свои усилия на этом моменте, чтобы сделать процесс максимально гибким. Всегда ли это можно сделать? Или это работает только в некоторых случаях? Большую часть кода можно написать так, чтобы он создавал временные файлы. Кроме того, большая часть биоинформатического анализа не является транзакционной в реальном времени, поэтому обычно легко обновить базу данных SQL только в  самом конце. И теперь у  вас есть единая точка отказа, относящаяся ко всему вашему коду, – если есть проблема, вы знаете, что вам нужно сосредоточиться только на этой самой последней части. Такой подход, когда применение его возможно, значительно облегчит ежедневное обслуживание сложного кода.

Использование ленивого программирования для конвейерной обработки Ленивое программирование – это стратегия, при которой мы откладываем вычисления до тех пор, пока они действительно не понадобятся. Она имеет много преимуществ по сравнению со своим аналогом, «нетерпеливым программированием», когда мы вычисляем все, как только запускаем вычисление. Python предоставляет множество механизмов для ленивого программирования – действительно, одно из самых больших изменений от Python 2 до Python 3 заключается в том, что язык стал более ленивым. Чтобы понять ленивое программирование, мы снова возьмем нашу базу данных генов и проделаем с ней упражнение. Мы собираемся проверить, есть ли у нас хотя бы n генов с y ридами каждый (например, три гена с пятью ридами каждый). Это может быть, скажем, мера качества нашей базы данных, то есть мера того, достаточно ли у  нас генов при определенном количестве образцов. Мы собираемся рассмотреть две реализации: одну ленивую и одну нетерпеливую. Затем сравним результаты обоих подходов.

Подготовка Код этого рецепта можно найти в Chapter12/Lazy.py.

Как это сделать... Рассмотрим эти два разных подхода. 1. Начнем с нетерпеливой реализации: import pandas as pd def load(file_name): df = pd.read_csv(file_name).set_index ('gene') return dict(df['count'])

Использование ленивого программирования для конвейерной обработки    329 def get_min_reads(all_data, min_reads): return { gene: count for gene, count in all_data.items() if count >= min_reads } def has_min_observations(subset_data, min_observations): return len(subset_data) >= min_observations print(has_min_observations( get_min_reads( load('my_genes.csv'), 4 ), 3))

Обратите внимание, что код загружает все данные, проверяет все вводы до количества 4 и уточняет, есть ли по крайней мере 3 наблюдения с этим количеством. 2. А это альтернативная, ленивая версия: def get_rec(file_name): with open(file_name) as f: f.readline() # header for line in f: toks = line.strip().split(',') yield toks[0], int(toks[1]) def gene_min_reads(source, min_reads): for gene, count in source: if count >= min_reads: yield gene def gene_min_observations(subset_source, min_observations): my_observations = 0 for gene in subset_source: my_observations += 1 if my_observations == min_observations: return True return False

330    Функциональное программирование в биоинформатике print(gene_min_observations( gene_min_reads( get_rec('my_genes.csv'), 4 ), 2))

Этот код ведет себя совершенно по-другому. Во-первых, get_rec и gene_ min_reads являются генераторами (обратите внимание на yield), поэтому они будут возвращать результаты только при необходимости, один за другим. Gene_min_observations вернет результат, как только увидит необходимое количество наблюдений. Это означает, что единственные данные, которые будут прочитаны и обработаны, – это абсолютный минимум для получения результата. В  худшем случае это все равно будет весь файл, но во многих случаях объем данных, которые необходимо обработать, может быть намного меньше.

Дополнительно... Преимущества этого подхода можно увидеть при работе с  очень большими файлами. Хотя у нашего игрушечного файла нет ощутимых преимуществ, если файл слишком велик и не помещается в памяти, первый подход просто рухнет! Кроме того, для каждого шага конвейера требуется достаточный объем памяти для хранения всех данных, которые он обрабатывает, и все шаги должны быть в  памяти одновременно. Таким образом, даже для файлов среднего размера могут возникнуть проблемы с  памятью при нетерпеливом подходе, которых можно было бы избежать при использовании ленивой версии. Во многих случаях ленивый код выполняет значительно меньшую обработку: как и  в предыдущем примере, он может остановить вычисления до того, как прочитает все данные. Это не  всегда гарантировано, но происходит во многих случаях его применения. Историческая справка: Python 2 был значительно более нетерпеливым, чем Python 3. На самом деле это одно из основных изменений от Python 2 к Python 3. В качестве тривиального примера рассмотрим поведение встроенной функции range в Python 2 по сравнению с Python 3. Часть нашей ленивой реализации можно написать более лаконично, используя некоторые встроенные модули Python, – мы вернемся к этому в финальном рецепте.

Ограничения рекурсии в Python Рекурсия  – функция, вызывающая сама себя,  – является фундаментальной концепцией функционального программирования. Многие языки, ориентированные на функциональное программирование, могут выполнять рекурсию до бесконечности. Python не  является одним из этих языков. Вы  можете использовать рекурсию в Python, но должны знать о ее ограничениях, а именно о том, что рекурсия влечет за собой довольно большие затраты памяти и что ее нельзя использовать для замены итерации – типичный случай при написании функциональных программ.

Ограничения рекурсии в Python    331 Чтобы увидеть пределы рекурсии в Python, реализуем последовательность Фибоначчи несколькими способами. Напомним, что последовательность Фибоначчи можно определить следующим образом: Fib(0) = 0 Fib(1) = 1 Fib(n) = Fib(n -1) + Fib(n –2)

Мы также вычисляем факториальную функцию, но в этом случае только рекурсивным способом: Fact(1) = 1 Fact(n) = n * Fact(n –1 )

Подготовка Наш код доступен в Chapter12/Recursion.py.

Как это сделать... Чтобы начать, выполните следующие действия. 1. Начнем с итеративной версии Фибоначчи: def fibo_iter(n): if n < 2: return n last = 1 second_last = 0 for _i in range(2, n + 1): result = second_last + last second_last = last last = result return result

Эта версия вполне достаточна и весьма эффективна. Мы не утверждаем, что итеративная версия чем-то хуже рекурсивной. Наоборот, с  Python она, скорее всего, будет работать лучше. 2. Вот рекурсивная версия: def fibo_naive(n): if n == 0: return 0 if n == 1: return 1 return fibo_naive(n - 1) + fibo_naive(n - 2)

332    Функциональное программирование в биоинформатике Если вы запустите это, то увидите, что производительность существенно снижается по сравнению с итеративной версией. Кроме того, если вы запросите fibo_naive(1000) или аналогичное большое число, код вообще будет работать очень долго (для 1000 его выполнение может занять много часов), тогда как итеративная версия будет работать вполне адекватно. В следующем рецепте мы исправим часть этой проблемы. А пока давайте углубимся в проблемы с рекурсией и Python. 3. Чтобы сделать ситуацию совершенно очевидной, давайте реализуем факториальную функцию, настолько простую, насколько это возможно с точки зрения рекурсивной реализации (даже проще, чем Фибоначчи): def factorial(n): if n == 1: return 1 return n * factorial(n - 1)

Если вы запустите эту функцию с  небольшим числом, таким как factorial(5), то получите правильный ответ, 5. Но если вы попробуете большое число, такое как factorial(20000), вы получите следующее: Traceback (most recent call last): File "Recursion.py", line 8, in factorial(20000) File "Recursion.py", line 4, in factorial return n * factorial(n - 1) File "Recursion.py", line 4, in factorial return n * factorial(n - 1) File "Recursion.py", line 4, in factorial return n * factorial(n - 1) [Previous line repeated 995 more times] File "Recursion.py", line 2, in factorial if n == 1: RecursionError: maximum recursion depth exceeded in comparison

Эта ошибка говорит о том, что существует ограничение на количество рекурсий в  Python: Python может рекурсивно выполнять операции только до определенного предела (см. sys.setrecursionlimit, чтобы изменить это). Хотя вы можете изменить количество рекурсий на большее, всегда будет ограничение, связанное с памятью, необходимой для обслуживания стека. Существуют также альтернативные способы явной реализации рекурсии, но ценой скорости их выполнения. Многие языки реализуют функцию, называемую оптимизацией хвостовых вызовов (Tail Call Optimization, TCO), которая обеспечивает бесконечные уровни рекурсии с высокой производительностью, но Python не реализует ее.

Демонстрация модуля Python functools    333

Дополнительно... Вы можете использовать рекурсию в Python для простых случаев без большого количества повторяющихся вызовов, но в Python рекурсия не является общей заменой итеративным решениям. Рекурсия, скорее всего, является наименее хорошо реализованной основной функцией функционального мира Python.

Демонстрация модуля Python functools В Python есть три встроенных модуля, которые очень помогают при написании кода на функциональном диалекте: functools, operator и itertools. В этом рецепте мы собираемся кратко обсудить модуль functools. Например, фундаментальная функция reduce (откуда и происходит часть названия MapReduce) доступна, только если вы импортируете functools. Подробное изучение этих модулей было бы слишком длинным для одного рецепта, поэтому мы собираемся продемонстрировать только некоторые функции, улучшив часть кода предыдущих рецептов с помощью функций functools, и продемонстрируем несколько наглядных примеров пользы применения этого модуля.

Подготовка Наш код доступен в Chapter12/Builtin.py. Мы будем делать ссылки на предыдущие рецепты.

Как это сделать... Рассмотрим несколько показательных примеров. 1. Помните, что наша рекурсивная реализация факториальной функции в предыдущем рецепте была не очень эффективной? Давайте смягчим многие из ее проблем очень простым способом: import functools @functools.cache def fibo(n): if n == 0: return 0 if n == 1: return 1 return fibo(n - 1) + fibo(n - 2)

Если вы помните рекурсивный код в предыдущем рецепте, он был очень медленным даже для небольших чисел. Выполнение fibo_naive(1000) может занять несколько часов. Эта функция, если добавить к ней декоратор1 Декоратор – это обертка вокруг функций (или классов) в Python, которая меняет способ работы этой функции. – Прим. ред.

1

334    Функциональное программирование в биоинформатике @functools.cache, может намного быстрее обрабатывать гораздо большие числа. Это связано с тем, что декоратор кеша реализует мемоизацию. Что такое мемоизация? Мемоизация – это процесс, посредством которого компьютер кеширует результаты выполнения и при следующем вызове вместо повторного вычисления просто возвращает сохраненный результат. Например, при первом вызове fibo(10) для получения результата функция выполняется фактически, но при втором вызове результат, кешированный при первом запуске, возвращается без выполнения. Мемоизация работает правильно только в том случае, если функция всегда возвращает один и тот же результат для одинакового ввода и не имеет побочных эффектов. То есть мемоизация работает только с чистыми функциями.

2. В  качестве другого примера альтернативы существующему коду с  использованием функционального подхода возьмем функцию из рецепта «Использование ленивого программирования для конвейеризации»: def gene_min_reads(source, min_reads): for gene, count in source: if count >= min_reads: yield gene

Здесь уже есть некоторая функциональная изюминка, так как мы используем генератор. 3. Эту функцию можно записать на более функциональном диалекте: def gene_min_reads(source, min_reads): return map( lambda x: x[0], filter(lambda x: x[1] >= min_reads, source.items()))

Здесь есть что распаковать. Во-первых, посмотрите на встроенную функцию filter: она будет применять функцию, определенную в первом параметре, к объектам итератора по второму параметру. В нашем случае будут сохранены объекты, у  которых второй элемент больше или равен min_reads. Затем функция map берет каждый объект (типа (`GENE`, count)) и  возвращает только первую часть. Функции map и  filter очень распространены в диалектах функционального программирования. Также обратите внимание на квинтэссенцию функциональной концепции анонимной функции, lambda: это функциональное определение, которое будет использоваться только в  одном месте  – что весьма полезно для очень маленьких функций. В этом случае нет прямой причины перепи-

Демонстрация модуля Python functools    335 сывать код таким образом (особенно потому, что генераторный тип предыдущего определения уже обеспечивает наиболее важную черту функционального диалекта для нашей задачи), но вы найдете много случаев, когда этот тип представления является более кратким и выразительным. 4. Еще один важный подход, который вам, скорее всего, понадобится при использовании распределенных систем,  – это приложения частичных функций; вот простой пример использования самых основных арифметических функций: def multiplication(x, y): return x * y double = functools.partial (multiplication, 2) double(3)

Функция double определяется как частичное решение одного из аргументов multiplication, поэтому мы используем термин «приложение частичной функции». Это не только стилистическая проблема, так как иногда на распределенных платформах (или даже просто с функцией map многопроцессорных пулов) вы ограничены предоставлением только одного параметра – следовательно, приложение частичной функции становится необходимостью.

Дополнительно... В модуле functools есть много других интересных приложений, с  которыми стоит ознакомиться. Модули itertools и operator также имеют много функций, которые идиоматичны с функциональной точки зрения.

Смотрите также... Для Python существует несколько библиотек функционального программирования, например см. следующее.  Toolz (http://toolz.readthedocs.org/) с  дополнительными итераторами, функциональными утилитами и  функциональными диалектами для словаря.  Pyrsistent реализует немутабельные структуры данных: https://github. com/tobgu/pyrsistent.  Для получения общих ресурсов по функциональному программированию для Python обязательно ознакомьтесь со списком потрясающих функциональных продуктов Python на GitHub: https://github.com/ sfermigier/awesome-functional-python.

Предметный указатель 1000 геномов Anopheles 101 1000 геномов человека 28, 31, 33, 77, 78 1TUP 236

A

AC 95 admixture 169, 194, 195, 199 AF 95 ALT 95, 96, 100 AmiGO 167 AN 95 Anaconda 20, 21, 23, 25, 29, 69, 139, 201 Anopheles atroparvus 146, 147 Anopheles gambiae 98, 115, 146, 147, 150, 151, 152, 155, 301 Anopheles gambiae 1000 Genomes Project 98 Apache Arrow 39, 52, 53 Apache Spark 320 API 67, 70, 87, 89, 96, 133, 159, 162, 163, 205, 231, 261, 262, 263, 264, 272, 305 API Blender 144 application programming interface 70 apply_win_funs 101, 103 Arlequin 214 Arrow 15, 39, 53, 54, 55, 230 Artifact API 133 as_integer 31 ASCII-деревья 224

B

BAM 16, 69, 78, 87, 88, 89, 90, 93, 97, 98 base pairs 83 Basic Local Alignment Search Tool 74 BCF 97 BED 110, 113, 264, 269, 271 BeforePosition 73 BetweenPosition 73 Binary Alignment Map 69 Bio.PDB 230, 259 Bio.Phylo 225 Bioconda 21, 22, 207, 214 bioconda 24, 87, 111 Bioconductor 28, 35

Biopython 12, 17, 39, 56, 70, 71, 73, 74, 75, 76, 77, 80, 140, 144, 145, 146, 200, 204, 206, 207, 208, 215, 216, 217, 218, 224, 231, 233, 235, 236, 238, 258, 260 BLAST 74 Blosc 306 Bokeh 59, 144 boxplot 84, 108 Browser Extensible Data 110 Burrows-Wheeler Aligner 86 BWA 86, 88, 151

C

calculate_frac 58 call_genotype 178, 180 CCDS 110, 113 CDC 206 ChIP-Seq 68 chrom_sizes 142 chromatin immunoprecipitation sequencing 68 CIGAR 89, 90, 161 CNV 94, 95, 96, 133, 168 comma-separated values 40 Complete Genomics 86, 93, 94 Comprehensive R Archive Network 35 Concise Idiosyncratic Gapped Alignment Report 89 conda 24, 25, 87, 94, 111, 134, 163, 231, 275 Consensus Coding Sequence 111 contigs 179 Copy Number Variation 133, 168 COVID 44, 45, 48 CPython 21 CRAN 35 CRT 70 CSV 40, 294, 321 CV 196 cyvcf2 94, 96, 97

D

D Тадзимы 210, 211 DAG 276

Предметный указатель    337

EBOV 201, 203, 206, 211, 212, 223, 226 EIGENSOFT 191, 192 EIGENSOFT SmartPCA 191 Ensembl 74, 110, 139, 145, 158, 159, 160, 161, 162, 163, 167 Ensembl ID 160, 161 Ensembl REST API 139, 159 Entrez 70, 71, 74, 75 ExactPosition 73

GATK 87, 88, 94, 97, 98, 114 GC 141 GenBank 69, 71, 74, 75, 77, 110, 200, 202, 203, 204, 213 Gene Ontology 139, 233 gene_id 156, 157 gene_min_observations 330 gene_min_reads 330 General Feature Format 110 General Transfer Format 152 Generic Feature Format 152 Genome Bioinformatics 74 Genome-Wide Association Studies 168 get_rec 330 get_sample_relation 105, 106, 107 GFF 16, 110, 152, 154, 155, 157, 264, 271 gffutils 152, 153, 154, 155 ggplot 31, 35, 144 ggplot2 24, 28, 31, 32, 34, 35, 36, 37 Git 20, 22 GitHub 17, 20, 138, 252, 258, 266, 335 GNU ZIP 79 GO 139, 233, 234 Grammar of Graphics 31, 33, 35 Graphviz 167, 272 Groovy 278, 279, 280, 281, 283 groupby 48 GT 95 GWAS 168, 172 gzip 40, 79, 147

F

H

Dask 15, 17, 49, 52, 53, 176, 177, 178, 180, 184, 189, 300, 307, 309, 310, 311, 312, 314, 315, 318, 320, 323, 325 dask.distributed 300, 314, 315, 316, 317, 319 DataFrame 28, 30, 31, 33, 34, 35, 37, 44, 50, 52, 54, 55, 60, 211, 310, 315 DAVID 167 decision trees 293 deep copy 228 DendroPy 200, 201, 202, 204, 205, 206, 209, 211, 213, 214, 215, 217, 218, 219, 220, 223, 224, 228, 229 DevOps 264 Disco 224 Docker 15, 20, 21, 26, 27, 28, 29, 36, 39, 40, 69, 114, 135, 139, 169, 200, 262, 263, 284, 300, 320 DP 95, 104, 107, 120 Drosophila melanogaster 146

E

FASTA 69, 75, 76, 79, 80, 110, 139, 141, 155, 202, 203, 204, 210, 216, 239 FASTQ 16, 28, 35, 69, 77, 79, 80, 81, 82, 83, 85, 86, 110, 137 featuretypes 153 ffmpeg 258 FIFO 222 filter 334 FILTER 97 Fixation Index 168 float 106 FlyBase 146 FST 16, 168, 188, 189, 190 functools 321, 333, 335 F-статистика 168, 191

G

Galaxy 15, 16, 261, 262, 263, 264, 266, 267, 268, 272

Hadoop 224 HapMap 78, 169, 170, 172, 174, 176, 190, 193, 195, 199, 272, 279, 280, 281, 285 head 42 heterozygote 106 HoloViews 144 homozygote 106 horse 161 horse_id 162 HTSeq 69, 110, 111, 112, 113 HTTP.remote 273 human-readable description 72

I

ID образца 178, 185 ID семейства 178, 185 INDEL 94, 95, 96 index 28, 31, 36, 45, 46, 47, 67, 145, 158, 211, 258, 260

338    Предметный указатель IPython 20, 36, 37, 38, 99 IronPython 21 itertools 333, 335 IUPAC 152

Mt 147 multiprocessing 306, 308, 309 MUSCLE 207, 208 MySQL 158, 162, 166

J

N

Java Virtual Machine 21 JSON 22, 160 Jupyter 15, 20, 22, 25, 27, 28, 32, 36, 134, 140, 146, 172, 201, 207, 211, 310, 316, 317 Jupyter Lab 20, 22, 25 Jupyter Notebooks 15, 22 jupytext 22, 25, 26, 134 JVM 21

K

k 198 K-средние 287, 290

L

lactase activity 163, 165 lambda 99, 124, 188, 224, 334 LCT 75, 110, 159, 160, 162, 163, 268, 321 LD- 169 LD-обрезка 174 len 100 LIFO 222 Linkage Disequilibrium 169

M

MAFFT 207 magic 36, 37, 38, 247 Makefile 272 map 224, 334 MapReduce 221, 224, 320, 333 MATLAB 66 Matplotlib 15, 35, 39, 42, 45, 56, 58, 59, 60, 62, 66, 67, 144, 187, 188, 230 matplotlib 66, 67, 81, 84, 92, 107, 225, 226, 244 matplotlib.pyplot 66, 150 Mayavi 144 MD5 78, 264 md5sum 78 median 103 megabase pairs 90 mmCIF 230, 231, 240, 258, 259, 260 MMCIF 259 molecular_function 166 MQ0 102

nbviewer 133, 134, 138 NCBI 69, 70, 71, 74, 75, 77, 146, 204, 233 NCBI Entrez 71, 162 Newick 207 Nextflow 261, 262, 272, 277, 278, 279, 280, 283 Next-generation sequencing 21, 68, 168 Nexus 207 NGS 21, 35, 68, 69, 74, 75, 76, 78, 88, 94, 97, 110, 114, 115, 168, 182 NS 95 nuc_div 210 NumPy 15, 39, 56, 57, 58, 59, 92, 119, 150, 176, 178, 189, 211, 230, 289, 300, 301, 305, 307, 309, 311, 312, 314, 315, 319 N-вызовы 82

O

OpenGL 254 operator 333, 335 orthologues 162

P

Pandas 15, 17, 40, 46, 49, 52, 182, 183, 187, 188, 300, 304, 309, 314, 315, 321, 322 pandas 15, 22, 29, 30, 34, 35, 37, 39, 40, 42, 45, 46, 49, 51, 52, 53, 55, 56, 57, 58, 59, 60, 64, 105, 126, 172, 176, 211, 230, 232, 267 Panther 167 paralogues 162 partial 99, 102 PCA 16, 133, 168, 169, 174, 191, 192, 193, 194, 272, 276, 277, 278, 279, 281, 282, 284, 285, 286, 287, 290, 293 PDB 16, 230, 235, 236, 238, 240, 249, 253, 259, 260 percentile 103 Phred 79, 80, 82, 83, 86, 90, 91, 92, 96 Phylip 206 pip 25, 94, 191, 195, 231, 285 PlasmoDB 146 Plasmodium falciparum 70, 139, 140, 141, 142, 144, 145, 146

Предметный указатель    339

PLINK 168, 169, 171, 172, 174, 175, 176, 177, 178, 180, 185, 191, 192, 194, 195, 197, 198, 285 Plink 2 272, 279 Principal Components Analysis 133, 168, 284 Protein Data Bank 230 Protein database 74 publishDir 280 PubMed 69, 74 PyMOL 230, 231, 244, 252, 253, 254, 255, 257, 258 PyMol_Intro.py 258 Pyrsistent 335 Pysam 87 PyTables 105 PYTHONPATH 24

Q

QD 128 QIIME 2 114, 133, 134, 135, 136, 137, 138 QIIME 2 CLI 137 QUAL 128 QuickGO 167

R

R 20, 28, 31, 35, 37, 184 R magic 20, 36, 37 RAD-Seq 68 RAxML 214, 215, 217, 218, 219, 224 read_delim 29 reduce 224 regex 111 reportlab 140 requests 159 REST API 160 REST API Ensembl 162 REST UniProt 235 restriction site-associated DNA sequencing 68 RESTV 206, 209, 210 ribonucleic acid sequencing 68 RNA-Seq 68 rpy2 20, 28, 34, 35, 36 rule engine 273, 274, 275

S

SAM 87, 89, 93, 97, 110 SAMtools 87, 88, 94, 97 SAMtools Tabix 87 samtools view 87

scikit-learn 25, 168, 191, 194, 284, 285, 287, 292, 309, 314 SciPy 105, 198 seaborn 81, 84 seg_sites 210 sel 188 seq_data 30, 32, 37 Sequence Alignment Map 87 Sequence Read Archive 74 sgkit 169, 176, 177, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 191 Single-Nucleotide Polymorphism 169 Singularity 28 SmartPCA 191, 192, 193, 194 Snakemake 15, 261, 262, 272, 273, 274, 278 SNP 15, 69, 74, 78, 94, 95, 96, 97, 98, 100, 101, 103, 104, 105, 106, 107, 108, 109, 121, 158, 168, 169, 173, 174, 179, 286, 289, 302, 304, 309 SnpEff 98, 107 soft clip 89 Spark 15, 49, 224, 320, 323, 325 SQLite 152 SRA 74 stackplot 92 strand 73 structure 194 subplots 65 subsample_1p 283 SUDV 206, 209, 210 SwissProt 233

T

tabix 87, 90, 94 Tail Call Optimization 332 taj_d 210 TCO 332 Toolz 335

U

UCSC 74, 158, 162 UniProt 161, 231, 235

V

Vaccine Adverse Event Reporting System  40 VAERS 40, 41, 48, 49, 53, 56, 60 VAERS_ID 40, 41, 48, 49 Variant Aller Gatk 88 Variant Call Format 69, 94, 97, 168

340    Предметный указатель VCF 16, 69, 93, 94, 95, 96, 97, 98, 100, 110, 114, 168 VectorBase 146, 156 Vega 161 Vertebrate Genome Annotation 161 VGSC 155, 157 VirtualBox 21 Visualization Toolkit 144 voltage-gated sodium channel 155 VTK 144

W

wat_theta 210 WGS 68, 78, 88 Whole-genome sequencing 68 WikiGene 161 window 100

X

xarray 169, 176 xarray DataSet 177 Xcode 22 Y YAML 264

Z

Zarr 52, 176, 300, 301, 302, 303, 304, 305, 306, 309, 311, 317

А

анализ основных компонентов 16, 133, 168, 284 Архив коротких ридов 74 Архив ридов секвенирования 74 аутосомы 174, 179

Б

база данных nucleotide 71 База данных о белках 74 базовый анализ последовательности  69 Базовый инструмент поиска локального выравнивания 74 Банк данных о белках 230 библиотека pygenomics 195, 285 библиотека requests 159

В

вариации числа копий 94, 168 виртуальная машина Java 21 вставки/делеции 94

вывод секвенсора 82 выравниватель Барроуза–Уилера 86

Г

ген лактазы человека 75 ген переносчика устойчивости к хлорохину 70 ген потенциал-зависимого натриевого канала 155 генетические особенности 152, 153, 297 Генная антология 139 геном лошади 161 генотип 95 глубина рида 95, 98 Грамматика графики 31, 35

Д

дамп базы данных 166 декоратор 333 демультиплексирование 135 деревья решений 293 диплоидный набор хромосом 174

И

идиома 76 индекс фиксации 16, 168 интерфейс прикладного программирования 70, 305 итератор 72, 76

К

кариотип 133 карты выравнивания последовательности 87 кластеризация 16, 288, 291, 292 коды неоднозначности 152 конвейер 109, 272, 279, 281, 283 контиги 153 контролируемое обучение 293 контрольные суммы Дайджеста сообщений 5 78 кортеж 49 краткая идиосинкратическая строка отчета о выравнивании 89 кросс-валидация 196

Л

ладдеризация 223, 228 ленивое программирование 321, 328

М

матричная цепь 73

мемоизация 334 менделевские ошибки 115, 119, 120, 124, 125 метаданные 28, 73, 116, 171, 172, 178, 184, 186, 192, 197, 269, 270 микросателлит 175 модель–цепь–остаток–атом 236 молекулярный докинг 230 мост PythonR 184 мутабельность 321, 324 мягкое клипирование 89

Н

Набор инструментов визуализации 144 направленный ациклический граф 276 Национальный центр биотехнологической информации  69, 146 немутабельность 321, 324 ненумерованные хромосомы 174

О

обратные риды 136 обучающий пример 289 общее количество аллелей 95 общий формат передачи 152 объектно ориентированный интерфейс 66 однонуклеотидный полиморфизм 69, 74, 169 ОК-вызов 124 онтологическое дерево 165 оптимизация хвостовых вызовов 332 ортологи 139, 159, 161 отсечение неравновесного сцепления генов 169

П

параллельная обработка 97 парсинг 141, 142, 154, 206, 236, 240, 259 пары оснований 83 первым вошел – первым вышел 222 полногеномное секвенирование 68 полногеномные ассоциативные исследования 168 последний вошел – первый вышел 222 предметно ориентированный язык  278 прогоны N 149 Проект 1000 геномов 28, 68

Проект аннотации генома позвоночных 161 процентиль 92, 102, 103, 150

Р

разнообразие нуклеотидов 210, 211 расчетная частота аллелей 95 рекурсия 330, 331, 333 референс-ID 89, 90 референсное основание 96 референсный геном 68, 87, 139, 140, 151 риды 82, 85, 89, 90, 151, 210 риды со спаренными концами 85, 89, 90, 151

С

сайты сегрегации 210 секвенирование ДНК 68 секвенирование ДНК, связанное с сайтом рестрикции 68 секвенирование иммунопреципитации хроматина 68 секвенирование по Сэнгеру 115 секвенирование рибонуклеиновой кислоты 68 секвенирование следующего поколения 21, 68 Система отчетности о побочных эффектах вакцин 40 скаффолд 146, 150, 151 случайный лес 298 современные форматы секвенирования 69 сравнение последовательностей 16, 209

Т

тета Уоттерсона 210, 211 транскрипты 153 трансляция РНК 77

У

удобочитаемое описание 72 универсальный формат особенностей  152

Ф

филогенетические деревья 200, 214, 224 формат вызова вариантов 168 фреймворк 20, 26, 224

342    Предметный указатель

Х

хороший гражданин 70 хромосома 96, 110, 112, 146, 147, 149, 156

Ц

Центр по контролю за заболеваниями  206

Ч

чистые функции 321, 323

Э

Эбола 16, 200, 205, 206, 209, 214, 217, 219, 224, 225, 228 экзон 72, 73, 98, 110, 152, 153, 154

Книги издательства «ДМК Пресс» можно заказать в торгово-издательском холдинге «КТК Галактика» наложенным платежом, выслав открытку или письмо по почтовому адресу: 115487, г. Москва, пр. Андропова д. 38 оф. 10. При оформлении заказа следует указать адрес (полностью), по которому должны быть высланы книги; фамилию, имя и отчество получателя. Желательно также указать свой телефон и электронный адрес. Эти книги вы можете заказать и в интернет-магазине: www.galaktika-dmk.com. Оптовые закупки: тел. (499) 782-38-89. Электронный адрес: [email protected].

Тиаго Антао

Биоинформатика с Python: книга рецептов Третье издание

Главный редактор

Мовчан Д. А.

[email protected]



Зам. главного редактора Перевод Корректор Верстка Дизайн обложки

Сенченкова Е. А. Люско И. Л. Синяева Г. И. Луценко С. В. Мовчан А. Г.

Формат 70×100 1/16. Гарнитура «PT Serif». Печать цифровая. Усл. печ. л. 27,95. Тираж 200 экз. Веб-сайт издательства: www.dmkpress.com