Conversation
EkaterinShitik
left a comment
There was a problem hiding this comment.
Учитывая объем задания, работа выполнена очень хорошо!
- В парсере 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.
Не совсем корректно представлены импорты. Корректирую все программы с помощью 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.
Насколько я знаю, так необходимо делать, если импортируются какие-то кастомные (собственные) пакеты. В данном случае, пакет Bio таким не является и мне кажется, не обязательно их разделять пустой строкой.
There was a problem hiding this comment.
- Встроенные
- Сторонние
- Локальные
Между ними по строке, так что Катя (в лице 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 |
| if input_path is None: | ||
| raise ValueError("You didn't enter any PATH to file") |
There was a problem hiding this comment.
Очень полезная ошибка) Облегчает работу с программой!
| 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.
Всё указано супер корректно, но не совсем понятно, зачем нам полученный результат переводить в лист?
При использовании парсера из BioPython, мы уже получаем коллекцию из SeqRecord, по которой можем итерироваться. У меня вот так работает :)
| seqs = list(SeqIO.parse(input_path, "fastq")) | |
| seqs = SeqIO.parse(input_path, "fastq") |
| 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.
Тоже супер классные и полезные ошибки! Взяла себе на заметку!
There was a problem hiding this comment.
В данном случае тем не менее не совсем ясно какой же из аргументов не правильного типа. Так что лучше если проверять, то разделять
| dictionary = { | ||
| 'A': 'U', | ||
| 'G': 'C', | ||
| 'U': 'A', | ||
| 'C': 'G', | ||
| 'a': 'u', | ||
| 'g': 'c', | ||
| 'u': 'a', | ||
| 'c': 'g', | ||
| } |
There was a problem hiding this comment.
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.
Вернусь к теме про абстрактный класс. 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.
Проверка - супер разумная, но, во-первых, она должна быть определена в следующем поколении классов.
А во-вторых, она дублирует возврат 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.
Перед 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.
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.
Интересное решение с 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.
С точки зрения кода всё отлично, но биологически при вычисления массы белка нужно вычитать воду, например:
| 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.
Хе-хе, вот она разница между биоинформатиком и программистом!
| pass | ||
|
|
||
|
|
||
| class BiologicalSequence(str): |
There was a problem hiding this comment.
По заданию, BiologicalSequence должен был быть абстрактным классом, то есть никакого функционала тут быть не должно. Абстрактные классы создают только шаблон структуры.
| self.gc_cont = None | ||
|
|
||
| def complement(self): | ||
| return type(self)(''.join([type(self).dictionary[i] for i in self.sequence])) |
| class BiologicalSequence(str): | ||
| def __init__(self, sequence): | ||
| self.sequence = sequence | ||
|
|
There was a problem hiding this comment.
еще по заданию нужно было добавить вот это:
| def __str__(self) -> str: | |
| return self.sequence |
Zoea1
left a comment
There was a problem hiding this comment.
Очень классная работа, мне особо и сказать нечего (тем более, что много чего уже было сказано до меня). Ты молодец!
| pass | ||
|
|
||
|
|
||
| class BiologicalSequence(str): |
There was a problem hiding this comment.
По заданию это должен был быть абстрактный класс.
| "V": 99.06841, | ||
| "W": 186.07931, | ||
| "Y": 163.06333, | ||
| } |
Ну тут все таки важно чтобы вы учились комментировать код, поэтому нет ничего плохого в том чтобы повторять чьи-то комментарии если вам тоже они пришли в голову |
Review GPR183