diff --git a/HW4_Toropov/protein_tools.py b/HW4_Toropov/protein_tools.py new file mode 100644 index 0000000..87bff7b --- /dev/null +++ b/HW4_Toropov/protein_tools.py @@ -0,0 +1,54 @@ +alphabet_protein = {'A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'Y'} +amino_acid_masses = { + 'A': 71.03711, + 'R': 156.10111, + 'N': 114.04293, + 'D': 115.02694, + 'C': 103.00919, + 'Q': 128.05858, + 'E': 129.04259, + 'G': 57.02146, + 'H': 137.05891, + 'I': 113.08406, + 'L': 113.08406, + 'K': 128.09496, + 'M': 131.04049, + 'F': 147.06841, + 'P': 97.05276, + 'S': 87.03203, + 'T': 101.04768, + 'W': 186.07931, + 'Y': 163.06333, + 'V': 99.06841 +} + + +def is_protein(seq): + unique_chars = set(seq) + return unique_chars <= alphabet_protein + + +def molecular_weight(seq): + molecular_weight = 0 + for amino_acid in seq: + molecular_weight += amino_acid_masses[amino_acid] + return round(molecular_weight, 3) + + +def run_protein_tools(*seqs_and_procedure): + procedure = seqs_and_procedure[-1] + seqs = seqs_and_procedure[:-1] + + results = [] + + for seq in seqs: + seq = seq.upper() + if is_protein(seq) is not True: + raise ValueError("Invalid alphabet") + if procedure == 'molecular_weight': + results.append(molecular_weight(seq)) + + if len(results) == 1: + return results[0] + else: + return results diff --git a/README.md b/README.md index f918170..b6b838a 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,46 @@ -# HW 4. Functions 2 -> *This is the repo for the fourth homework of the BI Python 2023 course* +# protein_tools.py -### Homework description +**protein_tools.py** - is a tool which allows the performing of various procedures for a user entered protein sequences. -На прошлой неделе вы делали утилиту для работы с последовательностями нуклеиновых кислот (с весьма строгим ТЗ). Пришло время для чего-то более самостоятельного. +### Usage -#### Основное задание +The tool works by calling the function `run_protein_tools`, which takes arbitrary number of arguments with protein sequencies (*str*) and the name of the procedure to be performed (always the last argument, *str*, see the usage examples below). The output is the result of the procedure as *string* if one sequence is submitted or *list* if several. +**NOTE:** For the procedure `check_mutations` a fixed number of string arguments are used: one RNA sequence, one protein sequence and the name of procedure itself. -Напишите утилиту для работы с последовательностями белков. Там должно быть минимум 5 различных операций, должна быть какая-то точка входа через которую пользователь будет всё это дело использовать. На этом, по сути, всё. Всё целиком зависит от вашей фантазии и креативности. Можете опираться на ДЗ №2 и №3. +### Procedures -Самая главная часть задания - это файл `README.md`. Сделайте краткое введение, напишите описание тула, приведите документацию по использованию со списком аргументов. Добавьте примеры использования. Возможно, вы захотите сделать секцию Troubleshooting. ***Почему это нужно?*** В этот раз проверяющий не будет знать того, как должен работать ваш тул. Это ваш авторский код. Даже самая прекрасная функциональность, не будучи отраженной в README, скорее всего останется незамеченной. README - это ваш способ познакомить пользователя с тулом, показать всё лучше и обосновать, почему именно ваша команда должна получить наивысший балл. +- `compute_molecular_weight` — computes molecular weight of protein sequence in g/mol +- `compute_length` — computes the number of amino acids in protein sequence +- `compute_hydrophobicity` — computes the percentage of gydrophobic aminoacids in protein sequence +- `check_mutations` — +- -Есть люди которые, любят писать документации, а есть те - кто не любит. Найдите в вашей команде того, кто любит. И в будущем в своих рабочих проектах всегда держите рядом такого человек (или будьте им). +### Examples +```python +run_protein_tools('MAEGEITNLP', 'tGQYLAMDTSgLLYGSQT', 'GSCKRGPRT', 'compute_length') # [10, 18, 9] +run_protein_tools('MAEGEITNLP', 'tGQYLAMDTSgLLYGSQT', 'GSCKRGPRT', 'compute_molecular_weight') # [1055.496, 1886.872, 942.482] +run_protein_tools('MAEGEITNLP', 'tGQYLAMDTSgLLYGSQT', 'GSCKRGPRT', 'compute_hydrophobicity') # [50.0, 27.778, 11.111] +run_protein_tools('AUGGAUCAUcAAUAA', 'MDKL*', 'check_mutations') #'Mutations:K3, L4.' +``` + +### Additional information +- The program works **only** with protein and RNA sequences. If any of the entered sequences contain inappropriate characters or cannot exist, the program will display an error. Sequences can contain characters of any case. -Примеры некоторых README, которыми можно вдохновляться: +```python +run_protein_tools('PROTEIN', 'compute_molecular_weight') # ValueError: Invalid protein sequence +run_protein_tools('AUGGAU_AUcAAUAA', 'MDKL*', 'check_mutations')# ValueError: Invalid RNA sequence +``` -- [MetaFX](https://github.com/ctlab/metafx), тул Артёма Иванова. Там еще и [wiki](https://github.com/ctlab/metafx/wiki) крутое. -- [samovar](https://github.com/nvaulin/samovar) -- [MetaGEM](https://github.com/franciscozorrilla/metaGEM) -- [Pharokka](https://github.com/gbouras13/pharokka) +### Contacts +Please use contacts below to reach out with any comments, concerns, or discussions regarding **protein_tools.py.**
+- Artyom Toropov ([@artyomtorr](https://github.com/artyomtorr/))
+- Sofiya Vinogradova ([@sofiyaga57](https://github.com/sofiyaga57/))
+- Nikita Zherko ([@rereremin](https://github.com/rereremin/))
+![изображение](https://github.com/artyomtorr/HW4_Functions2/assets/144557024/88f1c523-711a-40d7-9134-30c6b6639037) -Типовые секции, на которые стоит обратить внимание: Title, Overview, Usage, Options, Examples, Troubleshooting, Contacts. -**Tехническое требование к заданию.** - -Это задание будет выполняться в командах по 3 человека. Каждый из членов команды должен внести ***как минимум*** 2 функции. Каждое внесение функции должно сопровождаться коммитом с осмысленным описанием коммита. Ниже приведена последовательность действий для успешного выполнения задания (аналогично ДЗ №2): - -1. Посмотрите состав своей команды здесь ([**ССЫЛКА**](https://docs.google.com/spreadsheets/d/1KMBBBu8LqauRpDJb0v1ldPwpvzNn8-KakcHexAcqLsE/edit?usp=sharing)). -2. Тимлид делает форк данного репозитория. **В форке создает ветку `HW4_`, в ветке создает папку `HW4_`, в этой папке вы всё делаете.** -3. Члены команды могут либо делать свои форки, либо работать в репозитории тимлида в качестве колабораторов ("contributors"). В любом случае делаете клоны => пишите код локально => пушите. -4. В конце тимлид делайет pull-request из `HW4_` своего репозитория в `main` этого. - - -А также: -- Сопроводите программу лучшим `README.md` файлом в вашей жизни (на английском языке). -- В этом ДЗ проблемы с качеством кода (нейминги, пустые строки, анноатции типов, док.стринги, пробелы) могут привести к снижению балла. Воспользуйтесь линтерами чтобы себя обезопасить. IDE по типу PyCharm или VSCode имеют фунцонал по авто-исправлению многих проблем такого рода. - -Автотестов на GitHub в этом ДЗ нет, но вы можете прогнать линтеры на качество кода локально (как в ДЗ №3, подробнее читайте [тут](https://plausible-cannon-091.notion.site/Code-auto-checks-02b2ea69c1d545fca07b50ce5933ed5f?pvs=4)). - -- Программа должна сохранять регистр символов. -- Программа должна работать только с последовательностями белков. -- Запрещается использование сторонних модулей. - - -### Форма сдачи - -Прикрепите ссылку на pull-request тимлида в Google Class (можете сделать от лица каждого члена команды, но это не обязательно). - - -### Pазбалловка - -- За каждую из 5 операций - максимум **1.5 балла** -- За README - максимум **2.5 балла** -- Если вы не внесли как минимум 2 функции от себя, вы получаете 0 баллов (на баллы остальных членов команды это не влияет). -- За фото созвона в README можно получить 0.2 доп. балла (но не более 10 баллов суммарно) - - - -### **Предполагаемый учебный результат** - -Это задание позволит вам проявить креативность и учиться быть не только кодером, но и автором. Также это задание поможет окончательно закрепить материал по функциям который мы прошли. - -Удачи! ✨✨ +*Author contributions:*
+Artyom Toropov (teamlead): functions `is_protein`, `compute_molecular_weight`, `run_protein_tools`
+Sofiya Vinogradova: functions ...,
+Nikita Zherko: functions `compute_hydrophobicity`, `check_mutations` diff --git a/protein_tools.py b/protein_tools.py new file mode 100644 index 0000000..5dc669a --- /dev/null +++ b/protein_tools.py @@ -0,0 +1,232 @@ +alphabet_protein = {'A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'Y'} + +alphabet_rna = {'A', 'U', 'G', 'C'} + +amino_acid_masses = { + 'A': 71.03711, + 'R': 156.10111, + 'N': 114.04293, + 'D': 115.02694, + 'C': 103.00919, + 'Q': 128.05858, + 'E': 129.04259, + 'G': 57.02146, + 'H': 137.05891, + 'I': 113.08406, + 'L': 113.08406, + 'K': 128.09496, + 'M': 131.04049, + 'F': 147.06841, + 'P': 97.05276, + 'S': 87.03203, + 'T': 101.04768, + 'W': 186.07931, + 'Y': 163.06333, + 'V': 99.06841 +} + +gydrophobic_aminoacids = {"A", "V", "L", "I", "P", "F", "W", "M"} + +dna_codons = { + 'A': ['GCT', 'GCC', 'GCA', 'GCG'], + 'C': ['TGT', 'TGC'], + 'D': ['GAT', 'GAC'], + 'E': ['GAA', 'GAG'], + 'F': ['TTT', 'TTC'], + 'G': ['GGT', 'GGC', 'GGA', 'GGG'], + 'H': ['CAT', 'CAC'], + 'I': ['ATT', 'ATC', 'ATA'], + 'K': ['AAA', 'AAG'], + 'L': ['TTA', 'TTG', 'CTT', 'CTC', 'CTA', 'CTG'], + 'M': ['ATG'], + 'N': ['AAT', 'AAC'], + 'P': ['CCT', 'CCC', 'CCA', 'CCG'], + 'Q': ['CAA', 'CAG'], + 'R': ['CGT', 'CGC', 'CGA', 'CGG', 'AGA', 'AGG'], + 'S': ['TCT', 'TCC', 'TCA', 'TCG', 'AGT', 'AGC'], + 'T': ['ACT', 'ACC', 'ACA', 'ACG'], + 'V': ['GTT', 'GTC', 'GTA', 'GTG'], + 'W': ['TGG'], + 'Y': ['TAT', 'TAC'], + '*': ["UAA", "UAG", "UGA"]} + +rna_codons = { + "F": ["UUC", "UUU"], "L": ["UUA", "UUG", "CUU", "CUC", "CUA", "CUG"], + "I": ["AUU", "AUC", "AUA"], "M": ["AUG"], "V": ["GUU", "GUC", "GUA", "GUG"], + "S": ["UCU", "UCC", "UCA", "UCG"], "P": ["CCU", "CCC", "CCA", "CCG"], + "T": ["ACU", "ACC", "ACA", "ACG"], "A": ["GCU", "GCC", "GCA", "GCG"], + "Y": ["UAC", "UAU"], "*": ["UAA", "UAG", "UGA"], "H": ["CAU", "CAC"], + "Q": ["CAA", "CAG"], "N": ["AAU", "AAC"], + "K": ["AAA", "AAG"], "D": ["GAU", "GAC"], "E": ["GAA", "GAG"], + "C": ["UGU", "UGC"], "W": ["UGG"], "R": ["CGU", "CGC", "CGA", "CGG", "AGA", "AGG"], + "S": ["AGU", "AGC"], "G": ["GGU", "GGC", "GGA", "GGG"] + } + + +def is_protein(seq:str): + """ + Check the existence of a protein sequence, return boolean. + """ + unique_chars = set(seq.upper()) + return unique_chars <= alphabet_protein + + +def is_rna(seq:str): + """ + Check the existence of a RNA sequence, return boolean. + """ + unique_chars = set(seq.upper()) + return unique_chars <= alphabet_rna + + +def compute_molecular_weight(seq:str): + """ + Compute molecular weight (g/mol) of protein sequence. + """ + molecular_weight = 0 + for amino_acid in seq.upper(): + molecular_weight += amino_acid_masses[amino_acid] + return round(molecular_weight, 3) + + +def compute_length(seq:str): + """ + Compute the length of protein sequence. + """ + return len(seq) + + +def compute_hydrophobicity(protein:str) -> tuple: + """ + Compute the percentage of gydrophobic aminoacids in protein sequence. + + Argument: + - protein (str): protein sequence. Include hydrophobic + and hydrophilic aminoacids. + + Return: + - tuple, result of computation percentage of gydrophobic aminoacids. + """ + count_of_gydrophobic = 0 + for i in range(len(protein)): + if protein[i] in gydrophobic_aminoacids: + count_of_gydrophobic += 1 + + percentage = round(count_of_gydrophobic / len(protein) * 100, 3) + + return protein, percentage + + +def translate_rna(seq:str) -> str: + """ + Perform the translation of mRNA seguence into protein sequence. + + Argument: + - seq (str): mRNA sequence. Must contain start-codon and one of + the stop-codons. + + Return: + - str, protein sequence after translation. + Always starts with "M" and ends with "*". + """ + triplets = [seq[i:i + 3].upper() for i in range(0, len(seq), 3)] + protein = [] + for triplet in triplets: + for aminoacid in rna_codons.keys(): + if triplet in rna_codons[aminoacid]: + protein.append(aminoacid) + + if protein[-1] != "*": + raise ValueError("Stop-codon (*) is absent in mRNA") + if protein[0] != "M": + raise ValueError("Start-codon (M) is absent in mRNA") + + start = protein.index("M") + stop = protein.index("*") + return "".join(protein[start:stop + 1]) + + +def check_mutations(seq:str, protein:str) -> str: + """ + Check mutations in the protein sequence after translation. + + Use additional function "translation(seq)". + This function doesn't show mutations, which don't lead to + change aminoacids in protein sequence. + + Arguments: + - seq (str): translation sequence of mRNA with/without mutations + - protein (str): protein for comparison with protein after translation. + Every protein starts with "M" and ends with "*" (stop-codon). + Remark: is_protein(seq) doesn't see "*", but it's used in the other part of function. + + Return: + - str, if mRNA without mutations return "Protein without mutations." + If some mutations in protein, return aminoacid(s) and their position(s) + + Examples: + - "AUGGUAGGGAAAUUUUGA", "MVGKF*" -> "Protein without mutations." + - "AUGGUAGGGAAAUUUUGA", "MGGVF*" -> "Mutations:G2, V4." + - "AUGGUAGGGAAAUUUUGA", "MGGKF" –> "ValueError: Stop (*) is absent" + - "AUGGUAGGGAAAUUUUGA", "GGKF*" –> "ValueError: Start (M) is absent" + - "AUGAAAAAAUGA", "MK*" -> "ValueError: Different length of translated protein and protein" + """ + + correct_protein = translation(seq) + bank_of_mutations = [] + + if is_protein(protein[:-1]) is not True: + raise ValueError("Invalid protein sequence") + if is_rna(seq) is not True: + raise ValueError("Invalid RNA sequence") + if protein[-1] != "*": + raise ValueError("Stop (*) is absent") + if protein[0] != "M": + raise ValueError("Start (M) is absent") + if len(protein) != len(seq)/3: + raise ValueError("Different length of translated protein and protein") + + for i in range(len(correct_protein)): + if correct_protein[i] != protein[i]: + bank_of_mutations.append(f'{protein[i]}{i + 1}') + + if len(bank_of_mutations) == 0: + return "Protein without mutations." + else: + return "Mutations:" + ", ".join(bank_of_mutations) + "." + + +def run_protein_tools(*args:str): + """ + Function containing methods for protein analysis. + + Takes arbitrary number of arguments with protein sequencies + and the name of the procedure to be performed (always the + last argument). Returns the result of the procedure as string + if one sequnce is submitted or list if several. + + If procedure 'check_mutations' is used then input must be only three + arguments: RNA sequence, protein sequence and the name of procedure + itself. + """ + *seqs, procedure = args + results = [] + d_of_functions = {'compute_molecular_weight': compute_molecular_weight, + 'compute_length': compute_length, + 'compute_hydrophobicity': compute_hydrophobicity, + } + if procedure == 'check_mutations': + results.append(check_mutations(seqs[0], seqs[1])) + else: + for seq in seqs: + if is_protein(seq) is not True: + raise ValueError("Invalid protein sequence") + if procedure not in d_of_functions: + raise ValueError("Wrong procedure name") + else: + results.append(d_of_functions[procedure](seq)) + if len(results) == 1: + return results[0] + else: + return results +