Skip to content

Conversation

@nvaulin
Copy link
Member

@nvaulin nvaulin commented Feb 26, 2024

Review GAGE5

Copy link

@alibibio alibibio left a comment

Choose a reason for hiding this comment

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

В целом все хорошо, молодец!
Есть пара замечаний в комментариях. А также желательно сделать обработку исключений.
В задании "Biological sequences world". Не обработан крайний случай: длина последовательностей 0.

Comment on lines +34 to +35
elif not output_filename.endswith('.fastq'):
output_filename += '.fastq'
Copy link

Choose a reason for hiding this comment

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

супер

filtered_seqs.append(record)

if output_filename is None:
output_filename = f"filtered_{input_path}"
Copy link

Choose a reason for hiding this comment

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

Если в переменной input_path будет путь с использованием / или :, то такое имя файла будет невалидным.

def complement(self):
"""Return the complement sequence."""
comp_map_dna = {"A": "T", "G": "C", "T": "A", "C": "G", "a": "t", "t": "a", "g": "c", "c": "g"}
return ''.join(comp_map_dna.get(base, base) for base in self.sequence)
Copy link

Choose a reason for hiding this comment

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

Если будет буква "B", которой нет в словаре, то она перепишется в последовательность, что нарушает биологический смысл.

Copy link
Member Author

Choose a reason for hiding this comment

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

Верно подмечено. Здорово тогда было бы добавить что это решается использованием не get а [...]. В целом через get я очень редко встречал чтобы делали

Comment on lines +102 to +105
def transcribe(self):
"""Transcribe the DNA sequence into an RNA sequence."""
transcribed_seq = ''.join(self.TRANSCRIBE_DICT.get(base, base) for base in self.sequence)
return transcribed_seq
Copy link

Choose a reason for hiding this comment

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

сначала требует вызова complement(), а затем замену по словарю TRANSCRIBE_DICT


def check_alphabet(self):
"""Check if the sequence contains valid amino acid alphabet characters."""
if not set(self.sequence).issubset(NucleicAcidSequence.AMINO_ACID_LETTERS):
Copy link

Choose a reason for hiding this comment

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

Данный set лучше вынести из класса NucleicAcidSequence

Copy link

@Olga-Bagrova Olga-Bagrova left a comment

Choose a reason for hiding this comment

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

Код классный! Пара моментов, которые, как показалось, можно было бы доработать есть, но ничего критичного. Всё запустилось. Успехов!

Бонус поразвлекаться. Запустите это: print("\u2764\uFE0F")

from Bio.SeqUtils import GC
from abc import ABC, abstractmethod

def filter_fastq(input_path, gc_bounds=(0, 100), length_bounds=(0, float('inf')), quality_threshold=0, output_filename=None):

Choose a reason for hiding this comment

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

Переменные могут загрустить, что не знают свои типы здесь (но они точно есть в докстринге и это хорошо):

Suggested change
def filter_fastq(input_path, gc_bounds=(0, 100), length_bounds=(0, float('inf')), quality_threshold=0, output_filename=None):
def filter_fastq(input_path: str, gc_bounds: tuple = (0, 100), length_bounds: tuple = (0, float('inf')), quality_threshold: float = 0, output_filename: str = None)->str:

Comment on lines +6 to +18
"""
Filters a FASTQ file based on GC content, sequence length, and quality threshold using Biopython.

Args:
- input_path (str): Path to the input FASTQ file.
- gc_bounds (tuple): Tuple specifying the minimum and maximum GC content for filtering. Default is (0, 100).
- length_bounds (tuple): Tuple specifying the minimum and maximum sequence length for filtering. Default is (0, infinity).
- quality_threshold (float): Minimum quality score for filtering. Default is 0.
- output_filename (str): Name of the output file. If None, the default filename will be used.

Returns:
- str: Message indicating the success of the filtering process.
"""

Choose a reason for hiding this comment

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

Очень классная докстринга: понятно что делает функция, какие аргументы, их типы, а также дефолтные значения.


def gc_content(self):
"""Return the GC content of the sequence."""
gc_count = (self.sequence.upper().count('G') + self.sequence.upper().count('C')) / len(self.sequence) * 100

Choose a reason for hiding this comment

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

Учтено, что могут быть заглавные и прописные буквы. Класс!

"""Initialize a DNASequence object with a given sequence."""
super().__init__(sequence)
if not self.check_alphabet():
raise ValueError("Invalid DNA sequence")

Choose a reason for hiding this comment

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

Круто, что есть встроенная проверка. Но если взять что-то типо DNASequence('AUTCWT'), то тоже пройдёт проверку на алфавит, хотя тут есть и U (РНК), и T (ДНК), и W (Белок).

Copy link
Member Author

Choose a reason for hiding this comment

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

Верно подмечено


DNA_LETTERS = set("ATGCatgc")
RNA_LETTERS = set("AUGCaugc")
AMINO_ACID_LETTERS = set("ACDEFGHIKLMNPQRSTVWY")

Choose a reason for hiding this comment

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

Возможно аминокислотный алфавит стоило вынести из класса для нуклеиновых кислот, чтобы наши абстракции не нарушали биологию 😅. В целом классно, что учтено наличие больших и маленьких букв.

def complement(self):
"""Return the complement sequence."""
comp_map_dna = {"A": "T", "G": "C", "T": "A", "C": "G", "a": "t", "t": "a", "g": "c", "c": "g"}
return ''.join(comp_map_dna.get(base, base) for base in self.sequence)

Choose a reason for hiding this comment

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

Подкину к комментарию про метод get для словарей возможную альтернативу. Хотя конечно в дочерних классах и так идёт проверка алфавита при инициализации - это продуманно).

Suggested change
return ''.join(comp_map_dna.get(base, base) for base in self.sequence)
return ''.join([comp_map_dna[base] for base in self.sequence])

total_length = len(self.sequence)
for group, count in profile.items():
profile[group] = round((count / total_length), 2)
return profile

Choose a reason for hiding this comment

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

Классный метод!

Comment on lines +123 to +126
def check_alphabet(self):
"""Check if the sequence contains valid amino acid alphabet characters."""
if not set(self.sequence).issubset(NucleicAcidSequence.AMINO_ACID_LETTERS):
raise ValueError("Invalid amino acid sequence")

Choose a reason for hiding this comment

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

Если просто запустить AminoAcidSequence('QWERTY').check_alphabet(), то ничего не вернёт. Для ДНК и РНК было классно сделано с return'ом булевого значения.

Copy link

@Cucumberan Cucumberan left a comment

Choose a reason for hiding this comment

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

Отличный код, прекрасная работа!
Кроме нескольких проверок на правильность вводимых последовательностей (на них указали ревьюеры до меня), все сделано замечательно, исправлять и предлагать мне нечего. Оставила лишь пару комментариев о том, как можно по-другому. А так все супер.


def reverse(self):
"""Return the reverse of the RNA sequence."""
return RNASequence(self.sequence[::-1])

Choose a reason for hiding this comment

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

Здесь также, как и в предыдущих случаях, можно возвращать просто развернутую последовательность, а не создавать новый экземпляр класса

Suggested change
return RNASequence(self.sequence[::-1])
return self.sequence[::-1]

Copy link
Member Author

Choose a reason for hiding this comment

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

Ну не совсем.

У нас был объект сиквенс. Мы его развернули. Почему в ходе разворота тип данных изменила и последовательность превратилась в строку? Хотя тут проблема с тем что захардкожено имя класса, лучше через type(self)


def check_alphabet(self):
"""Check if the sequence contains valid nucleic- or aminoacid alphabet characters."""
return set(self.sequence).issubset(self.DNA_LETTERS | self.RNA_LETTERS | self.AMINO_ACID_LETTERS)

Choose a reason for hiding this comment

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

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

Copy link
Member Author

Choose a reason for hiding this comment

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

check_alphabet должа быть у всех из них
но про наборы букв - справедливо. Лучше иметь просто одну переменную - классовый атрибут alphabet с которым мы будем сравивать, но у разных классов он будет разный

def transcribe(self):
"""Transcribe the DNA sequence into an RNA sequence."""
transcribed_seq = ''.join(self.TRANSCRIBE_DICT.get(base, base) for base in self.sequence)
return transcribed_seq

Choose a reason for hiding this comment

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

Здесь в методе transcribe возвращается просто строка transcribed_seq, а ниже в классе RNASequence при вызове метода reverse возращается новый объект класса RNASequence. Можно было бы для достижения единообразия и там и там возращать строки, либо чтобы после вызова метода transcribe создавался объект класса RNASequence.

Suggested change
return transcribed_seq
return RNASequence(transcribed_seq)

@nvaulin
Copy link
Member Author

nvaulin commented Mar 17, 2024

Код классный! Пара моментов, которые, как показалось, можно было бы доработать есть, но ничего критичного. Всё запустилось. Успехов!

Бонус поразвлекаться. Запустите это: print("\u2764\uFE0F")

Ахахах
Запомните, дети, не запускайте непонятный код из интернетов

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.

5 participants