用最简单的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的训练模型就完成了。

相关推荐
TinTin Land10 分钟前
从黑客松出发,AI + Web3 项目怎么打磨成产品?
人工智能·web3
竹子_2315 分钟前
《零基础入门AI:传统机器学习核心算法(决策树、随机森林与线性回归)》
人工智能·算法·机器学习
Blossom.11825 分钟前
基于深度学习的医学图像分析:使用BERT实现医学文本分类
人工智能·深度学习·机器学习·3d·分类·cnn·bert
Ms_lan33 分钟前
邢台市某区人民医院智慧康养平台建设项目案例研究
大数据·人工智能·物联网·蓝牙网关·桂花网
m0_6423304739 分钟前
【人工智能-15】OpenCV直方图均衡化,模板匹配,霍夫变换,图像亮度变换,形态学变换
人工智能·opencv·计算机视觉
虎哥AI工具1 小时前
Code Buddy 和 Cursor 到底谁更强?别再猜了!我花一下午,把代码、Bug、效率全给你们测明白了!
人工智能
算家计算1 小时前
全新升级!Nexus-Gen V2本地部署教程:图像理解能力媲美GPT-4o
人工智能·开源·aigc
用户46175752470651 小时前
小白理解transformer里的前馈神经网络
人工智能
柠檬味拥抱1 小时前
AI Agent多模态融合策略研究与实证应用
人工智能
笔触狂放1 小时前
【机器学习】第九章 综合实战
人工智能·机器学习