Хотим найти отличия между структурами на макро- и микроуровнях, полученными методами NMR (PDBid 6TO6) и RSA (6TRI) для комплекса репрессора хлорид-иона + антирепрессора modulator of repression (MOR). Название белка громоздкое и будет немного ясно, что он такое, узнав, что он делает. Вкратце, это контроллер генетического переключателя между литическим и лизогенным жизненными циклами умеренных бактериофагов, который работает после заражения фагом клетки хозяина.
Мы приводим изображение наслоения ансамбля моделей NMR (голубые) на модель RSA (черная).
from IPython.display import Image
Image(filename='NMRvsRSA.png')
На макроуровне (в пределах одной цепи) модели выглядят одинаково: один и тот же набор структурных элементов, располагающихся в одном и том же порядке. Нет сильных различий.
Image(filename='NMR1vsRSA.png')
На микроуровне структуры различаются. Хотя бы один вариант из ансамбля структур из NMR будет не совпадать по положению крайних участков молекулы с структурой из РСА. Особенно сильно различается положение крайних петель, чей набор пространственных ограничений обычно недостаточно большой, чтобы в методе NMR точно указать одну единственно законную структуру белка. И множество возможных вариантов порождает различия.
Image(filename='localvs.png')
Если представить себе ситуацию, когда ансамбль моделей NMR представляет собой отражение эволюции системы во времени. В таком случае имеет смысл построить зависимость меры подвижности отдельных участков молекулы (RMSF) от соответствующих значений B-факторов. Так мы сможем прикинуть, в какой мере ансамбль моделей в записи PDB, полученной методом ЯМР, есть отражение подвижности белка.
import prody as pd
to=pd.parsePDB('6to6')
@> PDB file is found in working directory (6to6.pdb.gz). @> 1183 atoms and 20 coordinate set(s) were parsed in 0.16s. @> PDB file is found in working directory (6tri.pdb.gz). @> 1224 atoms and 1 coordinate set(s) were parsed in 0.03s.
import matplotlib.pyplot as plt
import numpy
resTo=[res for res in to.iterResidues()]
resToAlign=resTo[6:59]
RMSFs = [numpy.mean(pd.calcRMSF(res)) for res in resToAlign]
print(len(RMSFs))
53
print(resToAlign=resTo[6:59])
tri=pd.parsePDB('6tri')
@> PDB file is found in working directory (6tri.pdb.gz). @> 1224 atoms and 1 coordinate set(s) were parsed in 0.04s.
resTri=[res for res in tri['B'].iterResidues()]
resTriAlign=resTri[3:56]
mean_betas = [] # заведем список, чтобы хранить в нем сведения о mean_beta каждого из остатков нашего белка
for residue in resTriAlign:
mean_beta = numpy.mean(residue.getBetas())
mean_betas.append(mean_beta)
print(len(mean_betas))
53
print(sorted(RMSFs, reverse=True)[0],
sorted(RMSFs, reverse=False)[0])
print(sorted(mean_betas, reverse=True)[0],
sorted(mean_betas, reverse=False)[0])
2.2707862861216364 0.7705022527214456 83.74625 38.695
Мы построили scatter-plot зависимости RMSF от B-factor. И опять видим слабую зависимость, как и в случае зависимости B-фактора остатка от его расстояния до центра масс белка. Значит, прямого отношения между факторами нет. И не выходят из головы погрешности, пришедшие из эксперимента, хотя мы здесь и договорились считать, что все идеально. В любом случае, мы не докопаемся до истины, если будем опираться исключительно на B-факторы при поиске причин неполноты данных ЯМР.
import numpy as np
def scatter_hist(x, y, ax, ax_histx, ax_histy):
# no labels
ax_histx.tick_params(axis="x", labelbottom=False)
ax_histy.tick_params(axis="y", labelleft=False)
# the scatter plot:
ax.scatter(x, y,fc='#a8dadc',ec='black')
# now determine nice limits by hand:
xbinwidth = 2
ybinwidth = 0.05
xmax = np.max(np.abs(x))
ymax = np.max(np.abs(y))
xlim = (int(xmax/xbinwidth) + 1) * xbinwidth
ylim = (int(ymax/ybinwidth) + 1) * ybinwidth
xbins = np.arange(-xlim, xlim + xbinwidth, xbinwidth)
ybins = np.arange(-ylim, ylim + ybinwidth, ybinwidth)
ax_histx.hist(x, bins=xbins,facecolor='#cddafd',edgecolor='black')
ax_histy.hist(y, bins=ybins, orientation='horizontal',facecolor='#caf0f8',edgecolor='black')
# definitions for the axes
left, width = 0.21, 0.65
bottom, height = 0.1, 0.65
spacing = 0.005
rect_scatter = [left, bottom, width, height]
rect_histx = [left, bottom + height + spacing, width, 0.2]
rect_histy = [left + width + spacing, bottom, 0.2, height]
# start with a square Figure
fig = plt.figure(figsize=(8, 8))
ax = fig.add_axes(rect_scatter)
ax_histx = fig.add_axes(rect_histx, sharex=ax)
ax_histy = fig.add_axes(rect_histy, sharey=ax)
ax.set_xlim(38,84)
ax.set_ylim(0.7,2.3)
ax.set_xlabel("Beta factor")
ax.set_ylabel('RMSF')
# use the previously defined function
scatter_hist(mean_betas, RMSFs, ax, ax_histx, ax_histy)
plt.show()
Выберем три пары водородных связей, между атомами, которые находятся в разных участках белка. Будет интересно
узнать по набору из 20 моделей полученных ЯМР для нашего контроллера генетического преключателя атомы каких участков молекулы чаще образуют друг с другом водородные связи.
Участок номер 1: водородная связь между остовными атомами белкового ядра. Сразу приходит в голову водородная связь между азотом iго остатка и карбонильным кислородом i+3го остатка на альфа спирали. Здесь это остатки L49 и H53.
Image(filename='HL.png')
Следующая водородная связь образована между боковыми радикалами K36 и E47, которые лежат в ядре белка, хотя заряженную и полярную аминокислоты в ядре было нелегко отыскать. На картинке неправильная подпись при глутамате, приносим извинения.
Image(filename='QK.png')
И наконец, связь образованная между юоковыми группами остатков N68 и K64, лежащих на поверхности белка.
Image(filename='NK.png')
Теперь, определившись с 3 водородными связями остатками, мы решили узнать их расстояние, использовав модели из РСА, и на моделях из ЯМР определить какие максимальные, минимальные и средние значения расстояний они имеют. Результаты приведены в таблице ниже.
#useless
resTo=[res for res in to.iterResidues()]
myRes=[i for i in resTo if str(i)[4:] in ['64','68','53','49','47','36']]
for i in myRes:
for j in i:
#if str(j)[5] not in {'H','C'}:
#print(j,j.getCoords(),i)
if str(j).split("index ")[1][:-1] in {'845','781','752','563','1110','1029'}:
continue#print(j,j.getCoords(),i)
giveIndex=['Atom NZ (index 563) LYS 36','Atom OE2 (index 752) GLU 47','Atom N (index 845) HIS 53','Atom O (index 781) LEU 49',
'Atom NZ (index 1029) LYS 64','Atom OD1 (index 1110) ASN 68']
RMSFs = [{str(res):numpy.mean(pd.calcRMSF(res))} for res in myRes]
print(RMSFs)
[{'LYS 36': 1.042491544727477}, {'GLU 47': 1.0551051015880772}, {'LEU 49': 1.3978567241765774}, {'HIS 53': 1.4064573478905318}, {'LYS 64': 1.701812259445263}, {'ASN 68': 4.224778915348745}]
qk=[3.8, 2.9, 2.7, 3.4, 3.0, 2.6, 2.7, 3.4, 3.0, 2.6, 5.5, 2.7, 4.4, 2.7, 3.4, 4.0, 2.7, 2.6, 3.5, 4.8]
nk=[15.2, 10.4, 16.0, 16.8, 7.6, 9.0, 8.2, 12.0, 11.2, 16.8, 17.3, 17.2, 12.4, 17.7, 15.1, 18.3, 12.0, 12.1, 14.9, 19.0]
lh=[3.2, 3.0, 3.2, 3.7, 3.0, 3.4, 3.1, 3.2, 3.1, 3.0, 3.3, 3.1, 3.1, 3.9, 3.6, 3.1, 3.2, 3.5, 3.1, 2.9]
#print(np.max(qk),np.min(qk),np.median(qk))
#print(np.max(nk),np.min(nk),np.median(nk))
#print(np.max(lh),np.min(lh),np.median(lh))
print(len([i for i in qk if i<=3.5]),len([i for i in qk if i<=3.5])/20*100)
print(len([i for i in lh if i<=3.5]),len([i for i in lh if i<=3.5])/20*100)
print(len([i for i in nk if i<=3.5]),len([i for i in nk if i<=3.5])/20*100)
15 75.0 17 85.0 0 0.0
import unicodedata
print('\N{Angstrom Sign}')
Å
parameters/Hbonds | E47-K36 | L49-H53 | K64-N68 |
---|---|---|---|
type | sidechain | backbone | sidechain |
distance in RSA(Å) | 3.0 | 3.2 | 3.5 |
atoms in Hbonds | O(Q) and N(K) | O(L) and N(H) | O(N) and N(K) |
location | kernel | kernel | surface |
number of NMR models with Hbonds | 15 | 17 | 0 |
% NMR models with Hbonds | 75.0 | 85.0 | 0.0 |
min length | 2.6 | 2.9 | 7.6 |
max length | 5.5 | 3.9 | 19.0 |
median length | 3.0 | 3.15 | 15.0 |
Примечательно, что Hbond между остатками, лежащих в ядре, представлены гораздо чаще, чем та, что между поверхностными остатками. Видимо, NMRструктуры не дают единой информации о поверхностных остатках, особенно тех, которые находятся на петлях. N68 лежит в петле, а мы видели еще на самой первой картинке как такие петли, собираясь в ансамбль, образуют неупорядоченную метелку. Очевидно поэтому мы видим в таблице неадекватные длины между атомами K64 и N68 и их отсутствие в моделях из NMR.