Skip to content

Review RGL1#36

Open
nvaulin wants to merge 1 commit intomainfrom
RGL1
Open

Review RGL1#36
nvaulin wants to merge 1 commit intomainfrom
RGL1

Conversation

@nvaulin
Copy link
Copy Markdown
Member

@nvaulin nvaulin commented Feb 26, 2024

Review RGL1

Copy link
Copy Markdown

@nerofeeva2001 nerofeeva2001 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 RGL1.py
from typing import Union
from Bio import SeqIO, SeqUtils
from abc import ABC, abstractmethod

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Здорово, что используется Union, это упрощает понимание кода

Comment thread RGL1.py
if (quality_threshold < sum(record.letter_annotations['phred_quality']) /
len(record.letter_annotations['phred_quality'])):
return record

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Все три функции выше возвращают record или None. Это подход работает, но его можно упростить, изменив функции так, чтобы они возвращали True или False, что будет более понятно для проверки условий

Comment thread RGL1.py

def filter_fastq(file_path: str, output_file: str = 'input_fasta_name_filter.fastq',
lower_gc_bound: int = 0, upper_gc_bound: int = 30,
lower_length_bound: int = 0, upper_length_bound: int = 2**32,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Тут тоже можно было написать с помощью Union

Comment thread RGL1.py
gc_filter(record, lower_gc_bound, upper_gc_bound) and \
quality_filter(record, quality_threshold):
SeqIO.write(record, output, '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.

Повторная запись в файл для каждой подходящей записи через SeqIO.write(record, output, 'fastq') в цикле неэффективна, так как каждая запись обрабатывается отдельно. Лучше собрать подходящие записи в список, а затем записать их одним вызовом SeqIO.write(filtered_records, output, 'fastq')

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.

В данном случае есть 2 варианта

  1. Если читаем все разом, то и записывать лучше все разом, да
  2. Но можно обрабатывать данные "на лету" - прочитали одну запись и если надо - записали. В таком случае просто лучше держать все время файлы открытыми (делать все внутри блока with open). Так мне даже чуть больше нравится

Comment thread RGL1.py
gc_filter(record, lower_gc_bound, upper_gc_bound) and \
quality_filter(record, quality_threshold):
SeqIO.write(record, output, '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.

Нет проверки корректности диапазонов для lower_gc_bound, upper_gc_bound, lower_length_bound, upper_length_bound и quality_threshold

Comment thread RGL1.py

class SequenceFunction(BiologicalSequence):
alphabet = ()

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 RGL1.py
return self.seq[item]
else:
raise IndexError('Your index is incorrect')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Это избыточно, Python сам генерирует IndexError для индексов вне диапазона

Suggested change
def __getitem__(self, item: int) -> str:
return self.seq[item]

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 RGL1.py
def gc_content(self) -> Union[int, float]:
gc_count = self.seq.count('C') + self.seq.count('G')
return gc_count/len(self.seq)*100

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Аннотация типа возвращает Union[int, float], но на практике это всегда будет float

Suggested change
def gc_content(self) -> float:

Comment thread RGL1.py
def check_alphabet(self):
return super().check_alphabet()

def count_molecular_weight(self, amino_acid_weights: dict) -> int:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

В аннотации указано, что метод count_molecular_weight возвращает int, но на самом деле результатом будет float

Comment thread RGL1.py
return super().check_alphabet()

def count_molecular_weight(self, amino_acid_weights: dict) -> int:
molecular_weight = sum(self.amino_acid_weights.get(aa, 0) for aa 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.

Метод count_molecular_weight принимает параметр amino_acid_weights, который не используется. Вместо этого, используется атрибут класса

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
molecular_weight = sum(self.amino_acid_weights.get(aa, 0) for aa in self.seq)
def count_molecular_weight(self) -> int:
molecular_weight = sum(self.amino_acid_weights.get(aa, 0) for aa in self.seq)
return molecular_weight

Copy link
Copy Markdown

@LinaWhite15 LinaWhite15 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 RGL1.py

def length_filter(record, lower_length_bound: int, upper_length_bound: int):
if lower_length_bound <= len(record.seq) <= upper_length_bound:
return record
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Немного избыточно, можно либо True, либо False

Comment thread RGL1.py
def filter_fastq(file_path: str, output_file: str = 'input_fasta_name_filter.fastq',
lower_gc_bound: int = 0, upper_gc_bound: int = 30,
lower_length_bound: int = 0, upper_length_bound: int = 2**32,
quality_threshold: Union[int, float] = 0) -> 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.

Хорошее использование модулей

Comment thread RGL1.py
SeqIO.write(record, output, 'fastq')


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 RGL1.py
pass


class SequenceFunction(BiologicalSequence):
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 RGL1.py
self.length = len(self.seq)
return self.length

def __getitem__(self, item: int) -> str:
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 RGL1.py
super().__init__(seq)

def complement(self) -> str:
complement_seq = self.seq.translate(str.maketrans(self.complement_dict))
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 RGL1.py


class DNASequence(NucleicAcidSequence):
alphabet = ('A', 'T', 'G', 'C')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Регистрозависимость, на вход будут приниматься только записи большими буквами, возможно, стоит добавить upper в check_alphabet

Comment thread RGL1.py
super().__init__(seq)

def check_alphabet(self):
return super().check_alphabet()
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

@sofiyaga57 sofiyaga57 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 RGL1.py
@@ -0,0 +1,142 @@
from typing import Union
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Классно, что есть Union! Мне самое порой лениво его использовать, так что респект

Comment thread RGL1.py
from abc import ABC, abstractmethod


def length_filter(record, lower_length_bound: int, upper_length_bound: int):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Немножко не хватило аннотации, как-то было не очевидно сразу, что он может выдавать и record, и None

Comment thread RGL1.py
def filter_fastq(file_path: str, output_file: str = 'input_fasta_name_filter.fastq',
lower_gc_bound: int = 0, upper_gc_bound: int = 30,
lower_length_bound: int = 0, upper_length_bound: int = 2**32,
quality_threshold: Union[int, float] = 0) -> 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.

Like!

Comment thread RGL1.py
Comment on lines +66 to +70
def __getitem__(self, item: int) -> str:
if 0 <= item < len(self.seq):
return self.seq[item]
else:
raise IndexError('Your index is incorrect')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Немножко странно, что ошибка по индексу будет вылезать при отрицательных значениях индекса: это же ок для питона, мы можем обращаться к [-1] и тд

Comment thread RGL1.py
return complement_seq

def gc_content(self) -> Union[int, float]:
gc_count = self.seq.count('C') + self.seq.count('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.

Suggested change
gc_count = self.seq.count('C') + self.seq.count('G')
gc_count = sum(1 for base in self.seq if base in 'GC')

Можно попробовать так, как разбирали недавно на паре. Мне кажется, так будет более эффективно по скорости, потому что мы считаем один раз, а не два.

Comment thread RGL1.py
Comment on lines +23 to +24
lower_gc_bound: int = 0, upper_gc_bound: int = 30,
lower_length_bound: int = 0, upper_length_bound: int = 2**32,
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 RGL1.py
Comment on lines +94 to +95
alphabet = ('A', 'T', 'G', 'C')
complement_dict = {'A': 'T', 'T': 'A', 'G': 'C', '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.

Я бы добавила строчные буквы или тогда делала бы upper для входной строки

Comment thread RGL1.py
super().__init__(seq)

def complement(self) -> str:
complement_seq = self.seq.translate(str.maketrans(self.complement_dict))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Прикольно с maketrans:)) не знала о существовании этой функции

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