Целью задания было при помощи фнкционала Prody найти остатки с наименьшим и наибольшим средними значениями B-фактора по всем атомам остатка.
import numpy as np
import prody as pd
from IPython.display import Image
import seaborn as sns
import pandas as pn
prot = pd.parsePDB("6AFM")
factor = []
for res in prot.iterResidues():
factor.append([res, np.mean(res.getBetas())])
factor_wo_water = [res for res in factor if "CA" in res[0].getNames()] # Убираю воду
print(sorted(factor_wo_water, key = lambda x: x[1])[0]) # Минимум
print(sorted(factor_wo_water, key = lambda x: x[1], reverse = True)[0]) # Максимум
print(sorted(prot.select('resnum 78').getBetas()))
print(sorted(prot.select('resnum 268').getBetas())) # Смотрим разброс В-фактора
Остаток с наименьшим средним значением В-фактора - ALA-78. Он расположен в альфа-спирали посчти в центре глобулы белка. Он явно расположен в гидрофобной области и не контактирует с растворителем. Значения по атомам у остатка отличаются от среднего не сильно.
Image("sup/pr3/res1.png")
ARG-268 - остаток с наибольшим значением. Он расположен в петле на краю глобулы и явно свободно контактирует с растворителем. Значения В-фактора у атомов остатка отличаются от среднего сильно. Это скорее всего связано с тем, что у атомов остова значения В-фактора низкие из за образования амидной связи.
Image("sup/pr3/res2.png")
Положение остатков в белке.
Image("sup/pr3/both.png")
В этом задании необходимо было исследовать связь средних значений В-фактора остатков и их расстоянием от центра масс белка.
protein_mass_center = pd.calcCenter(prot, weights=prot.getMasses())
dist = []
factors = []
for res in prot.iterResidues():
if 'CA' in res.getNames():
factor = np.mean(res.getBetas())
factors.append(factor)
mass_center = pd.calcCenter(res, weights=res.getMasses())
d = pd.calcDistance(protein_mass_center, mass_center)
dist.append(d)
plot = sns.jointplot(x=dist, y=factors, marker = ".")
plot.set_axis_labels('Distances between mass centers, Å', 'B-factor')
На основе получившейся картинки можно сделать следующие выводы:
Цель практикума попытаться воспроизвести ход кристаллографического эксперимента на умозрительном и крайне упрощенном примере. Мы сами сгенерируем электронную плотность, разложим ее в ряд Фурье, а затем будем восстанавливать исходную функцию, имитируя ту или иную степень потери данных в ходе эксперимента.
Для начала придумаем нашу молекулярную систему. Возьмём молекулы HCN и CO2 на расстоянии 3.5 друг от друга. Приводя эту систему к виду для скрипта compile-func.py получаем следующее: -g 1,3,6+6,3,7.06+7,3,8.21+8,3,11.71+6,3,12.87+8,3,14.03
Суммируя гаусианы получаем функцию вида:
true_density = pn.read_csv('sup/pr3/true_density.txt', sep = '\t', header = 0, names = ['x','y'])
sns.lineplot(x = true_density['x'], y = true_density['y'])
Теперь посмотрим сколько гармоник необходимо, чтобы однозначно восстановить исходную функцию.
harmonika_10 = pn.read_csv('sup/pr3/harmonika_10.txt', sep = '\t', header = 0, names = ['x','y1','y2'])
sns.lineplot(x = harmonika_10['x'], y = harmonika_10['y2'])
10 гармоник, разрешение 3 ангстрема. Как раз достаточно, чтобы различить отдельные молекулы. К сожелению совершенно не достаточно, чтобы различить отдельные атомы. Качество восстановления - плохое.
harmonika_20 = pn.read_csv('sup/pr3/harmonika_20.txt', sep = '\t', header = 0, names = ['x','y1','y2'])
sns.lineplot(x = harmonika_20['x'], y = harmonika_20['y2'])
20 гармоник, разрешение 1,5 ангстрема. Начинаем видеть отдельные атомы, но пока остаётся только гадать что это за атомы и сколько их. В реальной однако ситуации мы будем примерно знать, что за остаток перед нами. Качество восстановления - среднее.
harmonika_30 = pn.read_csv('sup/pr3/harmonika_30.txt', sep = '\t', header = 0, names = ['x','y1','y2'])
sns.lineplot(x = harmonika_30['x'], y = harmonika_30['y2'])
30 гармоник, разрешение 1 ангстрем. Хорошо видны все атомы. Электронная плотность позволяет предположить что это за атомы. Даже атом водорода виден на фоне шума. Качество восстановления - отличное.
Однако это очень чистые случаи. В реальном эксперименте шум будет не таким однородным. Потому теперь поиграем с ним.
noise_0_5 = pn.read_csv('sup/pr3/noise_0_5.txt', sep = '\t', header = 0, names = ['x','y1','y2'])
sns.lineplot(x = noise_0_5['x'], y = noise_0_5['y2'])
5% шум по фазам. Такой же шум только по амплитудамне даёт значимых изменений. Про это подробнее скажу ниже. Качество - отличное.
noise_20_20 = pn.read_csv('sup/pr3/noise_20_20.txt', sep = '\t', header = 0, names = ['x','y1','y2'])
sns.lineplot(x = noise_20_20['x'], y = noise_20_20['y2'])
20% шум по амплитудам и фазам. Теперь шум неоднороден как в реальном эксперименте, но в целом молекулы и крупные атомы различимы. Водород потерялся в шуме. Качество восстановления - хорошее.
noise_30_30 = pn.read_csv('sup/pr3/noise_30_30.txt', sep = '\t', header = 0, names = ['x','y1','y2'])
sns.lineplot(x = noise_30_30['x'], y = noise_30_30['y2'])
30% шум по амплитудам и фазам. Появляются области, в которых можно подозревать молекулы. В реальности, по контексту скорее всего будет понятно, что там ничего нет, но прецедент имеется. Качество - среднее.
noise_50_50 = pn.read_csv('sup/pr3/noise_50_50.txt', sep = '\t', header = 0, names = ['x','y1','y2'])
sns.lineplot(x = noise_50_50['x'], y = noise_50_50['y2'])
50% шум по амплитудам и фазам. Вот теперь уже различить что-либо совершенно невозможно. Шум сопоставим с сигналом. Качество восстановления - очень плохое.
Замечу, что шум по фазам давал более заметные искажения, чем шум по амплитудам не только на 5%, но и с другими процентами. Из этого можно сделать вывод, что фазы нам важнее, или, если точнее, восстановление электронной плотности более чувствительно к точности значений фаз, чем амплитуд.
Но это всё было с полным набором гармоник от 0 до 30. Попробуем взять неполный набор и посмотреть, что случится.
harmonika_2_30 = pn.read_csv('sup/pr3/harmonika_2_30.txt', sep = '\t', header = 0, names = ['x','y1','y2'])
sns.lineplot(x = harmonika_2_30['x'], y = harmonika_2_30['y2'])
Без двух первых гармоник фон "пошёл волной", и немного опустился ниже 0. Это в целом ни сколько не мешает. Все атомы хорошо различимы. И такой результат ожидаем, ведь нулевая гармоника - константа, а первая гармоники самая низкочастотная. Качество - отличное.
harmonika_14_17 = pn.read_csv('sup/pr3/harmonika_14_17.txt', sep = '\t', header = 0, names = ['x','y1','y2'])
sns.lineplot(x = harmonika_14_17['x'], y = harmonika_14_17['y2'])
Без трёх средних гармоник шум усилился, а высота пиков немного поплыла. Водород уже можно не отличить от фона. Качество - хорошее.
harmonika_40 = pn.read_csv('sup/pr3/harmonika_40.txt', sep = '\t', header = 0, names = ['x','y1','y2'])
sns.lineplot(x = harmonika_40['x'], y = harmonika_40['y2'])
Добавление 40 гармоники почти не влияет на график. Она содержит высокочастотные детали, которые излишни для нашего примера. Качество - отличное.
Таблица с подведением итогов.
pn.read_csv('sup/pr3/result.txt', sep = '\t')