-
Notifications
You must be signed in to change notification settings - Fork 0
Review GPR183 #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Review GPR183 #39
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Учитывая объем задания, работа выполнена очень хорошо!
- В парсере fastq реализован весь функционал и использованы все необходимые модули из BioPython
- В задании по классам выполнены все условия полиморфизма и наследования
Из моментов для доработки я бы подстветила несколько моментов.
- BiologicalSequence не реализован, как абстрактный класс. Соответственно, он не опеределяет свод правил для всех дочерних классов. Плюсом он подтягивает методы строки, которые нам могут даже помешать.
- Очень много мелких моментов PEP8. Автоматизированные проверки спасают!
- Нет аннотаций типов.
В любом случае, вы молодец!
import os | ||
from Bio import SeqIO | ||
from Bio.SeqUtils import gc_fraction | ||
from typing import Union |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не совсем корректно представлены импорты. Корректирую все программы с помощью isort)
import os | |
from Bio import SeqIO | |
from Bio.SeqUtils import gc_fraction | |
from typing import Union | |
import os | |
from typing import Union | |
from Bio import SeqIO | |
from Bio.SeqUtils import gc_fraction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Насколько я знаю, так необходимо делать, если импортируются какие-то кастомные (собственные) пакеты. В данном случае, пакет Bio таким не является и мне кажется, не обязательно их разделять пустой строкой.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Встроенные
- Сторонние
- Локальные
Между ними по строке, так что Катя (в лице isort'a) и isort (в лице Кати) правы
This function work with FASTQ files and filters them by | ||
GC content, length and Q-score. | ||
|
||
Arguments (positional): | ||
- input_path (str): full path to the file that you want to work with | ||
- output_filename (str): enter just a name of the file, don't add extention | ||
|
||
Arguments (keyword): | ||
- gc_bound (tuple, int, float): tuple of required range of GC percentage (inclusive), | ||
num or float if only higher border of the range is needed (exclusive). | ||
- length_bound (tuple, int, float): tuple of required range of sequences length (inclusive), | ||
num or float if only higher border of the range is needed (exclusive). | ||
- quality_threshold (int): int of lowest level of Q-score (inclusive). | ||
|
||
Output: | ||
- list of BioSeq records. Write file to .fastq |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Классный и понятный докстринг!
if input_path is None: | ||
raise ValueError("You didn't enter any PATH to file") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Очень полезная ошибка) Облегчает работу с программой!
else: | ||
output_path = f'{input_folder}/fastq_filtrator_resuls/{output_filename}.fastq' | ||
# Create dict from FASTQ | ||
seqs = list(SeqIO.parse(input_path, "fastq")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Всё указано супер корректно, но не совсем понятно, зачем нам полученный результат переводить в лист?
При использовании парсера из BioPython, мы уже получаем коллекцию из SeqRecord, по которой можем итерироваться. У меня вот так работает :)
seqs = list(SeqIO.parse(input_path, "fastq")) | |
seqs = SeqIO.parse(input_path, "fastq") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ровно так
if len(seqs) <= 0: | ||
raise ValueError('There are no fastq sequences') | ||
# Check if all given argumets have relevant type | ||
gc_bound_type = isinstance(gc_bound, (tuple, int, float)) | ||
length_bound_type = isinstance(length_bound, (tuple, int, float)) | ||
quality_thr_type = isinstance(quality_threshold, (int, float)) | ||
if not (gc_bound_type and length_bound_type and quality_thr_type): | ||
raise ValueError('Your arguments are not suitable!') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тоже супер классные и полезные ошибки! Взяла себе на заметку!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В данном случае тем не менее не совсем ясно какой же из аргументов не правильного типа. Так что лучше если проверять, то разделять
dictionary = { | ||
'A': 'U', | ||
'G': 'C', | ||
'U': 'A', | ||
'C': 'G', | ||
'a': 'u', | ||
'g': 'c', | ||
'u': 'a', | ||
'c': 'g', | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PEP8
dictionary = { | |
'A': 'U', | |
'G': 'C', | |
'U': 'A', | |
'C': 'G', | |
'a': 'u', | |
'g': 'c', | |
'u': 'a', | |
'c': 'g', | |
} | |
dictionary = { | |
'A': 'U', | |
'G': 'C', | |
'U': 'A', | |
'C': 'G', | |
'a': 'u', | |
'g': 'c', | |
'u': 'a', | |
'c': 'g', | |
} |
def __len__(self): | ||
return len(self.sequence) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Вернусь к теме про абстрактный класс. BiologicalSequence должен был быть просто шаблоном, определяющим правила создания всех последующих классов. В данном шаблоне мы не должны прописывать никакие функции, так как они не выполняются, а только являются сводом правил. Данный код необходимо было перенести в дочерние классы. В данном случае, это NucleicAcidSequence и AminoAcidSequence
def __len__(self): | |
return len(self.sequence) | |
@abstractmethod | |
def __len__(self): | |
pass |
def alphabet_checking(self): | ||
if not set(self.sequence) <= set(type(self).dictionary.keys()): | ||
raise WrongSequence('Wrong sequence') | ||
return True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Проверка - супер разумная, но, во-первых, она должна быть определена в следующем поколении классов.
А во-вторых, она дублирует возврат True. Само условие уже подразумевает возвращение True или False, поэтому лучше бы сделать так
def alphabet_checking(self): | |
if not set(self.sequence) <= set(type(self).dictionary.keys()): | |
raise WrongSequence('Wrong sequence') | |
return True | |
def alphabet_checking(self): | |
return set(self.sequence) <= set(type(self).dictionary.keys()) |
def __init__(self, sequence): | ||
super().__init__(sequence) | ||
if not self.alphabet_checking(): | ||
del self.sequence | ||
raise WrongSequence('You have entered a wrong sequence') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Перед init должна быть одна строчка
def __init__(self, sequence): | |
super().__init__(sequence) | |
if not self.alphabet_checking(): | |
del self.sequence | |
raise WrongSequence('You have entered a wrong sequence') | |
def __init__(self, sequence): | |
super().__init__(sequence) | |
if not self.alphabet_checking(): | |
raise WrongSequence('You have entered a wrong sequence') |
del self.sequence | ||
raise WrongSequence('You have entered a wrong sequence') | ||
|
||
def protein_mass(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C функцией всё отлично, но тут добавлю комментарий по всему заданию по классам. Нигде не приведена аннотация. Кажется, с ней просматривать код намного приятней)
def protein_mass(self): | |
def protein_mass(self) -> float: |
gc_bound: Union[tuple, int, float] = (0, 100), | ||
length_bound: Union[tuple, int, float] = (0, 2**32), | ||
quality_threshold: Union[int, float] = 0) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Интересное решение с Union, взяла себе на заметку
raise WrongSequence('You have entered a wrong sequence') | ||
|
||
def protein_mass(self): | ||
mass = sum(self.dictionary.get(aa) for aa in self.sequence) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
С точки зрения кода всё отлично, но биологически при вычисления массы белка нужно вычитать воду, например:
mass = sum(self.dictionary.get(aa) for aa in self.sequence) | |
list_input_seq = list(seq) | |
water_mw = 18 | |
for aa in list_input_seq: | |
total_mw = sum(aa_weight_dict[a] for a in list_input_seq) | |
mw_water_removed = (total_mw - (water_mw * (len(list_input_seq)-1))) | |
return mw_water_removed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Хе-хе, вот она разница между биоинформатиком и программистом!
pass | ||
|
||
|
||
class BiologicalSequence(str): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
По заданию, BiologicalSequence должен был быть абстрактным классом, то есть никакого функционала тут быть не должно. Абстрактные классы создают только шаблон структуры.
self.gc_cont = None | ||
|
||
def complement(self): | ||
return type(self)(''.join([type(self).dictionary[i] for i in self.sequence])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Отличное решение!
class BiologicalSequence(str): | ||
def __init__(self, sequence): | ||
self.sequence = sequence | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
еще по заданию нужно было добавить вот это:
def __str__(self) -> str: | |
return self.sequence |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Очень классная работа, мне особо и сказать нечего (тем более, что много чего уже было сказано до меня). Ты молодец!
pass | ||
|
||
|
||
class BiologicalSequence(str): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
По заданию это должен был быть абстрактный класс.
"V": 99.06841, | ||
"W": 186.07931, | ||
"Y": 163.06333, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PEP8
Ну тут все таки важно чтобы вы учились комментировать код, поэтому нет ничего плохого в том чтобы повторять чьи-то комментарии если вам тоже они пришли в голову |
Review GPR183