The parsed data model¶
After parse(), everything pidibble extracted
lives in one attribute:
>>> from pidibble.pdbparse import PDBParser
>>> p = PDBParser(source_db='rcsb', source_id='4zmj').parse()
>>> p.parsed
{...}
parsed is a PDBRecordDict — a dictionary keyed
by record type.
Single records versus record lists¶
Each value is one of two things:
a single
PDBRecord, for records that occur once per structure (HEADER,TITLE,CRYST1, …); ora
PDBRecordListofPDBRecordinstances, for multiple-entry records that recur (ATOM,SEQRES,LINK, …).
PDBRecordList subclasses collections.UserList, so test for it with
isinstance — a plain type(v) == list check never matches, because a
UserList is not a list:
>>> from pidibble.pdbrecord import PDBRecordList
>>> isinstance(p.parsed['ATOM'], PDBRecordList) # a multiple-entry record
True
>>> isinstance(p.parsed['HEADER'], PDBRecordList) # a single-instance record
False
>>> [k for k, v in p.parsed.items() if type(v) == list] # the naive check never matches
[]
For everyday use, a PDBRecordList behaves like a list — index it, slice it,
iterate it, take its len:
>>> atoms = p.parsed['ATOM']
>>> len(atoms)
4518
>>> first, last = atoms[0], atoms[-1]
Which records land in which bucket is documented per record type in Supported record types.
Records and their fields¶
A PDBRecord stores each parsed field as an instance
attribute of the appropriate Python type — strings, int, float, or
nested record objects. The quickest way to see what a record contains is
pstr() (“pretty string”):
>>> print(p.parsed['HEADER'].pstr())
HEADER
classification: VIRAL PROTEIN
depDate: 04-MAY-15
idCode: 4ZMJ
The keys shown by pstr() are exactly the attribute names:
>>> h = p.parsed['HEADER']
>>> h.classification, h.depDate, h.idCode
('VIRAL PROTEIN', '04-MAY-15', '4ZMJ')
pstr() takes an excludes list (bookkeeping fields key, format and
continuation are hidden by default) and a pad width for the labels, so
you can widen or narrow the display or reveal the hidden internals.
Residues and other structured sub-fields¶
Some fields are not scalars but small structured objects. The most common is a residue, which groups a residue name, chain id, sequence number and insertion code:
>>> a = p.parsed['ATOM'][0]
>>> print(a.residue)
resName: LEU; chainID: G; seqNum: 34; iCode:
>>> a.residue.resName, a.residue.chainID, a.residue.seqNum
('LEU', 'G', 34)
Records that describe a relationship between residues expose more than one:
>>> b = p.parsed['SSBOND'][0]
>>> b.residue1.resName, b.residue1.chainID, b.residue1.seqNum
('CYS', 'G', 54)
>>> b.residue2.resName, b.residue2.chainID, b.residue2.seqNum
('CYS', 'G', 74)
Working with the model as a whole¶
Because parsed is an ordinary mapping, the usual idioms apply — membership
tests, iteration, comprehensions:
>>> 'ATOM' in p.parsed and 'HETATM' in p.parsed
True
>>> n_hetatm = len(p.parsed['HETATM'])
>>> chains = {a.residue.chainID for a in p.parsed['ATOM']}
>>> sorted(chains)
['B', 'G']
A record type that was not present in the file is simply absent from parsed,
so guard with in (or dict.get) before reaching for an optional record:
>>> 'SPLIT' in p.parsed
False