Задание 1. Расчёт расстояний в ЯМР-моделях¶

Алимова Альфия

In [1]:
# импортируем библиотеки
from Bio.PDB import *
import numpy as np 
import pandas as pd
In [2]:
# скачиваем данные PDB-файла
p = PDBParser()
structure = p.get_structure("NMR_6L7K", "6l7k.pdb")

Посчитаем расстояния между атомами в трёх различных случаях. Для этого воспользуемся BioPython.
*Нумерация моделей начинается с нуля.

Disclaimer: в ЯМР-модели авторы сделали мутагенез: изменили Tyr-70 на Cys-70, Val-60 на Cys-60.
**В РСА-модели рассматривали связи между атомами остова остатков Leu-78 и Tyr-70.

In [3]:
# для атомов остова 
h_bond_main1 = []
h_bond_main2 = []

for model in structure:
    atom1 = model["A"][70]["N"].get_coord()
    atom2 = model["A"][78]["O"].get_coord()
    
    atom3 = model["A"][70]["O"].get_coord()
    atom4 = model["A"][78]["N"].get_coord()
    
    diff_vector1 = atom1 - atom2 # между кислородом Leu-78 и азотом Cys-70
    distance1 = np.sqrt(np.sum(diff_vector1 **2)).round(2)
    h_bond_main1.append(distance1)
    
    diff_vector2 = atom3 - atom4 # между кислородом Cys-70 и азотом Leu-78
    distance2 = np.sqrt(np.sum(diff_vector2 **2)).round(2)
    h_bond_main2.append(distance2)

Для Tyr-14 и Arg-126 в РСА-модели наблюдалась лишь одна водородная связь из возможных трёх, так как остальные две связи по длине превышают 3.0-3.5 Å. Здесь мы тоже рассматриваем только одну связь, однако другие связи могут появиться (как и наблюдамая в РСА-модели исчезнуть) при смене ЯМР-модели (проверено в PyMol).

In [4]:
# для атомов боковых радикалов аминокислот
h_bond_side = []

for model in structure:
    atom5 = model["A"][14]["OH"].get_coord()
    atom6 = model["A"][126]["NH2"].get_coord()
    
    diff_vector3 = atom5 - atom6 # между кислородом Tyr-14 и азотом Arg-126
    distance3 = np.sqrt(np.sum(diff_vector3 **2)).round(2)
    h_bond_side.append(distance3)
In [5]:
# в петлях на поверхности белковой глобулы
h_bond_surface = []

for model in structure:
    atom7 = model["A"][88]["NZ"].get_coord()
    atom8 = model["A"][107]["OE1"].get_coord()
    
    diff_vector4 = atom7 - atom8 # между кислородом Glu-107 и азотом Lys-88
    distance4 = np.sqrt(np.sum(diff_vector4 **2)).round(2)
    h_bond_surface.append(distance4)

Выведем таблицу всех измеренных расстояний.

In [6]:
h_bonds_res = ['Cys70_N_and_Leu78_O', 'Cys70_O_and_Leu78_N', 'Tyr14_O_and_Arg126_N', 'Glu107_O_and_Lys-88_N']
In [7]:
dist_all = pd.DataFrame(list(zip(h_bond_main1, h_bond_main2, h_bond_side, h_bond_surface)),
               columns = h_bonds_res)
dist_all.head()
Out[7]:
Cys70_N_and_Leu78_O Cys70_O_and_Leu78_N Tyr14_O_and_Arg126_N Glu107_O_and_Lys-88_N
0 2.80 3.19 2.85 6.44
1 2.80 3.18 4.22 8.04
2 2.87 3.20 4.06 6.92
3 2.86 3.19 2.38 8.82
4 2.73 2.79 4.16 6.95

Найдём среднее, максимальное и минимальное значение этих расстояний.

In [8]:
print(dist_all.describe().round(2))
       Cys70_N_and_Leu78_O  Cys70_O_and_Leu78_N  Tyr14_O_and_Arg126_N  \
count                20.00                20.00                 20.00   
mean                  2.85                 3.13                  3.40   
std                   0.06                 0.14                  0.82   
min                   2.73                 2.79                  2.38   
25%                   2.80                 3.18                  2.78   
50%                   2.86                 3.19                  3.08   
75%                   2.88                 3.19                  4.08   
max                   3.00                 3.21                  4.88   

       Glu107_O_and_Lys-88_N  
count                  20.00  
mean                    7.61  
std                     0.96  
min                     5.76  
25%                     6.93  
50%                     7.61  
75%                     8.16  
max                     9.56  

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

In [9]:
h_filter = dist_all < 3.5
dist_all_filter = dist_all[h_filter]
numb_rows = 20 - dist_all_filter.isna().sum() # расчёт количества строк в каждом столбце
percent_rows = (numb_rows / 20) * 100
print(numb_rows) # в скольких моделях присутствует данная водородная связь (в штуках)
print(percent_rows) # и в процентах
Cys70_N_and_Leu78_O      20
Cys70_O_and_Leu78_N      20
Tyr14_O_and_Arg126_N     12
Glu107_O_and_Lys-88_N     0
dtype: int64
Cys70_N_and_Leu78_O      100.0
Cys70_O_and_Leu78_N      100.0
Tyr14_O_and_Arg126_N      60.0
Glu107_O_and_Lys-88_N      0.0
dtype: float64
In [10]:
dist_all_filter.head() # вместо False - NaN (см. ниже)
Out[10]:
Cys70_N_and_Leu78_O Cys70_O_and_Leu78_N Tyr14_O_and_Arg126_N Glu107_O_and_Lys-88_N
0 2.80 3.19 2.85 NaN
1 2.80 3.18 NaN NaN
2 2.87 3.20 NaN NaN
3 2.86 3.19 2.38 NaN
4 2.73 2.79 NaN NaN

Итоговая табличка расстояний между донором и акцептором:

In [11]:
X_ray_dist = [2.8, 2.7, 3.5, 3.2] # расстояния в РСА-модели
In [12]:
final_df = pd.DataFrame(list(zip(X_ray_dist, list(numb_rows), list(percent_rows), list(dist_all.max()), list(dist_all.min()), list(dist_all.mean()))), 
                        columns=['X_ray_dist', 'Number_of_Models', 'Percentage_of_Models', 'Max_NMR_dist', 'Min_NMR_dist', 'Mean_NMR_dist'],
                       index = h_bonds_res)
final_df
Out[12]:
X_ray_dist Number_of_Models Percentage_of_Models Max_NMR_dist Min_NMR_dist Mean_NMR_dist
Cys70_N_and_Leu78_O 2.8 20 100.0 3.00 2.73 2.850
Cys70_O_and_Leu78_N 2.7 20 100.0 3.21 2.79 3.131
Tyr14_O_and_Arg126_N 3.5 12 60.0 4.88 2.38 3.401
Glu107_O_and_Lys-88_N 3.2 0 0.0 9.56 5.76 7.613