Conversation
anshtompel
left a comment
There was a problem hiding this comment.
Хорошая работа! Понравился фильтратор, а еще некоторые реализации функций, очень компактно. Спасибо за работу!
| def filter_fastq(input_path: str, | ||
| gc_bounds: tuple = (0, 100), | ||
| length_bounds: tuple = (0, 2 ** 32), |
There was a problem hiding this comment.
Всё круто! Хорошее решение делать фильтрацию, возвращая bool от проверок
| class BiologicalSequence(ABC, str): | ||
| @abstractmethod | ||
| def check_alphabet(self) -> bool: | ||
| pass |
There was a problem hiding this comment.
В вашем абстрактном классе не реализованы еще сет методов: работа c len, индексация и вывод в печать
| complement_seq = ''.join(type(self).COMPLEMENT_DICT.get(base) | ||
| for base in self.sequence) |
There was a problem hiding this comment.
Интересная конструкция) Возьму на заметку
| pass | ||
|
|
||
|
|
||
| class NucleicAcidSequence(BiologicalSequence): |
| ONE_LETTER_ALPHABET = ('A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', | ||
| 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'Y') | ||
| THREE_LETTER_ALPHABET = ('Ala', 'Cys', 'Asp', 'Glu', 'Phe', 'Gly', 'His', | ||
| 'Ile', 'Lys', 'Leu', 'Met', 'Pro', 'Gln', 'Arg', | ||
| 'Ser', 'Thr', 'Val', 'Trp', 'Tyr') |
There was a problem hiding this comment.
Круто, что предусмотрено два варианта алфавита
| class BiologicalSequence(ABC, str): | ||
| @abstractmethod | ||
| def check_alphabet(self) -> bool: | ||
| pass | ||
|
|
There was a problem hiding this comment.
Здесь надо было добавить еще абстрактных методов, но об этом уже сказали :) (Прости, я вижу чужое ревью, только когда проверяю)
There was a problem hiding this comment.
Все нормально! Нет ничего плохого в том чтобы повторять уже сказанные моменты, так как в первую очередь вам надо делать свое ревью.
| class RNASequence(NucleicAcidSequence): | ||
| ALPHABET = ('A', 'U', 'G', 'C', 'N') | ||
| COMPLEMENT_DICT = {'A': 'U', 'U': 'A', 'G': 'C', 'C': 'G', 'N': 'N', | ||
| 'a': 'u', 'u': 'a', 'g': 'c', 'c': 'g'} |
There was a problem hiding this comment.
Здорово! Выносить эти словари не в инит, по моему мнению очень разумно, как и не выносить их глобально за пределы классов.
There was a problem hiding this comment.
А еще теперь это классовые атрибуты, поэтому их не надо делать капсом
| def __init__(self, sequence): | ||
| raise NotImplementedError('An instance of this class cannot be created') |
There was a problem hiding this comment.
Здесь можно было бы при ините делать последовательность сразу большими буквами, чтобы потом в словаре не хранить и маленькие, и большие буквы. И в целом инит вынести в этот класс, и убрать его в дочерних, с точки зрения информации подаваемой, что АА, что РНК, что ДНК это все строчки с буковками. Так что я бы вместо ошибки добавила вот это, а потом от этого дальше наследовалась, чтобы не дублироваться.
| def __init__(self, sequence): | |
| raise NotImplementedError('An instance of this class cannot be created') | |
| def __init__(self, sequence): | |
| self.seq = seq.upper() |
| """ Reads a FASTQ file, filters sequences based on GC content, sequence | ||
| length and quality threshold, and writes the filtered sequences to | ||
| a new file """ |
There was a problem hiding this comment.
Спасибо за докстринг! Но мне казалось, что туда необходимо добавлять, какой инпут и какой аутпут функции идет. Что-то вот такое
| """ Reads a FASTQ file, filters sequences based on GC content, sequence | |
| length and quality threshold, and writes the filtered sequences to | |
| a new file """ | |
| """ | |
| Reads a FASTQ file, filters sequences based on GC content, sequence | |
| length and quality threshold, and writes the filtered sequences to | |
| a new file | |
| Args: | |
| input_path (str): Path to the input FASTQ file. | |
| gc_bounds (tuple): A tuple of two integers specifying the lower and | |
| upper bounds of the GC content, in percent. Sequences with GC | |
| content outside these bounds will be filtered out. | |
| length_bounds (tuple): A tuple of two integers specifying the lower and | |
| upper bounds of the sequence length, in base pairs. Sequences | |
| with length outside these bounds will be filtered out. | |
| quality_threshold (int): An integer specifying the minimum average | |
| quality score for a sequence to be kept. Sequences with average | |
| quality score below this threshold will be filtered out. | |
| """ |
|
|
||
| def filter_quality(record: SeqRecord, quality_threshold: int) -> bool: | ||
| avg_quality = np.mean(record.letter_annotations["phred_quality"]) | ||
| return avg_quality >= quality_threshold |
There was a problem hiding this comment.
мне очень нравится реализация этих функций: кратко и понятно (есть аннотация с каждому аргументу, почти все уместилось на одну строку, без лишних переменных)
однако я помню, что по заданию границы для длины и GC состава могут быть заданы как в виде одного числа, так и в виде интервала. в ваших функциях есть только интервал
| a new file """ | ||
| path, filename = os.path.split(input_path) | ||
| name, ext = os.path.splitext(filename) | ||
| output_path = path + "/" + f"{name}_filtered{ext}" |
There was a problem hiding this comment.
мне нравится способ генерации имени выходного файла, здесь также можно было учесть из тз, что пользователь сам тоже может задавать имя файла.
| output_path = path + "/" + f"{name}_filtered{ext}" | ||
| input_seq_iterator = SeqIO.parse(input_path, 'fastq') | ||
| filtered_seq_iterator = (record for record in input_seq_iterator | ||
| if filter_length(record, length_bounds) |
There was a problem hiding this comment.
отличный способ проверить все три условия и сразу записать, list comprehension тут лаконично и эффективно вписался. то, что функции возвращают bool позволяет его провести- это хороший подход
| class RNASequence(NucleicAcidSequence): | ||
| ALPHABET = ('A', 'U', 'G', 'C', 'N') | ||
| COMPLEMENT_DICT = {'A': 'U', 'U': 'A', 'G': 'C', 'C': 'G', 'N': 'N', | ||
| 'a': 'u', 'u': 'a', 'g': 'c', 'c': 'g'} |
There was a problem hiding this comment.
да, верно, словарь лучше вынести за функцию. помимо этого, мне нравится логика наследования методов и полиморфизм здесь: то есть методы проверки на ДНК/РНК и построения комплементарной цепи реализуются для каждого дочернего класса называются одинаково, но учитывают отличия дня/рнк
| def transcribe(self) -> RNASequence: | ||
| """Transcribes DNA sequence into RNA sequence. | ||
| Returns RMASequence object""" | ||
| return RNASequence(self.sequence.replace('T', 'U').replace('t', 'u')) |
There was a problem hiding this comment.
я также реализовала транскрипцию, но оказалось это не самый оптимальный вариант, так как двойной replace = двойной проход по последовательности.
молодец, что возвращаешь экземпляр класса RNASeq
|
|
||
| def get_molecular_weight(self) -> float: | ||
| """ calculate molecular weight for one-letter amino acid sequence""" | ||
| WEIGHT_DICT = { |
There was a problem hiding this comment.
насколько мне известно, словарь лучше вынести за функцию
| 'D': 115.087, 'Q': 128.129, 'K': 128.172, 'E': 129.114, 'M': 131.196, | ||
| 'H': 137.139, 'F': 147.174, 'R': 156.186, 'Y': 163.173, 'W': 186.210 | ||
| } | ||
| terminal_h_oh_weight = 18.02 |
There was a problem hiding this comment.
круто, даже масса терминальной hoh учтена)
Review PCDHA12