Skip to content

Review GAD2#24

Open
nvaulin wants to merge 1 commit intomainfrom
GAD2
Open

Review GAD2#24
nvaulin wants to merge 1 commit intomainfrom
GAD2

Conversation

@nvaulin
Copy link
Copy Markdown
Member

@nvaulin nvaulin commented Feb 26, 2024

Review GAD2

Copy link
Copy Markdown

@grishchenkoira grishchenkoira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хорошая работа! 🐱 Очень понравился фильтратор. Ниже есть несколько замечаний

Comment thread GAD2.py
Comment on lines +1 to +5
import os
import re
from typing import TextIO, Optional, Union
from abc import ABC, abstractmethod
from Bio import SeqIO, SeqUtils
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Правильно остортировано по происхождению бибилиотек, но нет лексикографического порядка.

Suggested change
import os
import re
from typing import TextIO, Optional, Union
from abc import ABC, abstractmethod
from Bio import SeqIO, SeqUtils
import os
import re
from abc import ABC, abstractmethod
from typing import TextIO, Optional, Union
from Bio import SeqIO, SeqUtils

Comment thread GAD2.py
Comment on lines +72 to +73
complement_rule = {'a': 't', 'A': 'T', 't': 'a', 'T': 'A',
'g': 'c', 'G': 'C', 'c': 'g', 'C': 'G'}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здорово, что переменная вынесена до объявления всех методов. Она для всего класса и не предполагается, что она будет изменяться у экземпляров. Лучше записать заглавными буквами.

Comment thread GAD2.py
Function can procced only DNA seqences.
"""
transcribed_sequence = ''
transcribed_sequence = self.seq.replace('t', 'u').replace('T', 'U')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нет проверки на корректность. Если в сиквенсе будет буква, которой нет в алфавите, то она появится и в новой последовательности.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ну в целом проверка это не зона отвественности транскрайба

Comment thread GAD2.py
from abc import ABC, abstractmethod
from Bio import SeqIO, SeqUtils

class BiologicalSequence(ABC):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Отмечу здесь, потому что касается всех классов для последовательностей.

Нет аннотации типов.

Comment thread GAD2.py

def __init__(self, seq) -> None:
super().__init__(seq = seq)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нет класса для белковых последовательностей 😿

Comment thread GAD2.py
Comment on lines +191 to +192
if not input_path.endswith('.fastq'):
raise ValueError('Incorrect input file extension, should be .fastq')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здорово, что есть контроль входного файла!

Comment thread GAD2.py
Comment on lines +204 to +208
filtererd_fastq = open(os.path.join(output_path, output_filename), mode='w')
for seq_record in SeqIO.parse(input_path, "fastq"):
if check_gc(seq_record.seq, gc_params) and check_length(seq_record.seq, len_bound_params) and check_quality(seq_record, quality_threshold):
SeqIO.write(seq_record, filtererd_fastq, "fastq")
filtererd_fastq.close()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше использовать контекстный менеджер с оператором with, чтобы точно знать, что все закроется и не произойдет непредвиденного

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Даже в этом случае, согласен

Copy link
Copy Markdown

@KirPetrikov KirPetrikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Привет! Хорошая работа!
Куда-то потерялся AminoAcidSequence :'(
Функции для фильтрации, по-моему, уже можно было не выписывать отдельными, а реализовать сразу в run_fastq_filter. Но это не принципиально.
Докстринги - моё почтение!
Немного учесть замечания - и будет просто супер! Удачи!

Comment thread GAD2.py
from abc import ABC, abstractmethod
from Bio import SeqIO, SeqUtils

class BiologicalSequence(ABC):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Насколько я разобрался: абстрактный класс написан прям как надо

Comment thread GAD2.py
Comment on lines +22 to +23
"""Proccising nucleic acid sequences.
This class is parental for DNASequence and RNASequence classes.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Докстринги - замечательно.

Comment thread GAD2.py
"""Proccising nucleic acid sequences.
This class is parental for DNASequence and RNASequence classes.
"""
def __init__(self, seq) -> None:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Обратил внимание: у классов аннотация типов только для ретёрна у инитов. Кажется, инитам она как раз не очень нужна. И точно для аргументов можно добавить.

Suggested change
def __init__(self, seq) -> None:
def __init__(self, seq: str) -> None:

Comment thread GAD2.py
if self.check_sequence():
complement_sequence = ''
for nucleotide in self.seq:
if nucleotide in type(self).complement_rule:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здесь у экземпляра класса вызывается атрибут complement_rule, но в самом классе NucleicAcidSequence его нет. Кажется, это не очень корректно.
Предлагаю такой вариант: в NucleicAcidSequence ставим всем подобным атрибутам = None. Тогда проверку для этих атрибутов в методах делаем такую:

if self.complement_rule is None:
	raise NotImplementedError

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Когда пишите код в комментах PR, добавляйте python после ``` - так синтаксис будет подсвечен как надо

Comment thread GAD2.py
"""
if self.check_sequence():
complement_sequence = ''
for nucleotide in self.seq:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Такую проверку можно сделать не в цикле, а просто проверив подмножество:

if set(self.seq).issubset(set(complement_rule)):

Comment thread GAD2.py
return True


def int_to_tuple(input_parameters) -> tuple:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вынести это в отдельную функцию - очень правильное решение, по-моему, упрощает логику кода.

Comment thread GAD2.py
return (0, input_parameters)


def run_fastq_filter(input_path: Optional[str] = None, output_filename: Optional[str] = None, gc_bounds: Union[int, tuple] = (0, 100), length_bounds: Union[int, tuple] = (0, 2**32), quality_threshold: int = 0) -> TextIO:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А зачем дефолтный None для input_path? Всё равно упадёт с ошибкой при попытке прочитать None, но если у необходимого атрибута не будет дефолтного значения и его забудут указать, ошибка будет другая, понятнее.

Suggested change
def run_fastq_filter(input_path: Optional[str] = None, output_filename: Optional[str] = None, gc_bounds: Union[int, tuple] = (0, 100), length_bounds: Union[int, tuple] = (0, 2**32), quality_threshold: int = 0) -> TextIO:
def run_fastq_filter(input_path: Optional[str], output_filename: Optional[str] = None, gc_bounds: Union[int, tuple] = (0, 100), length_bounds: Union[int, tuple] = (0, 2**32), quality_threshold: int = 0) -> TextIO:

Comment thread GAD2.py
- reads quality score (quality_threshold).

Input:
- input_path (str): path to .fastq file; include 4 strings: 1 - read ID, 2 - sequence, 3 - comment, 4 - quality. Default - None.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Кажется, описание спецификации fastq-формата уже лишнее)

Suggested change
- input_path (str): path to .fastq file; include 4 strings: 1 - read ID, 2 - sequence, 3 - comment, 4 - quality. Default - None.
- input_path (str): path to .fastq file.

Comment thread GAD2.py
- output_filename (str): name of output file, by default, it will be saved in the directory 'fastq_filtrator_resuls'. Default name will be name of input file.
- gc_bounds (tuple or int): GC content filter parameters, it accepts lower and upper (tuple), or only upper threshold value (int). Default value (0, 100).
- length_bounds (tuple or int): read length filter parameters, it accepts lower and upper (tuple), or only upper threshold value (int). Default value (0, 2**32).
- quality_threshold (int): upper quality threshold in phred33 scale. Reads with average quality below the threshold are discarded. Default value - 0.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Только не верхняя граница, а нижняя.

Suggested change
- quality_threshold (int): upper quality threshold in phred33 scale. Reads with average quality below the threshold are discarded. Default value - 0.
- quality_threshold (int): lower quality threshold in phred33 scale. Reads with average quality below the threshold are discarded. Default value - 0.

Comment thread GAD2.py
gc_params = int_to_tuple(gc_bounds)
len_bound_params = int_to_tuple(length_bounds)
"Filter and record results"
filtererd_fastq = open(os.path.join(output_path, output_filename), mode='w')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Работать с файлами через with open надёжнее. Если, например, произойдёт ошибка перед filtererd_fastq.close(), файл не закроется.

Copy link
Copy Markdown

@Sarsaparella Sarsaparella left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Все аккуратно и чистно, радуют красивые докстринги и оформление. Некоторые стистические решения мне показались не очень понятными, но мб это мой недостаток. Нет класса AminoAcidSequence, ну ничего, он почти такой же как и NucleicAcidSequence, только там алфавит другой 😁

Comment thread GAD2.py
Comment on lines +25 to +26
def __init__(self, seq) -> None:
self.seq = seq
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я не очень понимаю зачем тут писать "-> None" 🤔 хотя сам концепт мне нравится (первый раз с ним сталкиваюсь и возьму себе на вооружение)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__init__ должен возвращать None по определению, иначе питон бросит ошибку, поэтому да, в случае с __init__ так делать не надо. Но когда просто функция кастомная, то там такая аннотация будет полезна

Comment thread GAD2.py
Comment on lines +48 to +49
def gc_content(self):
return (sum(1 for _ in re.finditer(r'[GCgc]', self.seq)))/self.__len__()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ничего плохого в этом способе нет, однако выглядит немного неинтуитивно что ли, сложно читат 🤧

Comment thread GAD2.py
'g': 'c', 'G': 'C', 'c': 'g', 'C': 'G'}

def __init__(self, seq: str) -> None:
super().__init__(seq = seq)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Возможно я чего-то не понимаю, но опять же не понятно зачем писать (seq = seq) 🤔

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В целом явное лучше чем не явное, поэтому прописывать в какие аргументы какие значения мы передаем - звучит полезно

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants