分子数据获取:公共数据库下载与解析
做SMILES生成模型,第一步就是搞数据。说白了,没有数据,模型就是个空壳。我个人习惯先从公开数据库下手,比如ChEMBL、ZINC和PubChem。这三个库,基本上把已知的分子世界都覆盖了。
为什么一定要用公共数据库?
你想想看,自己搭建一个分子筛选平台来生成SMILES,成本有多高?我曾经在项目初期试过自己做湿实验收集数据,结果忙活两个月,才攒了不到一千个分子。后来转用公共数据库,一天就拿到了几百万。这效率差距,不是一星半点。
公共数据库的好处很明显:
- 数据量大:动辄上亿个化合物,足够喂饱你的生成模型。
- 格式统一:大部分提供SMILES、SDF、JSON等多种导出格式。
- 可复现:你跑出来的结果,换个人也能从同一个源拉数据,对得上。
- 带标注:不少分子还有生物活性、毒性、代谢路径等额外信息,做条件生成时特别好用。
三大主流数据库:谁是谁?
这三个库,侧重点完全不同。我在刚开始接触时也搞混过,后来摸清楚门道了,给你们掰扯清楚。
| 数据库 | 规模(2024年数据) | 核心数据格式 | 访问方式 | 主要用途 |
|---|---|---|---|---|
| ChEMBL | 约240万活性化合物 | SMILES, SDF, CSV | Web + API + FTP批量下载 | 药物研发、活性预测 |
| ZINC | 约2.3亿可购买化合物 | SMILES, SDF, Mol2 | Web + FTP + Transzinc工具 | 虚拟筛选、分子动力学 |
| PubChem | 约1.2亿化合物 | SMILES, JSON, XML, SDF | Web + PUG REST API + FTP | 多学科、化合物信息检索 |
从ChEMBL抓数据:我常用的路子
ChEMBL是欧洲生物信息学研究所维护的,质量很高。我一般直接去FTP下载完整版,然后本地建索引。别每次都调API,几千几万个分子时,API请求容易超时。
方法一:FTP批量下载
ChEMBL每隔几个月就会发布一次完整快照。下载命令很简单:
wget -r -np -nH --cut-dirs=5 -R index.html \
ftp://ftp.ebi.ac.uk/pub/databases/chembl/ChEMBLdb/latest/
下载下来是一个SDF文件,解压后大概1-2GB。嗯,这里要注意,这个SDF文件里包含了所有活性数据、结构、靶点信息。如果你只想要SMILES,可以用RDKit过滤一下。
方法二:用Python实现自动下载与解析
我个人更喜欢直接写脚本控制。用chembl_webresource_client库,从API拉数据,边拉边存。
from chembl_webresource_client.new_client import new_client
molecule = new_client.molecule
results = molecule.filter(molecule_properties__mw__gte=300,
molecule_properties__mw__lte=500,
molecule_properties__alogp__lte=5).only(
['molecule_chembl_id', 'molecule_structures'])
smiles_list = []
for mol in results:
if 'canonical_smiles' in mol['molecule_structures']:
smiles_list.append(mol['molecule_structures']['canonical_smiles'])
print(f"共获取 {len(smiles_list)} 条SMILES")
with open('chembl_smiles.txt', 'w') as f:
for smi in smiles_list:
f.write(smi + '\n')
ZINC数据下载:量最大但有点“脏”
ZINC数据库最大的特点就是全。2.3亿个化合物,而且都是可以购买的。缺点是数据质量参差不齐,有些SMILES里的原子类型不标准。我曾经从ZINC下载了一批数据,结果训练模型时一直不收敛,最后发现是SMILES里有“*”通配符,模型学歪了。
子集选择是关键
ZINC按不同的过滤规则分了子集,下载前想清楚你要什么:
- ZINC20_clean:约2亿个,标准格式,去除了盐和溶剂
- ZINC20_in-stock:约2300万个,表示可以买到的
- ZINC20_drug-like:约1300万个,符合类药五规则
- ZINC20_fragment:约1.4亿个,分子量≤250
我一般用Transzinc工具来下载,支持多线程,比FTP快多了。
transzinc --subset drug-like --format smi --output zin_druglike.smi
输出是标准的SMILES文件,每行一个分子。
解析ZINC的SMILES
ZINC的SMILES文件通常比较大,几十GB。我习惯用pandas分批读取,然后做一些基本清洗。
import pandas as pd
# 读取SMILES文件,假设第一列是ZINC ID,第二列是SMILES
chunks = pd.read_csv('zin_druglike.smi',
sep='\t',
header=None,
names=['zinc_id', 'smiles'],
chunksize=100000)
valid_smiles = []
for chunk in chunks:
# 简单过滤:长度小于50,且不含特殊字符
mask = (chunk['smiles'].str.len() < 50) & \
(~chunk['smiles'].str.contains(r'[\*\.\[\]]', na=False))
valid_smiles.extend(chunk[mask]['smiles'].tolist())
print(f"清洗后剩余 {len(valid_smiles)} 条SMILES")
PubChem:最全的“化合物百科全书”
PubChem的优势是跨学科。它不止有化合物,还有基因、蛋白、疾病关联。如果你做的是多模态生成或者条件生成,PubChem的丰富标注数据特别宝贵。
PUG REST API使用
PubChem的API设计得很规范,能直接查询化合物的SMILES。
import requests
def get_smiles_from_pubchem(cid):
"""从PubChem获取CID对应的SMILES"""
url = f"https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/{cid}/property/CanonicalSMILES/JSON"
try:
r = requests.get(url, timeout=10)
if r.status_code == 200:
data = r.json()
return data['PropertyTable']['Properties'][0]['CanonicalSMILES']
except Exception as e:
print(f"请求CID {cid} 失败: {e}")
return None
# 示例:获取阿司匹林(CID=2244)的SMILES
smiles = get_smiles_from_pubchem(2244)
print(smiles) # 输出: CC(=O)Oc1ccccc1C(=O)O
PubChem FTP下载
如果你要拉全库,FTP是最靠谱的。PubChem的FTP结构很清晰:
# 下载整个化合物的SMILES文件(约3GB压缩)
wget ftp://ftp.ncbi.nlm.nih.gov/pubchem/Compound/Extras/CID-SMILES.gz
gzip -d CID-SMILES.gz
# 查看格式:每行一个CID和SMILES
# 2244 CC(=O)Oc1ccccc1C(=O)O
# 76175 CC(C)CC1=CC=C(C=C1)C(C)C(=O)O
下载后,我会用awk或者Python快速筛选:比如只保留分子量在200-500之间的。
三种数据下载方式的对比
我个人在实际项目中,会根据情况选不同的方式。给你列个表,方便做技术选型:
| 下载方式 | 适用场景 | 数据量级 | 实时性 | 网络要求 |
|---|---|---|---|---|
| Web页面导出 | 少量样本测试,验证思路 | <1000条 | 实时 | 低 |
| REST API | 有编程接口,可批量查询 | 1K-100K条 | 准实时 | 中 |
| FTP/Transzinc批量 | 全量或大子集下载 | 百万-亿级 | 快照版 | 高 |
-c参数,血泪教训。
解析三大数据库的核心流程
不管你从哪个库下载数据,解析的流程基本一致。我总结了一个通用流程,你们可以拿着直接用:
常用解析工具与库
解析SMILES时,有几个库是绕不开的。我用过不少,最顺手的给你列出来:
- RDKit:化学信息学的瑞士军刀。解析、标准化、可视化一条龙。我99%的场景用它。
- Open Babel:格式转换神器,能将SDF转为SMILES,还能批量处理。
- ChemDataExtractor:挖论文里SMILES用的,做多源数据合并时帮了大忙。
- pybel:Open Babel的Python封装,写脚本时比命令行友好很多。
一个完整的解析示例
我来演示一下,从SDF文件解析SMILES并做初步清洗的全过程。这是我项目里几乎每天都会用的脚本模板。
from rdkit import Chem
from rdkit.Chem import Descriptors, SaltRemover
import pandas as pd
def parse_sdf_to_smiles(sdf_path, output_path):
"""从SDF文件提取SMILES,并做过滤"""
supplier = Chem.SDMolSupplier(sdf_path)
remover = SaltRemover.SaltRemover()
data = []
for idx, mol in enumerate(supplier):
if mol is None:
continue
# 去除盐和溶剂
mol_no_salt = remover.StripMol(mol, dontRemoveEverything=True)
# 获取SMILES
smiles = Chem.MolToSmiles(mol_no_salt, canonical=True)
# 简单过滤
mw = Descriptors.MolWt(mol_no_salt)
if mw < 150 or mw > 600:
continue
# 检查SMILES是否合法
try:
check = Chem.MolFromSmiles(smiles)
if check is None:
continue
except:
continue
data.append({
'smiles': smiles,
'mol_weight': round(mw, 2),
'num_atoms': mol_no_salt.GetNumAtoms()
})
df = pd.DataFrame(data)
df.to_csv(output_path, index=False)
print(f"写入 {len(df)} 条SMILES到 {output_path}")
return df
# 使用
df_smiles = parse_sdf_to_smiles('chembl_32.sdf', 'clean_smiles.csv')
常见问题与避坑指南
做数据获取这几年,踩过不少坑。分享几个典型的:
- SMILES不唯一:同一个分子在不同数据库里的SMILES写法可能不同。解决方法是统一用RDKit做canonical(规范)转换。
- 特殊符号问题:ZINC里经常出现“*”、“[”这些通配符,训练模型前一定要过滤掉。
- 数据不平衡:ChEMBL偏重药物分子,分子量集中在250-500。如果你要生成小分子或大分子,需要额外补充数据源。
- 网络中断:下载大文件时,一定要做重试机制。我用的脚本里都加了三次重试。
好了,以上就是从三个公共数据库获取并解析SMILES的全部内容。说白了,数据质量决定了模型效果的天花板。花时间把这块做好,后面训练模型时会顺很多。