Hierarchical Views¶
This part describes how to use hierarchical views. We start by importing everything from the ProDy package:
In [1]: from prody import *
Hierarchical Views¶
Then we parses a structure to get an AtomGroup
instance which has a
plain view of atoms:
In [2]: structure = parsePDB('3mkb')
In [3]: structure
Out[3]: <AtomGroup: 3mkb (4776 atoms)>
A hierarchical view of the structure can be simply get by calling the
AtomGroup.getHierView()
method:
In [4]: hv = structure.getHierView()
In [5]: hv
Out[5]: <HierView: AtomGroup 3mkb (4 chains, 946 residues)>
Indexing¶
Indexing HierView
instances return Chain
:
In [6]: hv['A']
Out[6]: <Chain: A from 3mkb (254 residues, 1198 atoms)>
In [7]: hv['B']
Out[7]: <Chain: B from 3mkb (216 residues, 1193 atoms)>
In [8]: hv['Z'] # This will return None, which means chain Z does not exist
The length of the hv variable gives the number of chains in the structure:
In [9]: len(hv)
Out[9]: 4
In [10]: hv.numChains()
Out[10]: 4
It is also possible to get a Residue
by
directly indexing the HierView
instance:
In [11]: hv['A', 100]
Out[11]: <Residue: MET 100 from Chain A from 3mkb (8 atoms)>
Insertion codes can also be passed:
In [12]: hv['A', 100, 'B']
This does not return anything, since residue 100B does not exist.
Iterations¶
One can iterate over HierView
instances to get chains:
In [13]: for chain in hv:
....: print(chain)
....:
Chain A
Chain B
Chain C
Chain D
It is also possible to get a list()
of chains simply as follows:
In [14]: chains = list(hv)
In [15]: chains
Out[15]:
[<Chain: A from 3mkb (254 residues, 1198 atoms)>,
<Chain: B from 3mkb (216 residues, 1193 atoms)>,
<Chain: C from 3mkb (245 residues, 1189 atoms)>,
<Chain: D from 3mkb (231 residues, 1196 atoms)>]
Residues¶
In addition, one can also iterate over all residues:
In [16]: for i, residue in enumerate(hv.iterResidues()):
....: if i == 4: break
....: print(residue)
....:
ALA 1
PHE 2
THR 3
GLY 4
Chains¶
We obtain a Chain
object for chain A by indexing the HierView
object.
In [17]: chA = hv['A']
In [18]: chA
Out[18]: <Chain: A from 3mkb (254 residues, 1198 atoms)>
The length of the chain is equal to the number of residues in it:
In [19]: len(chA)
Out[19]: 254
In [20]: chA.numResidues()
Out[20]: 254
Indexing¶
Indexing a Chain
instance with a scalar returns an Atom
like indexing an Atomgroup
:
In [21]: chA[1]
Out[21]: <Atom: CA from 3mkb (index 1)>
In [22]: chA[1000]
Out[22]: <Atom: C from 3mkb (index 1000)>
Indexing a Chain
instance with a tuple returns an Residue
. This can also be achieved with the
prody.atomic.chain.Chain.getResidue()
method.
In [23]: chA[(1,)]
Out[23]: <Residue: ALA 1 from Chain A from 3mkb (5 atoms)>
In [24]: chA.getResidue(1)
Out[24]: <Residue: ALA 1 from Chain A from 3mkb (5 atoms)>
The use of a tuple is necessary to allow inclusion of insertion codes. In this case there aren’t any so None
is returned.
In [25]: chA[1, 'A'] # Residue 1 with insertion code A also does not exist
Iterations¶
Iterating over a chain also yields residues:
In [26]: for i, residue in enumerate(chA):
....: if i == 4: break
....: print(residue)
....:
ALA 1
PHE 2
THR 3
GLY 4
Note that water atoms, each constituting a residue, are also part of a chain if they are labeled with that chain’s identifier.
This enables getting a list()
of residues simply as follows:
In [27]: chA_residues = list(chA)
In [28]: chA_residues[:4]
Out[28]:
[<Residue: ALA 1 from Chain A from 3mkb (5 atoms)>,
<Residue: PHE 2 from Chain A from 3mkb (11 atoms)>,
<Residue: THR 3 from Chain A from 3mkb (7 atoms)>,
<Residue: GLY 4 from Chain A from 3mkb (4 atoms)>]
In [29]: chA_residues[-4:]
Out[29]:
[<Residue: HOH 471 from Chain A from 3mkb (1 atoms)>,
<Residue: HOH 485 from Chain A from 3mkb (1 atoms)>,
<Residue: HOH 490 from Chain A from 3mkb (1 atoms)>,
<Residue: HOH 493 from Chain A from 3mkb (1 atoms)>]
Get data¶
All methods defined for AtomGroup
class are also defined for
Chain
and Residue
classes:
In [30]: chA.getCoords()
Out[30]:
array([[ -2.139, 17.026, -13.287],
[ -1.769, 15.572, -13.111],
[ -0.296, 15.257, -13.467],
...,
[ -5.843, 17.181, -16.86 ],
[-13.199, -9.21 , -49.692],
[ -0.459, 0.378, -46.156]])
In [31]: chA.getBetas()
Out[31]: array([59.35, 59.14, 58.5 , ..., 57.79, 47.77, 40.77])
Selections¶
Finally, you can select atoms from a Chain
instance:
In [32]: chA_backbone = chA.select('backbone')
In [33]: chA_backbone
Out[33]: <Selection: '(backbone) and (chain A)' from 3mkb (560 atoms)>
In [34]: chA_backbone.getSelstr()
Out[34]: '(backbone) and (chain A)'
As you see, the selection string passed by the user is augmented with “chain” keyword and identifier automatically to provide internal consistency:
In [35]: structure.select(chA_backbone.getSelstr())
Out[35]: <Selection: '(backbone) and (chain A)' from 3mkb (560 atoms)>
Residues¶
In [36]: chA_res1 = chA.getResidue(1)
In [37]: chA_res1
Out[37]: <Residue: ALA 1 from Chain A from 3mkb (5 atoms)>
Indexing¶
Residue
instances can be indexed to get individual atoms:
In [38]: chA_res1['CA']
Out[38]: <Atom: CA from 3mkb (index 1)>
In [39]: chA_res1['CB']
Out[39]: <Atom: CB from 3mkb (index 4)>
In [40]: chA_res1['X'] # if atom does not exist, None is returned
Iterations¶
Iterating over a residue instance yields Atom
instances:
In [41]: for i, atom in enumerate(chA_res1):
....: if i == 4: break
....: print(atom)
....:
Atom N (index 0)
Atom CA (index 1)
Atom C (index 2)
Atom O (index 3)
This makes it easy to get a list()
of atoms:
In [42]: list(chA_res1)
Out[42]:
[<Atom: N from 3mkb (index 0)>,
<Atom: CA from 3mkb (index 1)>,
<Atom: C from 3mkb (index 2)>,
<Atom: O from 3mkb (index 3)>,
<Atom: CB from 3mkb (index 4)>]
Get data¶
All methods defined for AtomGroup
class are also defined for
Residue
class:
In [43]: chA_res1.getCoords()
Out[43]:
array([[ -2.139, 17.026, -13.287],
[ -1.769, 15.572, -13.111],
[ -0.296, 15.257, -13.467],
[ 0.199, 14.155, -13.155],
[ -2.752, 14.639, -13.898]])
In [44]: chA_res1.getBetas()
Out[44]: array([59.35, 59.14, 58.5 , 59.13, 59.02])
Selections¶
Finally, you can select atoms from a Residue
instance:
In [45]: chA_res1_bb = chA_res1.select('backbone')
In [46]: chA_res1_bb
Out[46]: <Selection: '(backbone) and ... and (chain A))' from 3mkb (4 atoms)>
In [47]: chA_res1_bb.getSelstr()
Out[47]: '(backbone) and (resnum 1 and (chain A))'
Again, the selection string is augmented with the chain identifier and residue number (resnum).
Atoms¶
The lowest level of the hierarchical view contains Atom
instances.
In [48]: chA_res1_CA = chA_res1['CA']
In [49]: chA_res1_CA
Out[49]: <Atom: CA from 3mkb (index 1)>
Get atomic data
All methods defined for AtomGroup
class are also defined for
Atom
class with the difference that method names are singular
(except for coordinates):
In [50]: chA_res1_CA.getCoords()
Out[50]: array([ -1.769, 15.572, -13.111])
In [51]: chA_res1_CA.getBeta()
Out[51]: 59.14
State Changes¶
A HierView
instance represents the state of an AtomGroup
instance at the time it is built. When chain identifiers or residue numbers
change, the state that hierarchical view represents may not match the current
state of the atom group:
In [52]: chA.setChid('X')
In [53]: chA
Out[53]: <Chain: X from 3mkb (254 residues, 1198 atoms)>
In [54]: hv['X'] # returns None, since hierarchical view is not updated
In [55]: hv.update() # this updates hierarchical view
In [56]: hv['X']
Out[56]: <Chain: X from 3mkb (254 residues, 1198 atoms)>
When this is the case, HierView.update()
method can be used to update
hierarchical view.