1. PDB 文件格式基础:历史、用途与结构解析
PDB 文件,全称 Protein Data Bank 文件格式。说白了,它就是生物大分子世界的“通用语言”。
你手里有一份晶体结构数据,想发给合作者看看?用 PDB。你想在 PyMOL 里可视化一个蛋白质?先加载 PDB。你想跑个分子动力学模拟?输入文件多半也是 PDB。我在项目中遇到过不下百次这样的场景:不同实验室、不同软件,全靠这个格式来沟通。
1.1 一个“意外”诞生的标准
PDB 格式的历史,其实挺有意思。1971年,布鲁克海文国家实验室的几位结构生物学家,为了解决数据共享的难题,建立了一个公共数据库。当时他们没想太多,就设计了一套简单的固定列宽格式来记录原子坐标。
嗯,你没听错——固定列宽。每一行从第几个字符到第几个字符代表什么,都是写死的。这在今天的 JSON、YAML 满天飞的环境下,显得有点“复古”。
数据库的访问量增长飞快。到了 90 年代,PDB 格式已经成了结构生物学的事实标准。我记得当年读文献时,几乎所有结构数据的补充材料里都附带一个 .pdb 文件。即便是现在,新格式 mmCIF 已经主流化,PDB 格式依然因其简洁、易读、解析成本低而广泛存在。
1.2 PDB 文件到底在记录什么?
一份标准的 PDB 文件,包含以下几个核心信息块:
| 信息类别 | 记录类型(Record Type) | 内容说明 |
|---|---|---|
| 文件头部 | HEADER / TITLE / COMPND | 分子名称、分类、来源、发表文献等元数据 |
| 结晶/实验信息 | CRYST1 / REMARK | 晶胞参数、空间群、数据收集与精修方法 |
| 序列与链信息 | SEQRES / DBREF | 氨基酸或核苷酸序列、链标识符、数据库交叉引用 |
| 原子坐标 | ATOM / HETATM | 每个原子的名称、残基类型、直角坐标 (x, y, z)、温度因子 (B值) 和占有率 |
| 化学键与连接 | CONECT / LINK / SSBOND | 非标准化学键、二硫键、配体连接 |
| 注释与结束 | REMARK / END | 实验细节、功能注释;文件结束标记 |
其中最为核心的,就是 ATOM 和 HETATM 记录。你后期做的所有特征提取——从计算溶剂可及性表面积到分析残基接触网络——都离不开这些原子坐标。
1.3 ATOM 记录:你手把手读懂一行坐标
我们来看一个真实的 ATOM 行:
ATOM 1 N ALA A 1 19.296 13.249 28.553 1.00 42.18 N
别被这一串吓到。我们用“固定列宽”的思维拆解一下:
- 列 1-6: "ATOM " — 记录类型,固定 6 个字符。
- 列 7-11: " 1" — 原子序号,右对齐。
- 列 13-16: " N " — 原子名称。这里的空格位置很有讲究,标准原子名在列 13-14,右侧可能有空格。
- 列 17: " " — 备用位点指示符。如果是 'A' 或 'B',表示这个原子有交替构象。我曾经因为这个字段没处理好,导致有多个构象的结构计算出了很大偏差。避坑指南:解析时不要忽略这个字段。
- 列 18-20: "ALA" — 残基名称,三字母缩写。
- 列 22: "A" — 链标识符。如果结构只有一条链,通常是空格或 'A'。
- 列 23-26: " 1" — 残基号。
- 列 27: " " — 插入码。用于处理序列中插入的额外残基。
- 列 31-38, 39-46, 47-54: 直角坐标 x, y, z,单位是 埃(Å)。
- 列 55-60: " 1.00" — 占有率。通常为 1.0,表示该位置完全被这个原子占据。
- 列 61-66: " 42.18" — 温度因子(B-factor)。值越高,代表这个原子在晶体中震动得越厉害,或者说它的位置越不确定。
- 列 77-78: " N" — 元素符号。
line[12:16] 取原子名,而不是 line.split()。
1.4 一张图厘清 PDB 文件的核心结构
我把上面讲的内容,画成了一张结构图。你顺着箭头看,就能理解 PDB 文件的整体轮廓。
1.5 现代工具怎么读 PDB?
在 Python 里,我们有多个选择。我个人最常用 Biopython,因为它的 PDB.PDBParser 模块封装了几乎所有解析细节。
给你看一个最基础的解析脚本——我建议初学的朋友先从这种写法开始,不要急着上复杂的数据结构:
from Bio.PDB import PDBParser
parser = PDBParser(QUIET=True)
structure = parser.get_structure("1ubq", "1ubq.pdb")
for model in structure:
for chain in model:
for residue in chain:
for atom in residue:
# 取出你需要的信息
atom_name = atom.get_name() # " N ", " CA "
coord = atom.get_vector() # Numpy array [x, y, z]
b_factor = atom.get_bfactor() # 温度因子
occupancy = atom.get_occupancy() # 占有率
element = atom.element # 元素符号(自动推断)
print(f"{residue.get_resname()} {residue.get_id()[1]} {atom_name}: {coord}")
解析原子要取什么?
你别把整行 ATOM 记录都存成字符串。你应该关注三项数据:坐标(几何位置)、B-factor(柔性/有序度)、残基身份(序列信息)。后面做特征工程,比如计算残基间距、分析活性口袋的柔性,全靠这三项。
如果不依赖 Biopython,自己手写一个解析器也很直接。关键是掌握固定列宽的偏移量:
def parse_atom_line(line):
if line.startswith("ATOM") or line.startswith("HETATM"):
serial = int(line[6:11].strip())
name = line[12:16].strip()
alt_loc = line[16]
resname = line[17:20].strip()
chain_id = line[21].strip() if line[21] != ' ' else ''
resseq = int(line[22:26].strip())
icode = line[26].strip()
x = float(line[30:38].strip())
y = float(line[38:46].strip())
z = float(line[46:54].strip())
occupancy = float(line[54:60].strip())
bfactor = float(line[60:66].strip())
element = line[76:78].strip()
return {
"serial": serial,
"name": name,
"resname": resname,
"chain": chain_id,
"resid": resseq,
"x": x, "y": y, "z": z,
"b_factor": bfactor,
"occupancy": occupancy,
"element": element
}
float() 了对整个坐标字段,会直接报错。所以做通用解析器时,最好先强检查 len(line) >= 54。
OK,PDB 文件的过去、现在和它的“骨架”就讲到这儿。你手头如果有一个 .pdb 文件,建议现在就打开它,对照着上面的 ATOM 行拆解,看一遍就全明白了。