Skip to content

Conversation

@nvaulin
Copy link
Member

@nvaulin nvaulin commented Feb 26, 2024

Review PRICKLE4

Comment on lines +88 to +93
def transcribe(self):
"""
Function return transcript of DNA sequence
"""
transcribe = self.seq.maketrans(self.dict_trans)
res = self.seq.translate(transcribe)

Choose a reason for hiding this comment

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

Усложнили немного)
Тут можно обойтись просто заменой T на U, вместо использования отдельного алфавита под транскрипцию

Copy link
Member Author

Choose a reason for hiding this comment

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

Но в целом так тоже окей)

Comment on lines +31 to +38
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}

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)

Choose a reason for hiding this comment

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

Крутой метод) возьму на заметку

Comment on lines +20 to +24
@abstractmethod
def to_print_seq(self):
return self.seq
pass

Choose a reason for hiding this comment

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

Абстрактные методы в абстрактном классе определяют некий интерфейс, который будет унаследован дочерними классами. Тут подразумевалось, что в родительском классе Biological sequence вы скажите, что дочерние классы должны уметь считать длину, получать индексы и прочее. Например, про длину, это можно записать как

Suggested change
@abstractmethod
def to_print_seq(self):
return self.seq
pass
@abstractmethod
def __len__(self):
pass

И потом переопределить нашу заглушку в дочернем, написав что-то осмысленное вместо pass
Как-то так)

Comment on lines +67 to +70
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)

Choose a reason for hiding this comment

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

Вместо цикла можно использовать методы строк, типа

Suggested change
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):

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)

Choose a reason for hiding this comment

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

Круто, что предусмотрели, что возвращается объект другого класса!

Comment on lines +169 to +170
if not os.path.isdir('fastq_filtrator_resuls'):
os.mkdir('fastq_filtrator_resuls')

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"))

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)

Comment on lines +187 to +188
SeqIO.write((record for record in filtered_records_q), output_fastq, "fastq")
return

Choose a reason for hiding this comment

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

Работа хорошая, мне понравилась) что то взяла себе на заметку. Удачи!

Copy link

@IuriiSl IuriiSl left a comment

Choose a reason for hiding this comment

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

В целом работа хорошая. Немножко подправить абстрактные методы. Из-за путаницы в наследовании у ДНК и РНК не будет работать проверка алфавита. Есть парочка интересных решений)

Comment on lines +7 to +10
class BiologicalSequence(ABC, str):

def __init__(self, seq):
self.seq = seq
Copy link

Choose a reason for hiding this comment

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

Абстрактный класс должен содержать только абстрактные методы, которые определяют, что должно быть в дочерних классах

Comment on lines +76 to +77
complementary_dna = self.seq.maketrans(self.dict_comp)
res = self.seq.translate(complementary_dna)
Copy link

Choose a reason for hiding this comment

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

Интересно реализовано

Comment on lines +99 to +100
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'}
Copy link

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):
Copy link

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):
Copy link

Choose a reason for hiding this comment

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

То же, что для DNASequence

Comment on lines +106 to +113
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
"""
Copy link

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)
Copy link

Choose a reason for hiding this comment

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

Suggested change
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)
Copy link

Choose a reason for hiding this comment

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

Suggested change
print(new_records)

Copy link

@zmitserbio zmitserbio left a 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):

Choose a reason for hiding this comment

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

Здесь не стоило наследоваться от строки, все же это абстрактный класс, ну и вообще, как оказалось, наследование от базового класса здесь не предполагалось.

Comment on lines +8 to +10

def __init__(self, seq):
self.seq = seq

Choose a reason for hiding this comment

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

Как сама автор кода указывает ниже, здесь стоило оставить только абстрактные методы. Тот же комментарий к методам ниже.

Comment on lines +19 to +23
# не поняла, не доделала
@abstractmethod
def to_print_seq(self):
return self.seq
pass

Choose a reason for hiding this comment

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

Здесь, если я правильно понимаю, предполагалась реализация "вывода на печать в удобном виде"? В таком случае, стоило это сделать через str. Вообще, как я понимаю, предполагалось, что наш объект имеет атрибут, в котором лежит строка с последовательностью. Тогда можно было бы сделать так:

Suggested change
# не поняла, не доделала
@abstractmethod
def to_print_seq(self):
return self.seq
pass
def __str__(self):
return self.seq

Как я понимаю, здесь логика была иной, учитывая наследование от строки. Такой объект тоже можно было бы перевести в тип строка, например, итерируясь по нему, добавляя элементы в list, а затем переведя лист в строку. Это не очень осмысленно, но если нужен именно тип строка, так можно было бы сделать.
Повторюсь только, что в абстрактном классе делать этого не стоило.

Comment on lines +26 to +27
class UnexpectedSymbolInSeqError(ValueError):
pass

Choose a reason for hiding this comment

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

Приятно, что сделана специальная ошибка.

Comment on lines +31 to +38
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}

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

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

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

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)

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)

Choose a reason for hiding this comment

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

И снова принт.

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