Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions HW4_Toropov/protein_tools.py
Original file line number Diff line number Diff line change
@@ -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
89 changes: 35 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.** <br>
- Artyom Toropov ([@artyomtorr](https://github.com/artyomtorr/)) <br>
- Sofiya Vinogradova ([@sofiyaga57](https://github.com/sofiyaga57/)) <br>
- Nikita Zherko ([@rereremin](https://github.com/rereremin/)) <br>
![изображение](https://github.com/artyomtorr/HW4_Functions2/assets/144557024/88f1c523-711a-40d7-9134-30c6b6639037)

Типовые секции, на которые стоит обратить внимание: Title, Overview, Usage, Options, Examples, Troubleshooting, Contacts.

**Tехническое требование к заданию.**

Это задание будет выполняться в командах по 3 человека. Каждый из членов команды должен внести <ins>***как минимум***</ins> 2 функции. Каждое внесение функции должно сопровождаться коммитом с осмысленным описанием коммита. Ниже приведена последовательность действий для успешного выполнения задания (аналогично ДЗ №2):

1. Посмотрите состав своей команды здесь ([**ССЫЛКА**](https://docs.google.com/spreadsheets/d/1KMBBBu8LqauRpDJb0v1ldPwpvzNn8-KakcHexAcqLsE/edit?usp=sharing)).
2. Тимлид делает форк данного репозитория. **В форке создает ветку `HW4_<surname>`, в ветке создает папку `HW4_<surname>`, в этой папке вы всё делаете.**
3. Члены команды могут либо делать свои форки, либо работать в репозитории тимлида в качестве колабораторов ("contributors"). В любом случае делаете клоны => пишите код локально => пушите.
4. В конце тимлид делайет pull-request из `HW4_<surname>` своего репозитория в `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:* <br>
Artyom Toropov (teamlead): functions `is_protein`, `compute_molecular_weight`, `run_protein_tools` <br>
Sofiya Vinogradova: functions ..., <br>
Nikita Zherko: functions `compute_hydrophobicity`, `check_mutations`
232 changes: 232 additions & 0 deletions protein_tools.py
Original file line number Diff line number Diff line change
@@ -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