Conversation
alibibio
left a comment
There was a problem hiding this comment.
В целом все хорошо, молодец!
Есть пара замечаний в комментариях. А также желательно сделать обработку исключений.
В задании "Biological sequences world". Не обработан крайний случай: длина последовательностей 0.
| elif not output_filename.endswith('.fastq'): | ||
| output_filename += '.fastq' |
| filtered_seqs.append(record) | ||
|
|
||
| if output_filename is None: | ||
| output_filename = f"filtered_{input_path}" |
There was a problem hiding this comment.
Если в переменной 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) |
There was a problem hiding this comment.
Если будет буква "B", которой нет в словаре, то она перепишется в последовательность, что нарушает биологический смысл.
There was a problem hiding this comment.
Верно подмечено. Здорово тогда было бы добавить что это решается использованием не get а [...]. В целом через get я очень редко встречал чтобы делали
| 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 |
There was a problem hiding this comment.
сначала требует вызова 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): |
There was a problem hiding this comment.
Данный set лучше вынести из класса NucleicAcidSequence
Olga-Bagrova
left a comment
There was a problem hiding this comment.
Код классный! Пара моментов, которые, как показалось, можно было бы доработать есть, но ничего критичного. Всё запустилось. Успехов!
Бонус поразвлекаться. Запустите это: 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): |
There was a problem hiding this comment.
Переменные могут загрустить, что не знают свои типы здесь (но они точно есть в докстринге и это хорошо):
| 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: |
| """ | ||
| 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. | ||
| """ |
There was a problem hiding this comment.
Очень классная докстринга: понятно что делает функция, какие аргументы, их типы, а также дефолтные значения.
|
|
||
| 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 |
There was a problem hiding this comment.
Учтено, что могут быть заглавные и прописные буквы. Класс!
| """Initialize a DNASequence object with a given sequence.""" | ||
| super().__init__(sequence) | ||
| if not self.check_alphabet(): | ||
| raise ValueError("Invalid DNA sequence") |
There was a problem hiding this comment.
Круто, что есть встроенная проверка. Но если взять что-то типо DNASequence('AUTCWT'), то тоже пройдёт проверку на алфавит, хотя тут есть и U (РНК), и T (ДНК), и W (Белок).
|
|
||
| DNA_LETTERS = set("ATGCatgc") | ||
| RNA_LETTERS = set("AUGCaugc") | ||
| AMINO_ACID_LETTERS = set("ACDEFGHIKLMNPQRSTVWY") |
There was a problem hiding this comment.
Возможно аминокислотный алфавит стоило вынести из класса для нуклеиновых кислот, чтобы наши абстракции не нарушали биологию 😅. В целом классно, что учтено наличие больших и маленьких букв.
| 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) |
There was a problem hiding this comment.
Подкину к комментарию про метод get для словарей возможную альтернативу. Хотя конечно в дочерних классах и так идёт проверка алфавита при инициализации - это продуманно).
| 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 |
| 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") |
There was a problem hiding this comment.
Если просто запустить AminoAcidSequence('QWERTY').check_alphabet(), то ничего не вернёт. Для ДНК и РНК было классно сделано с return'ом булевого значения.
Cucumberan
left a comment
There was a problem hiding this comment.
Отличный код, прекрасная работа!
Кроме нескольких проверок на правильность вводимых последовательностей (на них указали ревьюеры до меня), все сделано замечательно, исправлять и предлагать мне нечего. Оставила лишь пару комментариев о том, как можно по-другому. А так все супер.
|
|
||
| def reverse(self): | ||
| """Return the reverse of the RNA sequence.""" | ||
| return RNASequence(self.sequence[::-1]) |
There was a problem hiding this comment.
Здесь также, как и в предыдущих случаях, можно возвращать просто развернутую последовательность, а не создавать новый экземпляр класса
| return RNASequence(self.sequence[::-1]) | |
| return self.sequence[::-1] |
There was a problem hiding this comment.
Ну не совсем.
У нас был объект сиквенс. Мы его развернули. Почему в ходе разворота тип данных изменила и последовательность превратилась в строку? Хотя тут проблема с тем что захардкожено имя класса, лучше через 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) |
There was a problem hiding this comment.
Мне кажется, что проверку на принадлежность к аминокислотной последовательности можно было бы убрать из класса NucleicAcidSequence и оставить только в классе AminoAcidSequence.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Здесь в методе transcribe возвращается просто строка transcribed_seq, а ниже в классе RNASequence при вызове метода reverse возращается новый объект класса RNASequence. Можно было бы для достижения единообразия и там и там возращать строки, либо чтобы после вызова метода transcribe создавался объект класса RNASequence.
| return transcribed_seq | |
| return RNASequence(transcribed_seq) | |
Ахахах |
Review GAGE5