-
Notifications
You must be signed in to change notification settings - Fork 0
Review PRICKLE4 #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Review PRICKLE4 #25
Conversation
| def transcribe(self): | ||
| """ | ||
| Function return transcript of DNA sequence | ||
| """ | ||
| transcribe = self.seq.maketrans(self.dict_trans) | ||
| res = self.seq.translate(transcribe) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Усложнили немного)
Тут можно обойтись просто заменой T на U, вместо использования отдельного алфавита под транскрипцию
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Но в целом так тоже окей)
| alphabet = {'M', 'O', 'v', 'D', 'f', 'N', 'c', 'A', 'R', 'W', 'I', 'm', 'L', 's', 'H', 'q', 'w', 'V', 'n', 'i', | ||
| 'g', 'F', 'S', 'e', 'l', 'U', 'P', 'Q', 'K', 'Y', 'u', 'y', 'd', 'h', 'k', 'r', 't', 'G', 'o', 'E', | ||
| 'p', 'T', 'C', 'a'} | ||
| masses = {'A': 71.08, 'R': 156.2, 'N': 114.1, 'D': 115.1, 'C': 103.1, 'E': 129.1, 'Q': 128.1, 'G': 57.05, 'H': 137.1, | ||
| 'I': 113.2, 'L': 113.2, 'K': 128.2, 'M': 131.2, 'F': 147.2, 'P': 97.12, 'S': 87.08, 'T': 101.1, | ||
| 'W': 186.2, 'Y': 163.2, 'V': 99.13, 'U': 168.05, 'O': 255.3, 'a': 71.08, 'r': 156.2, 'n': 114.1, 'd': 115.1, | ||
| 'c': 103.1, 'e': 129.1, 'q': 128.1, 'g': 57.05, 'h': 137.1, 'i': 113.2, 'l': 113.2, 'k': 128.2, 'm': 131.2, | ||
| 'f': 147.2, 'p': 97.12, 's': 87.08, 't': 101.1, 'w': 186.2, 'y': 163.2, 'v': 99.13, 'u': 168.05, 'o': 255.3} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Такое количество алфавитов кажется избыточным. Если вы хотите проверить последовательность и определить массу, вы можете использовать всего один словарь)
| """ | ||
| Function return complement sequence | ||
| """ | ||
| complementary_dna = self.seq.maketrans(self.dict_comp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Крутой метод) возьму на заметку
| @abstractmethod | ||
| def to_print_seq(self): | ||
| return self.seq | ||
| pass | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Абстрактные методы в абстрактном классе определяют некий интерфейс, который будет унаследован дочерними классами. Тут подразумевалось, что в родительском классе Biological sequence вы скажите, что дочерние классы должны уметь считать длину, получать индексы и прочее. Например, про длину, это можно записать как
| @abstractmethod | |
| def to_print_seq(self): | |
| return self.seq | |
| pass | |
| @abstractmethod | |
| def __len__(self): | |
| pass | |
И потом переопределить нашу заглушку в дочернем, написав что-то осмысленное вместо pass
Как-то так)
| for nucl in self.seq: | ||
| if nucl == 'c' or nucl == 'g' or nucl == 'C' or nucl == 'G': | ||
| n += 1 | ||
| return 100 * n / len(self.seq) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Вместо цикла можно использовать методы строк, типа
| for nucl in self.seq: | |
| if nucl == 'c' or nucl == 'g' or nucl == 'C' or nucl == 'G': | |
| n += 1 | |
| return 100 * n / len(self.seq) | |
| n = self.seq.upper().count('G') + self.seq.upper().count('C') | |
| return 100 * n / len(self.seq) | |
либо использовать regex :)
| return RNASequence(res) | ||
|
|
||
|
|
||
| class RNASequence(BiologicalSequence): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
То же самое наследование от NucleicAcidSequence
| transcribe = self.seq.maketrans(self.dict_trans) | ||
| res = self.seq.translate(transcribe) | ||
|
|
||
| return RNASequence(res) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Круто, что предусмотрели, что возвращается объект другого класса!
| if not os.path.isdir('fastq_filtrator_resuls'): | ||
| os.mkdir('fastq_filtrator_resuls') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Папка для результатов - отличное решение)
| length_bounds_both_side = (0, length_bounds) | ||
| else: | ||
| length_bounds_both_side = length_bounds | ||
| records = list(SeqIO.parse(input_fastq, "fastq")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Думаю, так тоже рабочий вариант) Или же еще можно итерироваться по объектам SeqIO, и например, скормить только последовательность в виде seq_record.seq функцию filter_lenghth, кроме того метод .letter_annotations сам по себе спокойно работает с классом SeqRecord)
| SeqIO.write((record for record in filtered_records_q), output_fastq, "fastq") | ||
| return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Работа хорошая, мне понравилась) что то взяла себе на заметку. Удачи!
IuriiSl
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В целом работа хорошая. Немножко подправить абстрактные методы. Из-за путаницы в наследовании у ДНК и РНК не будет работать проверка алфавита. Есть парочка интересных решений)
| class BiologicalSequence(ABC, str): | ||
|
|
||
| def __init__(self, seq): | ||
| self.seq = seq |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Абстрактный класс должен содержать только абстрактные методы, которые определяют, что должно быть в дочерних классах
| complementary_dna = self.seq.maketrans(self.dict_comp) | ||
| res = self.seq.translate(complementary_dna) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Интересно реализовано
| alphabet = {'U', 'A', 'g', 'G', 'a', 'c', 'C', 'u'} | ||
| dict_comp = {'A': 'U', 'C': 'G', 'U': 'A', 'G': 'C', 'a': 'u', 'c': 'g', 'u': 'a', 'g': 'c'} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Хорошо, что предусмотрены регистры во всех алфавитах
| res = self.seq.translate(complementary_dna) | ||
| return NucleicAcidSequence(res) | ||
|
|
||
| class DNASequence(BiologicalSequence): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
По идее не должно работать, если бы абстрактный класс содержал абстрактные методы. И получается, что проверка алфавита отсутствует
| return RNASequence(res) | ||
|
|
||
|
|
||
| class RNASequence(BiologicalSequence): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
То же, что для DNASequence
| def filter_gc(records, gc_bounds_both_side=(0, 100)) -> list: | ||
| """ | ||
| This function selects sequences with the GC content of your interest | ||
| :parameters: | ||
| records: records from fastq parced by SeqIO | ||
| gc_bound: interval for the of acceptable GC content, in % | ||
| :return:(dict) new dictionary consists of selected sequences | ||
| """ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Видимо, забыли исправить докстрингу. Функция возвращает список, а не словарь.
| for record in records: | ||
| if (length_bounds_both_side[1] >= len(record.seq) >= length_bounds_both_side[0]): | ||
| new_records.append(record) | ||
| print(new_records) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| print(new_records) | |
| for record in records: | ||
| if (sum(record.letter_annotations["phred_quality"])/len(record.seq) >= quality_threshold): | ||
| new_records.append(record) | ||
| print(new_records) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| print(new_records) | |
zmitserbio
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В целом, работа неплохая, отдельные моменты отметил себе на будущее. Однако есть проблемы с не совсем корректной реализацией классов.
Еще, стоит прогнать код через линтер - периодически количество пустых строк не соответствует требуемому правилами. И лучше сделать аннотации типов.
| from Bio import SeqUtils | ||
|
|
||
|
|
||
| class BiologicalSequence(ABC, str): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Здесь не стоило наследоваться от строки, все же это абстрактный класс, ну и вообще, как оказалось, наследование от базового класса здесь не предполагалось.
|
|
||
| def __init__(self, seq): | ||
| self.seq = seq |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Как сама автор кода указывает ниже, здесь стоило оставить только абстрактные методы. Тот же комментарий к методам ниже.
| # не поняла, не доделала | ||
| @abstractmethod | ||
| def to_print_seq(self): | ||
| return self.seq | ||
| pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Здесь, если я правильно понимаю, предполагалась реализация "вывода на печать в удобном виде"? В таком случае, стоило это сделать через str. Вообще, как я понимаю, предполагалось, что наш объект имеет атрибут, в котором лежит строка с последовательностью. Тогда можно было бы сделать так:
| # не поняла, не доделала | |
| @abstractmethod | |
| def to_print_seq(self): | |
| return self.seq | |
| pass | |
| def __str__(self): | |
| return self.seq |
Как я понимаю, здесь логика была иной, учитывая наследование от строки. Такой объект тоже можно было бы перевести в тип строка, например, итерируясь по нему, добавляя элементы в list, а затем переведя лист в строку. Это не очень осмысленно, но если нужен именно тип строка, так можно было бы сделать.
Повторюсь только, что в абстрактном классе делать этого не стоило.
| class UnexpectedSymbolInSeqError(ValueError): | ||
| pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Приятно, что сделана специальная ошибка.
| alphabet = {'M', 'O', 'v', 'D', 'f', 'N', 'c', 'A', 'R', 'W', 'I', 'm', 'L', 's', 'H', 'q', 'w', 'V', 'n', 'i', | ||
| 'g', 'F', 'S', 'e', 'l', 'U', 'P', 'Q', 'K', 'Y', 'u', 'y', 'd', 'h', 'k', 'r', 't', 'G', 'o', 'E', | ||
| 'p', 'T', 'C', 'a'} | ||
| masses = {'A': 71.08, 'R': 156.2, 'N': 114.1, 'D': 115.1, 'C': 103.1, 'E': 129.1, 'Q': 128.1, 'G': 57.05, 'H': 137.1, | ||
| 'I': 113.2, 'L': 113.2, 'K': 128.2, 'M': 131.2, 'F': 147.2, 'P': 97.12, 'S': 87.08, 'T': 101.1, | ||
| 'W': 186.2, 'Y': 163.2, 'V': 99.13, 'U': 168.05, 'O': 255.3, 'a': 71.08, 'r': 156.2, 'n': 114.1, 'd': 115.1, | ||
| 'c': 103.1, 'e': 129.1, 'q': 128.1, 'g': 57.05, 'h': 137.1, 'i': 113.2, 'l': 113.2, 'k': 128.2, 'm': 131.2, | ||
| 'f': 147.2, 'p': 97.12, 's': 87.08, 't': 101.1, 'w': 186.2, 'y': 163.2, 'v': 99.13, 'u': 168.05, 'o': 255.3} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тут стоит отметить, что хотя решение не является неправильным, alphabet здесь не обязательная переменная (кстати, переменные стоит писать капсом). Ведь можно использовать set(masses.keys()). Это, конечно, не ошибка, но на будущее, это как будто несколько более экономно, не хранить избыточную информацию (особенно учитывая, что в реальных примерах она может весить гигабайты).
| :parameters: | ||
| records: records from fastq parced by SeqIO | ||
| gc_bound: interval for the of acceptable GC content, in % | ||
| :return:(dict) new dictionary consists of selected sequences |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Следует поправить тип возвращаемого объекта.
| :parameters: | ||
| records: records from fastq parced by SeqIO | ||
| length_bound: interval for the of acceptable sequense length in number of nucleotide | ||
| :return:(dict) new dictionary consists of selected sequences |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Аналогично.
| parameters: | ||
| seqs: dictionary of FASTQ sequences {name: (sequence, quality)} | ||
| quality_treshold: threshold value for average quality per nucleotide (phred33 scale) | ||
| :return:(dict) recordes for selected sequences |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
И еще раз.
| for record in records: | ||
| if (length_bounds_both_side[1] >= len(record.seq) >= length_bounds_both_side[0]): | ||
| new_records.append(record) | ||
| print(new_records) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не очень понятно, почему здесь принт... Видимо, забыли убрать?
| for record in records: | ||
| if (sum(record.letter_annotations["phred_quality"])/len(record.seq) >= quality_threshold): | ||
| new_records.append(record) | ||
| print(new_records) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
И снова принт.
Review PRICKLE4