用 BioPython 读取 PDB:安装、读取与遍历
处理 PDB 数据,肯定得有个趁手的工具。
我个人最喜欢的就是 BioPython。
你想想看,一个纯 Python 库,能读能写能分析,生态还特别好。
这一章咱们就把它的 PDB 模块搞熟。
先说个我自己的教训。
早年我做结构预处理时,用的全是手写字符串匹配,每次改格式都头大。
后来换到 BioPython,真的是省心太多。
安装 BioPython
安装这件事,其实简单。
只要你有 Python 环境,一行命令就搞定。
pip install biopython
如果你需要指定版本——我个人习惯用最新稳定版。
pip install biopython==1.83
读取 PDB 文件
BioPython 读取 PDB 文件有两个主要方式:PDBParser 和 MMCIFParser。
先说最常用的 PDBParser。
from Bio.PDB import PDBParser
parser = PDBParser()
structure = parser.get_structure("1ake", "1ake.pdb")
那句 get_structure 后面跟的两个参数:
第一个是 结构ID(你随便起),第二个是 文件路径。
我遇到过一个问题:
如果文件里的链名或残基编号不符合标准,parser 会报错。
这时候可以加一个 PERMISSIVE=True 参数,让它更宽容。
parser = PDBParser(PERMISSIVE=True)
如果你想读 mmCIF 格式,就换成 MMCIFParser。
from Bio.PDB import MMCIFParser
parser = MMCIFParser()
structure = parser.get_structure("1ake", "1ake.cif")
我个人建议:
如果能拿到 mmCIF,优先用 mmCIF。
因为它没有 PDB 格式的 80 列宽度限制,原子信息更完整。
结构对象层级
读懂 BioPython 的结构层级,是后续一切操作的基础。
说白了,BioPython 把 PDB 数据组织成一个树形结构。
- Structure —— 最顶层,代表整个文件。
- Model —— 模型,通常 NMR 结构会有多个。
- Chain —— 链。
- Residue —— 残基。
- Atom —— 原子。
看看代码是怎么体现这个层级的。
# 获取第一个模型
model = structure[0]
# 获取所有链
for chain in model:
print(f"Chain ID: {chain.id}")
# 获取链里的所有残基
for residue in chain:
print(f" Residue: {residue.resname} {residue.id[1]}")
# 获取残基里的所有原子
for atom in residue:
print(f" Atom: {atom.name}, coord: {atom.coord}")
这段代码会把每个原子坐标都打印出来。
我在项目中用过这个逻辑来提取特定残基的侧链原子。
遍历模型与链
大多数 X 射线晶体结构只有一个模型,但 NMR 结构可能有十几个。
这时候你必须遍历所有模型。
for model in structure:
print(f"Model ID: {model.serial_num}")
for chain in model:
print(f" Chain: {chain.id}")
为什么会这样?
因为 BioPython 的 structure[0] 默认只取第一个模型。
如果你不管 NMR,直接用索引取,会漏掉很多信息。
另外一个坑是我曾经踩过的:
有的 PDB 文件会包含 水分子链,链名叫 W(实际是水)。
你在处理时得判断是否要跳过。
for chain in model:
# 跳过水分子链
if chain.id == "W":
continue
process_chain(chain)
BioPython 没有内置的 HETATM 区分方法,但你可以通过残基名判断。
常见的水分子残基名:HOH、DOD、WAT。
访问原子与残基属性
每个原子对象都有丰富的属性。
我常用的属性列表:
| 属性 | 含义 |
|---|---|
name |
原子名称(如 CA、CB) |
coord |
三维坐标(numpy array) |
bfactor |
温度因子 |
occupancy |
占位率 |
serial_number |
原子序号 |
element |
元素符号 |
举个例子:提取所有 Cα 原子的坐标。
ca_atoms = []
for model in structure:
for chain in model:
for residue in chain:
if "CA" in residue:
ca_atoms.append(residue["CA"].coord)
残基的属性也比较直观。
| 属性 | 含义 |
|---|---|
resname |
残基名称(如 ALA) |
id |
残基编号和插入码(元组) |
full_id |
完整标识(含链名、模型等) |
residue.resname == "CYS" 做过滤。
知识体系图
下面这个 SVG 梳理了本章的核心流程。
用 BioPython 解析 PDB 文件,然后遍历结构层级,提取所需数据。
这个图我就不多解释了。
你们跟着箭头走,就能理解从 PDB 文件到提取原子坐标的完整链路。
最后说一句
BioPython 的 PDB 模块很成熟,但并不意味着无脑用。
我遇到过一次:
某个老旧的 PDB 文件里有非标准残基名,解析直接挂了。
最后还得手写一个补丁来处理它。
所以,永远保留原始文件,永远做格式校验。
这是数据预处理的铁律。