用最简单的python语法来利用机器学习算法预测药物分子的xlogp

在这个项目中,我们拥有一个含有4000多分子量的文件,我们需要通过这个文件,来预测分子的xlogp值。以下是项目的需求:

  • 先对数据进行初步的处理,加载我们需要的数据:smiles列与xlogp列,并处理异常值。
  • 对于缺失的异常值,利用rdkit计算xlogp的数值进行填充。
  • 特征化处理smiles,利用分子指纹将smiles表示成字符串的形式,利于机器理解。
  • 构建随机森林计算模型,并将数据集分为训练/测试两个集合,进行训练。
  • 查看模型的结果

第一步先导入需要的包:

python 复制代码
# 数据分析所需要的包
import pandas as pd
import numpy as np

#化学分析需要的包
import rdkit as rd
from rdkit.Chem import Descriptors
from rdkit import Chem
from rdkit.Chem import Crippen

from rdkit import rdBase #处理rdkit刷屏警告的方法
rdBase.DisableLog('rdApp.warning') #处理rdkit刷屏警告的方法



#机器学习所需要的包
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import r2_score, mean_squared_error

数据的处理

用到的是pandas这个包,这个包经常用于对数据的初步处理:

  • 加载数据集
  • 对异常数据进行修改/删除
  • 对多个不同数据集的数据进行合并
  • ...

加载数据集

ini 复制代码
# 以pubchem中下载的药物分子数据集为例
# 读取数据集
df = pd.read_csv("data.csv")

可以使用以下代码来了解数据集的结构。

shell 复制代码
# df.columns # 查看数据集的所有列名
# df.head() # 查看数据集的前几行
# df.info() # 查看数据集的基本信息,包括数据类型和非空值数量
df.shape

选取特定的行,此处将数据集中的列smiles和列xlogp选取了出来。

ini 复制代码
xlog = df['XLogP']
smiles = df['SMILES']
data_1 = pd.DataFrame({'SMILES': smiles, 'XLogP': xlog})
data_1.head()

数据集的清洗

我们现在已经初步加载了数据集,下一步是对数据的异常有个初步的了解,针对这个数据集,我们通常会遇到以下问题(经验所得):

  • 存在NA值。也就是数据缺失,我们需要保证输入到模型中的数据都是数字形式,因此我们需要对NA值进行处理,在处理之前,我们可以先检查一下数据集有多少NA值。
  • 存在无效值。针对这个数据集来说,我们后续需要对smiles值进行特征化处理,如果smiles值异常,就会导致后期报错,因此我们也需要检查一下数据集中存在多少无效值

接下来我们就对症下药:先查看一下目标列中是否存在异常值

python 复制代码
xlogp_na = df['XLogP'].isna().sum()
smiles_invalid = df['SMILES'].apply(lambda x: Chem.MolFromSmiles(x) is None).sum()
print(f"XLogP 中 NaN 的数量:{xlogp_na}")
print(f"无效的 SMILES 数量:{smiles_invalid}")

再处理掉异常值:

  • xlogp中的异常值,用rdkit计算的logp值替代。
  • smiles的异常值直接删去。
less 复制代码
# 替换无效的 XLogP 值
df['XLogP'] = df.apply(
    lambda x: Crippen.MolLogP(Chem.MolFromSmiles(x['SMILES'])) 
    if pd.isna(x['XLogP']) else x['XLogP'], axis=1
)
# 删除无效的 SMILES
df = df[df['SMILES'].apply(lambda x: Chem.MolFromSmiles(x) is not None)]

做完这一步,我们可以再次检查一下数据结构

python 复制代码
xlogp_na = df['XLogP'].isna().sum()
smiles_invalid = df['SMILES'].apply(lambda x: Chem.MolFromSmiles(x) is None).sum()
df.shape
print(f"XLogP 中 NaN 的数量:{xlogp_na}")
print(f"无效的 SMILES 数量:{smiles_invalid}")

比较前后数据结构的变化。 到这一步,xlogp中的na值与smiles中的异常值都被我们去掉了,数据的清洗完美结束了!下一步就是对数据进行特征化处理了。

特征化数据

由于smiles所含的信息太少,并且机器只能读懂数字,我们需要用到rdkit中提供的分子指纹办法,将smiles进行特征化处理。

scss 复制代码
# 把 SMILES 转成分子对象
molecules = df["SMILES"].apply(Chem.MolFromSmiles)

# 分别计算 14 个描述符,并添加到新的列中
df["MolLogP"] = molecules.apply(Descriptors.MolLogP)
df["HeavyAtomCount"] = molecules.apply(Descriptors.HeavyAtomCount)
df["NumHAcceptors"] = molecules.apply(Descriptors.NumHAcceptors)
df["NumHeteroatoms"] = molecules.apply(Descriptors.NumHeteroatoms)
df["NumHDonors"] = molecules.apply(Descriptors.NumHDonors)
df["MolWt"] = molecules.apply(Descriptors.MolWt)
df["NumRotatableBonds"] = molecules.apply(Descriptors.NumRotatableBonds)
df["RingCount"] = molecules.apply(Descriptors.RingCount)
df["Ipc"] = molecules.apply(Descriptors.Ipc)
df["HallKierAlpha"] = molecules.apply(Descriptors.HallKierAlpha)
df["NumValenceElectrons"] = molecules.apply(Descriptors.NumValenceElectrons)
df["NumSaturatedRings"] = molecules.apply(Descriptors.NumSaturatedRings)
df["NumAliphaticRings"] = molecules.apply(Descriptors.NumAliphaticRings)
df["NumAromaticRings"] = molecules.apply(Descriptors.NumAromaticRings)

print(df.head())

做完这一步,可以查看一下现在表格的结构,是否维度发生了变化,

简单的特征化处理就结束了。

模型的训练与评估

我们现在计算完分子描述符,可以尝试使用这些分子描述符进行训练了。

  • 数据集的切割
  • 使用随机森林模型
  • 评估模型

数据的切割

我们需要将描述符作为输入,并将xlogp值作为输出,因此我们需要定义一个包含14个描述符的X(输入),和一个包含xlogp的y(输出)。

ini 复制代码
# X:描述符
X = df[['MolLogP', 'HeavyAtomCount', 'NumHAcceptors', 'NumHeteroatoms',
          'NumHDonors', 'MolWt', 'NumRotatableBonds', 'RingCount', 'Ipc',
          'HallKierAlpha', 'NumValenceElectrons', 'NumSaturatedRings',
          'NumAliphaticRings', 'NumAromaticRings']]

# y:目标性质
y = df['XLogP'] 

rdkit计算出来的某些描述符可能会很大,也有可能产生新的na值。我们可以检查一下X中数据是否能够训练

scss 复制代码
# 1. 检查 NaN 个数
nan_counts = X.isna().sum()

# 2. 检查 +inf 和 -inf 个数
posinf_counts = np.isposinf(X).sum()
neginf_counts = np.isneginf(X).sum()

# 3. 检查超大值(>1e5,可调整阈值)
threshold = 1e5
large_counts = (X > threshold).sum()

print("=== NaN 个数 ===")
print(nan_counts[nan_counts > 0])

print("\n=== 正无穷个数 ===")
print(posinf_counts[posinf_counts > 0])

print("\n=== 负无穷个数 ===")
print(neginf_counts[neginf_counts > 0])

print("\n=== 大于阈值的个数 ===")
print(large_counts[large_counts > 0])

以[Ipc]超大值为例,如果出现超大值,使用以下方式去掉:

ini 复制代码
# 看到Ipc有超大值,进行处理
# 将 Ipc 列中的超大值替换为阈值
X["Ipc"] = X["Ipc"].clip(upper=1e5)

接下来就可以对数据进行分割了:

ini 复制代码
# 分割数据集
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

使用随机森林模型

分割完数据集,我们就可以开始训练模型了,我们直接选用简单的随机森林模型。

scss 复制代码
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train, y_train)

运行完后,进行模型的评估

scss 复制代码
y_pred = model.predict(X_test)
print("R²:", r2_score(y_test, y_pred))
print("MSE:", mean_squared_error(y_test, y_pred))

一个简单的预测xlogp的训练模型就完成了。

相关推荐
天涯海风27 分钟前
检索增强生成(RAG) 缓存增强生成(CAG) 生成中检索(RICHES) 知识库增强语言模型(KBLAM)
人工智能·缓存·语言模型
lxmyzzs2 小时前
基于深度学习CenterPoint的3D目标检测部署实战
人工智能·深度学习·目标检测·自动驾驶·ros·激光雷达·3d目标检测
跟着珅聪学java2 小时前
Apache OpenNLP简介
人工智能·知识图谱
AwhiteV3 小时前
利用图数据库高效解决 Text2sql 任务中表结构复杂时占用过多大模型上下文的问题
数据库·人工智能·自然语言处理·oracle·大模型·text2sql
Black_Rock_br3 小时前
AI on Mac, Your Way!全本地化智能代理,隐私与性能兼得
人工智能·macos
☺����3 小时前
实现自己的AI视频监控系统-第一章-视频拉流与解码2
开发语言·人工智能·python·音视频
fsnine4 小时前
机器学习——数据清洗
人工智能·机器学习
小猿姐4 小时前
KubeBlocks AI:AI时代的云原生数据库运维探索
数据库·人工智能·云原生·kubeblocks
算法_小学生5 小时前
循环神经网络(RNN, Recurrent Neural Network)
人工智能·rnn·深度学习
吱吱企业安全通讯软件5 小时前
吱吱企业通讯软件保证内部通讯安全,搭建数字安全体系
大数据·网络·人工智能·安全·信息与通信·吱吱办公通讯