Skip to content

Review TRAF4#32

Open
nvaulin wants to merge 2 commits intomainfrom
TRAF4
Open

Review TRAF4#32
nvaulin wants to merge 2 commits intomainfrom
TRAF4

Conversation

@nvaulin
Copy link
Copy Markdown
Member

@nvaulin nvaulin commented Feb 26, 2024

Review TRAF4

Copy link
Copy Markdown

@nastasia-iv nastasia-iv left a comment

Choose a reason for hiding this comment

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

Хороший рабочий код 😊
Есть небольшие расхождения с условием заданий, внесла парочку исправлений и предложений.
Но в общем и целом всё гуд, молодец!)

Comment thread TRAF4.py
pass


class BiologicalSequence(str):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

В этой дз такое вроде было разрешено, но в целом лучше не наследоваться от встроенных типов данных. Подробнее об этом можно посмотреть в консультации от 28 февраля (примерно 28-я минута)

Suggested change
class BiologicalSequence(str):
class BiologicalSequence():

Comment thread TRAF4.py
'g': 'c', 'G': 'C',
'c': 'g', 'C': 'G'
}
if 'U' in self.sequence.upper():
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Здорово, что этот биологический момент предусмотрен!

Comment thread TRAF4.py
super().__init__(sequence)

def complement(self):
if self.complement_dict == None:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

С None лучше использовать is, а не ==:

Suggested change
if self.complement_dict == None:
if self.complement_dict is None:

Comment thread TRAF4.py

def check_seq(self):
valid_nucleotide_symbols = {'A', 'C', 'G', 'T', 'U'}
valid_prot_symbols = {'A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','Y'}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

По PEP-8 здесь стоит поставить пробелы после запятых :) Также можно было бы немного разнести символы по строкам, это бы улучшило восприятие кода

Comment thread TRAF4.py
't': 'u', 'T': 'U',
'g': 'g', 'G': 'G',
'c': 'c', 'C': 'C'
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Замечательно, что здесь подумали о читабельности кода и вывели элементы словаря на разных строках :)
Потерялась табуляция, возвращаю:

Suggested change
}
transcription_dict = {
'a': 'a', 'A': 'A',
't': 'u', 'T': 'U',
'g': 'g', 'G': 'G',
'c': 'c', 'C': 'C'
}

(по PEP-8 закрывающая скобка идёт на уровне с названием переменной)

Comment thread TRAF4.py
return f'AminoAcidSequence("{self.sequence}")'



Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Затесалась лишняя строка

Suggested change

Comment thread TRAF4.py
elif seq_symbols.issubset(valid_prot_symbols):
print('AA sequence')
else:
raise ValueError('Incorrect sequence input!')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Мне кажется, здесь тоже было бы здорово вызвать кастомную ошибку, тем более что одна из таких у вас уже определена в начале скрипта

Comment thread TRAF4.py
def __init__(self, sequence):
self.sequence = sequence

def check_seq(self):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Все так

Comment thread TRAF4.py
't': 'u', 'T': 'U',
'g': 'g', 'G': 'G',
'c': 'c', 'C': 'C'
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Тут получается, что метод transcribe создает словарь transcription_dict каждый раз, когда вызывается метод. Чтобы этого избежать, можно было бы создать этот словарь как атрибут класса, т.е. просто поставить его перед __init__

Comment thread TRAF4.py
- average phred scores, not less than specified
Default output file name 'filtered.fastq'
"""
records = list(SeqIO.parse(input_path, 'fastq'))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

В подобных случаях лучше обрабатывать записи в цикле, а не загружать всё целиком (на больших файлах может не хватить памяти):

Suggested change
records = list(SeqIO.parse(input_path, 'fastq'))
for record in SeqIO.parse(input_path, "fastq"):
...

Copy link
Copy Markdown

@iliapopov17 iliapopov17 left a comment

Choose a reason for hiding this comment

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

В целом мне всё понравилось!
Очень хороший код, всё супер классно.
Возможно, мои отдельные комментарии ощущались как-то душно, но я сам всё ещё смешарик в питоне, и специально для ревью сидел и вчитывался во всё что можно...

Comment thread TRAF4.py
super().__init__(sequence)

def complement(self):
if self.complement_dict == None:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Хорошо

if self.complement_dict == None:

Лучше

if self.complement_dict is None:

Suggested change
if self.complement_dict == None:
if self.complement_dict is None:

Comment thread TRAF4.py
from Bio.SeqUtils import gc_fraction


class NuclAcidnucleotideError(ValueError):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Класс NuclAcidnucleotideError:
Этот класс представляет собой пользовательскую ошибку для классов, связанных с нуклеиновыми кислотами. Очень хорошо! Однако, название класса немного запутанное + не соответствует CamelCase. Можно сделать его более ясным. Например, NucleotideError или InvalidNucleotideError.

Suggested change
class NuclAcidnucleotideError(ValueError):
class InvalidNucleotideError(ValueError):

Comment thread TRAF4.py
Comment on lines +94 to +119
def gravy(self):
"""Calculate GRAVY (grand average of hydropathy) value"""
gravy_aa_values = {'L': 3.8,
'K': -3.9,
'M': 1.9,
'F': 2.8,
'P': -1.6,
'S': -0.8,
'T': -0.7,
'W': -0.9,
'Y': -1.3,
'V': 4.2,
'A': 1.8,
'R': -4.5,
'N': -3.5,
'D': -3.5,
'C': 2.5,
'Q': -3.5,
'E': -3.5,
'G': -0.4,
'H': -3.2,
'I': 4.5}
gravy_aa_sum = 0
for amino_ac in self.sequence.upper():
gravy_aa_sum += gravy_aa_values[amino_ac]
return round(gravy_aa_sum / len(self.sequence), 3)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Можно использовать функцию sum с генератором, чтобы упростить код.

Suggested change
def gravy(self):
"""Calculate GRAVY (grand average of hydropathy) value"""
gravy_aa_values = {'L': 3.8,
'K': -3.9,
'M': 1.9,
'F': 2.8,
'P': -1.6,
'S': -0.8,
'T': -0.7,
'W': -0.9,
'Y': -1.3,
'V': 4.2,
'A': 1.8,
'R': -4.5,
'N': -3.5,
'D': -3.5,
'C': 2.5,
'Q': -3.5,
'E': -3.5,
'G': -0.4,
'H': -3.2,
'I': 4.5}
gravy_aa_sum = 0
for amino_ac in self.sequence.upper():
gravy_aa_sum += gravy_aa_values[amino_ac]
return round(gravy_aa_sum / len(self.sequence), 3)
def gravy(self):
gravy_aa_values = {'L': 3.8, 'K': -3.9, 'M': 1.9, ...}
return round(sum(gravy_aa_values[amino_ac] for amino_ac in self.sequence.upper()) / len(self.sequence), 3)

Comment thread TRAF4.py
Comment on lines +158 to +161
for count, record in enumerate(records):
gc_percent = gc_fraction(record.seq) * 100
if min_gc <= gc_percent <= max_gc:
filtered_1_gc_idxs.append(count)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Можно использовать более читаемую конструкцию for record in records вместо for count, record in enumerate(records).

Suggested change
for count, record in enumerate(records):
gc_percent = gc_fraction(record.seq) * 100
if min_gc <= gc_percent <= max_gc:
filtered_1_gc_idxs.append(count)
for record in records:
gc_percent = gc_fraction(record.seq) * 100
if min_gc <= gc_percent <= max_gc:
filtered_1_gc_idxs.append(record)

Comment thread TRAF4.py
Comment on lines +126 to +134
def make_thresholds(threshold: int | float | tuple) -> tuple:
"""Check thresholds input and convert single value to tuple"""
if isinstance(threshold, int) or isinstance(threshold, float):
lower = 0
upper = threshold
else:
lower = threshold[0]
upper = threshold[1]
return lower, upper
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Можно изменить make_thresholds для более явного понимания его назначения.

Suggested change
def make_thresholds(threshold: int | float | tuple) -> tuple:
"""Check thresholds input and convert single value to tuple"""
if isinstance(threshold, int) or isinstance(threshold, float):
lower = 0
upper = threshold
else:
lower = threshold[0]
upper = threshold[1]
return lower, upper
def make_thresholds(threshold: int | float | tuple) -> tuple:
if isinstance(threshold, (int, float)):
return 0, threshold
elif isinstance(threshold, tuple):
return threshold
else:
raise ValueError('Invalid threshold input')

Comment thread TRAF4.py
return f'AminoAcidSequence("{self.sequence}")'



Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Лишняя строчка

Suggested change

Comment thread TRAF4.py
Comment on lines +172 to +173
for idx in filtered_3_phred_idxs:
filtered_results.append(records[idx])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Можно использовать генераторное выражение для создания списка filtered_results.

Suggested change
for idx in filtered_3_phred_idxs:
filtered_results.append(records[idx])
filtered_results = [records[idx] for idx in filtered_3_phred_idxs]

Comment thread TRAF4.py
Comment on lines +37 to +41
def complement(self):
if self.complement_dict == None:
raise NotImplementedError('It is a basic NA class. You should implement it for descendant class: DNASequence or RNASequence.')
result = type(self)(''.join([self.complement_dict[nuc] for nuc in self.sequence]))
return result
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

В методе complement можно использовать метод .get для получения значения из словаря. Это позволит избежать ошибок, если нуклеотид отсутствует в словаре.

Suggested change
def complement(self):
if self.complement_dict == None:
raise NotImplementedError('It is a basic NA class. You should implement it for descendant class: DNASequence or RNASequence.')
result = type(self)(''.join([self.complement_dict[nuc] for nuc in self.sequence]))
return result
def complement(self):
if not all(nuc in self.complement_dict for nuc in self.sequence):
raise NuclAcidnucleotideError('Invalid nucleotide in the sequence')
result = type(self)(''.join([self.complement_dict.get(nuc, '') for nuc in self.sequence]))
return result

Comment thread TRAF4.py
Comment on lines +93 to +119
class AminoAcidSequence(BiologicalSequence):
def gravy(self):
"""Calculate GRAVY (grand average of hydropathy) value"""
gravy_aa_values = {'L': 3.8,
'K': -3.9,
'M': 1.9,
'F': 2.8,
'P': -1.6,
'S': -0.8,
'T': -0.7,
'W': -0.9,
'Y': -1.3,
'V': 4.2,
'A': 1.8,
'R': -4.5,
'N': -3.5,
'D': -3.5,
'C': 2.5,
'Q': -3.5,
'E': -3.5,
'G': -0.4,
'H': -3.2,
'I': 4.5}
gravy_aa_sum = 0
for amino_ac in self.sequence.upper():
gravy_aa_sum += gravy_aa_values[amino_ac]
return round(gravy_aa_sum / len(self.sequence), 3)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Константу - словарь gravy_aa_values можно определить в начале класса, что поможет улучшить их видимость и управление.

Suggested change
class AminoAcidSequence(BiologicalSequence):
def gravy(self):
"""Calculate GRAVY (grand average of hydropathy) value"""
gravy_aa_values = {'L': 3.8,
'K': -3.9,
'M': 1.9,
'F': 2.8,
'P': -1.6,
'S': -0.8,
'T': -0.7,
'W': -0.9,
'Y': -1.3,
'V': 4.2,
'A': 1.8,
'R': -4.5,
'N': -3.5,
'D': -3.5,
'C': 2.5,
'Q': -3.5,
'E': -3.5,
'G': -0.4,
'H': -3.2,
'I': 4.5}
gravy_aa_sum = 0
for amino_ac in self.sequence.upper():
gravy_aa_sum += gravy_aa_values[amino_ac]
return round(gravy_aa_sum / len(self.sequence), 3)
class AminoAcidSequence(BiologicalSequence):
GRAVY_AA_VALUES = {'L': 3.8, 'K': -3.9, ...}
def gravy(self):
return round(sum(AminoAcidSequence.GRAVY_AA_VALUES[amino_ac] for amino_ac in self.sequence.upper()) / len(self.sequence), 3)

Comment thread TRAF4.py
self.sequence = sequence

def check_seq(self):
valid_nucleotide_symbols = {'A', 'C', 'G', 'T', 'U'}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Как я поняла из задания, проверки необходимо делать в дочерних классах, это абстрактный класс

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Верно

Comment thread TRAF4.py
else:
raise ValueError('Incorrect sequence input!')

def __str__(self):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

сначала вы насладитесь от str, а потом пишете метод def str. как я понимаю, тут наследование от str- лишнее тогда

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

В целом нет, __str__ же задает правило отображения, мы вполне можем хотеть их переопределить

Comment thread TRAF4.py
'c': 'g', 'C': 'G'
}
if 'T' in self.sequence.upper():
raise NuclAcidnucleotideError('T-contain sequence is not proper RNA sequence')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

молодец, что тут и раннее пишешь кастомные ошибки. очень хорошо вышло

Comment thread TRAF4.py
raise NuclAcidnucleotideError('U-contain sequence is not proper DNA sequence')

def transcribe(self):
transcription_dict = {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

словарь лучше бы вынести за функцию, в начало класса

Comment thread TRAF4.py

class NucleicAcidSequence(BiologicalSequence):
def __init__(self, sequence):
self.complement_dict = None
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

отличный подход с complement_dict = None в родительском классе и его последующей спецификации в дочерних классах

Comment thread TRAF4.py
raise NuclAcidnucleotideError('U-contain sequence is not proper DNA sequence')

def transcribe(self):
transcription_dict = {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Comment thread TRAF4.py
Comment on lines +126 to +134
def make_thresholds(threshold: int | float | tuple) -> tuple:
"""Check thresholds input and convert single value to tuple"""
if isinstance(threshold, int) or isinstance(threshold, float):
lower = 0
upper = threshold
else:
lower = threshold[0]
upper = threshold[1]
return lower, upper
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Comment thread TRAF4.py
Comment on lines +158 to +173
for count, record in enumerate(records):
gc_percent = gc_fraction(record.seq) * 100
if min_gc <= gc_percent <= max_gc:
filtered_1_gc_idxs.append(count)

for idx in filtered_1_gc_idxs:
if min_len <= len(records[idx].seq) <= max_len:
filtered_2_len_idxs.append(idx)

for idx in filtered_2_len_idxs:
phred_values = records[idx].letter_annotations['phred_quality']
if sum(phred_values) / len(phred_values) >= quality_threshold:
filtered_3_phred_idxs.append(idx)

for idx in filtered_3_phred_idxs:
filtered_results.append(records[idx])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

все логично, но очень громоздко. как известно, чем проще и короче, тем легче читается код и быстрее выполняется, можно было бы записать каждый из фильтров как свою функцию, а потом проверить для каждой последовательности выполнение трех условий (вывод каждой функции True/False).

но твой вариант тоже рабочий, это главное

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