Задание 1. Prody и B-факторы часть 1

Целью задания было при помощи фнкционала Prody найти остатки с наименьшим и наибольшим средними значениями B-фактора по всем атомам остатка.

In [1]:
import numpy as np
import prody as pd

from IPython.display import Image

import seaborn as sns
import pandas as pn
In [2]:
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()] # Убираю воду
@> PDB file is found in working directory (6afm.pdb.gz).
@> 2496 atoms and 1 coordinate set(s) were parsed in 0.04s.
In [3]:
print(sorted(factor_wo_water, key = lambda x: x[1])[0]) # Минимум
print(sorted(factor_wo_water, key = lambda x: x[1], reverse = True)[0]) # Максимум
[<Residue: ALA 78 from Chain A from 6AFM (5 atoms)>, 3.864]
[<Residue: ARG 268 from Chain A from 6AFM (11 atoms)>, 31.670909090909092]
In [4]:
print(sorted(prot.select('resnum 78').getBetas()))
print(sorted(prot.select('resnum 268').getBetas())) # Смотрим разброс В-фактора
[3.55, 3.67, 3.86, 4.11, 4.13]
[14.81, 17.96, 19.82, 20.76, 30.25, 36.64, 39.13, 40.12, 42.64, 42.97, 43.28]

Остаток с наименьшим средним значением В-фактора - ALA-78. Он расположен в альфа-спирали посчти в центре глобулы белка. Он явно расположен в гидрофобной области и не контактирует с растворителем. Значения по атомам у остатка отличаются от среднего не сильно.

In [5]:
Image("sup/pr3/res1.png")
Out[5]:

ARG-268 - остаток с наибольшим значением. Он расположен в петле на краю глобулы и явно свободно контактирует с растворителем. Значения В-фактора у атомов остатка отличаются от среднего сильно. Это скорее всего связано с тем, что у атомов остова значения В-фактора низкие из за образования амидной связи.

In [6]:
Image("sup/pr3/res2.png")
Out[6]:

Положение остатков в белке.

In [7]:
Image("sup/pr3/both.png")
Out[7]:

Задание 2. Prody и B-факторы часть 2

В этом задании необходимо было исследовать связь средних значений В-фактора остатков и их расстоянием от центра масс белка.

In [8]:
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)
In [9]:
plot = sns.jointplot(x=dist, y=factors, marker = ".")
plot.set_axis_labels('Distances between mass centers, Å', 'B-factor')
Out[9]:
<seaborn.axisgrid.JointGrid at 0x1ea17e6aeb8>

На основе получившейся картинки можно сделать следующие выводы:

  • С ростом расстояния от центра масс белка растут значения В-фактора
  • Остатки с большими значениями В-фактора с большей вероятностью находятся на краю глобулы белка

Задание 3. Как работает восстановление функции электронной плотности по экспериментальным данным

Цель практикума попытаться воспроизвести ход кристаллографического эксперимента на умозрительном и крайне упрощенном примере. Мы сами сгенерируем электронную плотность, разложим ее в ряд Фурье, а затем будем восстанавливать исходную функцию, имитируя ту или иную степень потери данных в ходе эксперимента.

Для начала придумаем нашу молекулярную систему. Возьмём молекулы 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

Суммируя гаусианы получаем функцию вида:

In [10]:
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'])
Out[10]:
<matplotlib.axes._subplots.AxesSubplot at 0x1ea18096a58>

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

In [11]:
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'])
Out[11]:
<matplotlib.axes._subplots.AxesSubplot at 0x1ea1803c5c0>

10 гармоник, разрешение 3 ангстрема. Как раз достаточно, чтобы различить отдельные молекулы. К сожелению совершенно не достаточно, чтобы различить отдельные атомы. Качество восстановления - плохое.

In [12]:
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'])
Out[12]:
<matplotlib.axes._subplots.AxesSubplot at 0x1ea1819f240>

20 гармоник, разрешение 1,5 ангстрема. Начинаем видеть отдельные атомы, но пока остаётся только гадать что это за атомы и сколько их. В реальной однако ситуации мы будем примерно знать, что за остаток перед нами. Качество восстановления - среднее.

In [13]:
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'])
Out[13]:
<matplotlib.axes._subplots.AxesSubplot at 0x1ea191e56a0>

30 гармоник, разрешение 1 ангстрем. Хорошо видны все атомы. Электронная плотность позволяет предположить что это за атомы. Даже атом водорода виден на фоне шума. Качество восстановления - отличное.

Однако это очень чистые случаи. В реальном эксперименте шум будет не таким однородным. Потому теперь поиграем с ним.

In [14]:
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'])
Out[14]:
<matplotlib.axes._subplots.AxesSubplot at 0x1ea1922b860>

5% шум по фазам. Такой же шум только по амплитудамне даёт значимых изменений. Про это подробнее скажу ниже. Качество - отличное.

In [15]:
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'])
Out[15]:
<matplotlib.axes._subplots.AxesSubplot at 0x1ea1927f2e8>

20% шум по амплитудам и фазам. Теперь шум неоднороден как в реальном эксперименте, но в целом молекулы и крупные атомы различимы. Водород потерялся в шуме. Качество восстановления - хорошее.

In [16]:
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'])
Out[16]:
<matplotlib.axes._subplots.AxesSubplot at 0x1ea19302ef0>

30% шум по амплитудам и фазам. Появляются области, в которых можно подозревать молекулы. В реальности, по контексту скорее всего будет понятно, что там ничего нет, но прецедент имеется. Качество - среднее.

In [17]:
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'])
Out[17]:
<matplotlib.axes._subplots.AxesSubplot at 0x1ea19362438>

50% шум по амплитудам и фазам. Вот теперь уже различить что-либо совершенно невозможно. Шум сопоставим с сигналом. Качество восстановления - очень плохое.

Замечу, что шум по фазам давал более заметные искажения, чем шум по амплитудам не только на 5%, но и с другими процентами. Из этого можно сделать вывод, что фазы нам важнее, или, если точнее, восстановление электронной плотности более чувствительно к точности значений фаз, чем амплитуд.

Но это всё было с полным набором гармоник от 0 до 30. Попробуем взять неполный набор и посмотреть, что случится.

In [18]:
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'])
Out[18]:
<matplotlib.axes._subplots.AxesSubplot at 0x1ea193c1e48>

Без двух первых гармоник фон "пошёл волной", и немного опустился ниже 0. Это в целом ни сколько не мешает. Все атомы хорошо различимы. И такой результат ожидаем, ведь нулевая гармоника - константа, а первая гармоники самая низкочастотная. Качество - отличное.

In [19]:
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'])
Out[19]:
<matplotlib.axes._subplots.AxesSubplot at 0x1ea19413e10>

Без трёх средних гармоник шум усилился, а высота пиков немного поплыла. Водород уже можно не отличить от фона. Качество - хорошее.

In [20]:
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'])
Out[20]:
<matplotlib.axes._subplots.AxesSubplot at 0x1ea194598d0>

Добавление 40 гармоники почти не влияет на график. Она содержит высокочастотные детали, которые излишни для нашего примера. Качество - отличное.

Таблица с подведением итогов.

In [21]:
pn.read_csv('sup/pr3/result.txt', sep = '\t')
Out[21]:
Набор гармоник Разрешение (Å) Полнота данных (%) Шум амплитуды (% от величины F) Шум фазы (% от величины phi) Качество восстановления
0 0-10 3 Å 100% 0 0 Плохе
1 0-20 1,5 Å 100% 0 0 Среднее
2 0-30 1 Å 100% 0 0 Отличное
3 0-30 1 Å 100% 0 5 Отличное
4 0-30 1 Å 100% 20 20 Хорошее
5 0-30 1 Å 100% 30 30 Среднее
6 0-30 1 Å 100% 50 50 Очень плохое
7 2-30 1 Å 94% 0 0 Отличное
8 0-14, 18-30 1 Å 90% 0 0 Хорошее
9 0-30, 40 0,75 Å 78% 0 0 Отличное