ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于复杂网络的药物-疾病关系预测:数据清洗、特征归一化与交叉验证

基于复杂网络的药物-疾病关系预测:数据清洗、特征归一化与交叉验证 简介基于复杂网络的药物疾病关系预测方法实现源码面向生物信息、计算机、人工智能等专业学生可支撑课程大作业、毕业设计、期末项目或科研早期探索。压缩包共9个文件包含7个Python脚本、1份Markdown项目说明和1份CSV数据文件整体仅69KB轻量便携脚本内容覆盖数据读取与预处理、Z-score标准化、副作用统计、复杂网络指标计算以及ROC/AUC评估等环节同时提供Markdown文档说明环境配置与使用方式结构清晰方便按图索骥。该资源已有176人学习下载且代码经过实际运行验证稳定可靠适合直接运行并复现结果。透过源码可系统理解药物-疾病关联预测的网络建模思路掌握节点特征提取、链路预测和评估指标的可视化流程在此基础上还可二次开发拓展至其他生物医学关系挖掘场景是一个兼具学习与扩展价值的高性价比项目。1. 药物-疾病二分网络与复杂网络预测毕设题目背后在做什么很多人拿到“基于复杂网络的药物疾病关系预测方法”这个题目时第一反应是要不要训练一个深度模型。但解压源码包看到的是 main1.py、du.py、AUCPaint.py 这一组脚本项目并没有想象中那么重。它的核心思路是把药物和疾病放进同一个网络已知的药物-疾病关联作为边然后用网络中的邻居信息、路径或相似度去推断未知关系。相比传统机器学习复杂网络方法的优势在于中间结果可解释为什么推荐某个药物可以用共同邻居或网络扩散路径来说明。这套源码非常适合作为计算机、数据科学相关专业的课程大作业也能作为毕业设计第一版基线。工程上的难点不在于算法本身而在于 du.csv 的格式、ID 映射、交叉验证信息泄露以及最终阈值怎么选。2. du.csv 数据清洗与邻接矩阵构建从边表到可算的网络2.1 先确认 du.csv 的格式拿到源码后不要急着运行 main1.py大概率会卡在数据读取上。项目里的 du.csv 是药物-疾病关系边表我见过两种常见写法一种只有 drug、disease 两列每一行代表一条已确认关联另一种带 label 列用 0/1 表示是否存在关联。两种格式后续处理方式差别很大需要先确认。import pandas as pd df pd.read_csv(du.csv, headerNone) print(df.head(10)) print(df.shape)逻辑说明headerNone 是为了防止文件没有表头时第一行数据被当作列名。df.shape 的第二个维度很关键如果输出是 (3000, 2)说明是两列边表如果是 (3000, 3)说明包含 label 或其他权重列。参数说明当文件确实有表头时要去掉 headerNone否则药物名称那一行会被误当成数据。如果遇到两列边表需要自己构造 label 列后面评估阶段会用到df pd.read_csv(du.csv, names[drug, disease]) df[label] 1这里 names 参数直接指定列名省去表头处理。注意两列边表隐含的正样本只有 1负样本要从未知关系里采样。采样比例会影响最终 AUC一般按 1:1 到 1:10 之间调节。如果项目里已经提供负样本则不需要这一步。下面把源码包的文件职责先列出来方便后续对号入座文件在流水线中的角色du.csv药物-疾病关系边表构造网络的边来源du.py加载边表、生成邻接矩阵、提供预测接口side.py加载副作用或相似性 side 信息zscoreSides.py对 side 特征矩阵做 z-score 标准化main1.py主控脚本串联整个预测和评估流程ROC.py / AUCPaint.py计算 ROC 数据并绘制曲线从表里能看到整个源码包的模块划分。实际运行顺序是 du.csv → du.py → side.py/zscoreSides.py → main1.py → ROC.py/AUCPaint.py。如果中途某个模块报错优先检查上一个模块的输出形状尤其注意 side 矩阵的行数是否和药物节点数一致。2.2 构建药物-疾病二分网络数据格式确认后要把字符串 ID 转成数字索引再构造邻接矩阵。这一步是所有复杂网络模型共享的底层操作后面计算 z-score、扩散打分、ROC 评估都会围绕这个矩阵展开。from scipy.sparse import lil_matrix import numpy as np drugs sorted(df[drug].unique()) diseases sorted(df[disease].unique()) drug2idx {d: i for i, d in enumerate(drugs)} disease2idx {d: i for i, d in enumerate(diseases)} n_drug, n_dis len(drugs), len(diseases) adj lil_matrix((n_drug, n_dis), dtypenp.float32) for _, row in df[df[label] 1].iterrows(): adj[drug2idx[row[drug]], disease2idx[row[disease]]] 1.0逻辑说明先对药物和疾病分别排序并建立映射字典再用 label1 的行填充邻接矩阵。矩阵形状为 n_drug 行、n_disease 列元素 1 表示已知关联。参数说明dtype 用 float32 是为了减少后续相似度矩阵计算时的内存占用如果你打算用 NetworkX 做可视化可以把 lil_matrix 转为 coo_matrix再用 nx.from_scipy_sparse_array 转换成图对象。这一步最常见的坑是药物名称前后有空格导致同一个药物被当成两个节点。稳妥的做法是在读取后对所有字符串列做 stripfor col in [drug, disease]: df[col] df[col].astype(str).str.strip()另外如果 du.csv 里疾病 ID 使用疾病本体DOID或 MeSH 代码和 side.py 里另一套 ID 混合时必须提前统一 ID 体系否则网络会出现大量本应重叠的节点被拆成两个独立节点网络密度会被严重稀释。构建完的邻接矩阵建议顺便统计一下每个药物的度分布。如果平均度小于 2后面所有基于邻居传播的预测都会变得不稳定。3. z-score 归一化与相似度打分side.py 与 zscoreSides.py 的核心设计3.1 side 信息在药物-疾病预测里代表什么只靠药物和疾病的二分网络预测覆盖度很有限。比如一个药物只出现在一条已知关联里它在网络里能借用的邻居太少。所以源码中 side.py 和 zscoreSides.py 承担的是引入侧面信息的任务。从文件名看side 很可能指药物副作用或疾病语义特征。实际项目中side 矩阵的行对应网络节点列是某种生物学特征。常见做法是把 side 矩阵作为辅助特征矩阵在预测打分前先做 z-score 标准化。如果不做标准化副作用列里频次差异大的特征会主导打分结果。比如某一列几乎所有药物都是 1另一列只有极少数药物为 1后者对区分的贡献会被前者淹没。z-score 能把所有列统一为均值 0、标准差 1让每个特征只贡献方向和强度不贡献绝对大小。3.2 z-score 标准化实现与空值处理zscoreSides.py 的核心逻辑用 scipy.stats.zscore 可以还原。原始数据中经常有空值比如某个药物没有测到某个副作用这时不能直接丢弃整列否则样本量会大幅下降。from scipy import stats import numpy as np def zscore_sides(side_matrix): z stats.zscore(side_matrix, axis0, nan_policyomit) z np.nan_to_num(z, nan0.0, posinf3.0, neginf-3.0) return z逻辑说明stats.zscore 在 axis0 时按列计算每个特征的均值和标准差然后做 (x - mean) / std 转换。nan_policyomit 会跳过 NaN但输出对应位置仍然是 NaN所以需要 nan_to_num 处理。参数说明posinf 和 neginf 限制在 ±3是为了防止极少数异常值在后续矩阵乘法中把分数拉到极限。如果业务场景里希望保留异常信息可以把这两个参数改成 np.inf。空值填充为 0 的含义要理解z-score 中 0 代表该特征等于平均值。填充 0 等效于用整个数据集的平均水平去猜测缺失值这是一种保守处理。副作用矩阵里的缺失通常不是随机缺失但课程大作业层面这个做法足够。还需要注意一种边界情况某列标准差为 0zscore 的结果是 NaN 或 inf。nan_to_num 可以把 nan 替换成 0但如果你希望该列不参与后续计算更合理的做法是先检查方差把方差为 0 的列直接删掉。用一行代码就能完成side_matrix side_matrix.loc[:, side_matrix.std(axis0) 0]3.3 基于网络相似度的打分函数有了邻接矩阵和特征预测关系的方法可以有很多种。du.py 中封装的算法我理解可以近似看作资源扩散或网络传播。这里用最基础的药物相似性打分做演示先通过疾病关联计算药物-药物相似度再对每个疾病候选药物累加相似度。def resource_allocation_score(adj, gamma0.5): normalized adj.tocsr() row_sum np.asarray(normalized.sum(axis1)).ravel() row_sum[row_sum 0] 1.0 normalized normalized.multiply(1.0 / row_sum[:, None]).tocsr() drug_sim normalized normalized.T return drug_sim, normalized这是计算药物相似度的部分。逻辑说明先对行做归一化让每个药物连接到邻居的资源均匀分配。drug_sim 矩阵中的元素 (i, j) 表示药物 i 和药物 j 通过共同疾病建立的相似度。参数 gamma0.5 在部分资源扩散算法里用来调节直接邻居和两步邻居的权重如果源码中是一个 sparse_alpha 参数实际含义是保留多少自身信息gamma 1 - alpha。接下来对未知关系打分def predict_by_similarity(adj, drug_sim): n_drugs, n_diseases adj.shape scores np.dot(drug_sim, adj.toarray()) mask adj.toarray().astype(bool) scores[mask] -1 return scores逻辑说明scores 矩阵里每个位置 (i, d) 表示药物 i 对疾病 d 的预测分。为了避免已知关联也出现在结果里把原始关系存在的位置置为 -1评估时只关心未知部分。参数说明如果数据集负样本占比很高建议把 scores 转换成 rank 再计算 ROC因为排序对 AUC 有意义原始分数绝对值并不重要。到这一步课程大作业的基本功能已经能跑通。再往下优化就是把 zscoreSides 得到的特征矩阵和这里的拓扑相似度做加权拼接然后替换 drug_sim 参与打分。权重可以用网格搜索常见范围在 0.3 到 0.7 之间。需要注意的是side 特征与网络拓扑高度相关时加权后 AUC 提升可能不明显这时不要强行加特征。4. main1.py 交叉验证与 AUCPaint 曲线评估4.1 从入口函数理解整体流程main1.py 是整个项目的启动文件它把前面模块串起来。拿到源码后建议先读 main 部分而不是逐行看代码。典型的 main1.py 调用顺序是读取 du.csv调用 du.py 生成邻接矩阵调用 side.py 和 zscoreSides.py 构造特征得到预测分数最后用 ROC.py 和 AUCPaint.py 输出评估结果。下面用模块化示例还原 main1.py 的核心骨架from du import load_data, build_adjacency, predict_by_similarity from side import load_side_features from zscoreSides import zscore_sides from ROC import compute_auc from AUCPaint import draw_roc def run(): df load_data(du.csv) adj, drug2idx, disease2idx build_adjacency(df) sides load_side_features(side.csv) if sides is not None: sides zscore_sides(sides) scores predict_by_similarity(adj) auc, fpr, tpr compute_auc(df[label], scores[df.index]) draw_roc(fpr, tpr, save_pathroc_curve.png) print(AUC {:.4f}.format(auc))逻辑说明这个骨架最大的价值是让数据流清晰。predict_by_similarity 接收邻接矩阵返回与 adj 形状相同的分数矩阵。compute_auc 接收真实标签和预测分数返回 AUC 和 ROC 坐标序列。参数说明如果你的 du.csv 还配套有 side 信息 CSVload_side_features 必须返回与药物顺序一致的特征矩阵顺序对不上会导致结果完全错乱。4.2 交叉验证先用药物分组再用随机划分药物疾病预测里最常被评审老师追问的问题是“交叉验证时有没有信息泄露”。如果直接用 train_test_split 随机切分同一药物在训练集和测试集同时出现模型会记住药物的整体特征导致 AUC 虚高。正确做法是用 GroupKFold把药物 ID 作为分组变量。交叉验证方式划分子集依据得分可信度适合场景随机样本切分样本行偏高有泄露演示用药物分组切分drug 列的 ID接近真实泛化毕业设计疾病分组切分disease 列的 ID接近真实泛化新疾病预测任务代码实现from sklearn.model_selection import GroupKFold from sklearn.metrics import roc_auc_score gkf GroupKFold(n_splits5) for train_idx, test_idx in gkf.split(df, df[label], groupsdf[drug]): train_df df.iloc[train_idx] test_df df.iloc[test_idx] # 在 train_df 上构建邻接矩阵和 side 特征 # 对 test_df 中未知关系打分 auc roc_auc_score(test_label, test_score) print(fold AUC:, auc)逻辑说明GroupKFold 根据 groups 参数把同一药物 ID 的所有样本放入同一折测试集药物在训练网络里完全不出现这样评估的是新药物上的泛化能力。参数说明n_splits 不一定要大药物总量少于 50 个时建议用 3 折如果还要保持正负样本比例可以用 sklearn 的 StratifiedGroupKFold。需要特别留意在药物分组交叉验证中训练集构建的网络会比全量网络稀疏很多。因为测试集药物被移除它们关联的边也消失这会降低打分绝对值但不会破坏排序。如果 AUC 和全量训练相比下降超过 0.1说明算法过度依赖全局拓扑而不是真正的泛化信号。4.3 ROC 曲线怎么看AUCPaint.py 的核心工作是把 ROC 数据画出来。ROC.py 根据真实标签和预测分数从高到低遍历阈值计算每个阈值处的 TPR 和 FPR然后绘制曲线。AUC 是曲线下面积。在药物疾病预测里AUC 达到 0.85 以上通常说明网络结构包含有效信号。如果 ROC 曲线在中线上方且起始段很陡说明排序靠前的预测中真阳性密度很高候选列表前几名值得重点关注。如果曲线出现明显平台说明正负样本分布不均衡建议换成 PR 曲线观察因为 PR 曲线对样本不平衡更敏感。AUCPaint 如果没有 PR 选项用 matplotlib 自己实现二十行就能完成这里不展开。补充一点ROC.py 计算的 AUC 是整个测试集所有样本的。如果论文需要按疾病类型分别报告可以把测试集按疾病分组分别计算 AUC 再取宏平均。这种细节在答辩时很加分。5. 把源码迁移到新数据集的三个检查点疾病本体、孤立节点和阈值5.1 统一疾病 ID 体系换一套数据集时最容易翻车的是疾病 ID 对不上。du.csv 里用 DOIDside.py 里用 MeSH构建出的矩阵会出现大量孤立列。在 build_adjacency 后面加一个断言assert len(set(df[disease]) set(disease2idx.keys())) df[disease].nunique()这个断言确保所有疾病字符串都能映射到索引。失败时优先检查疾病名称标准化尤其是大小写、空格和 ICD 版本差异。多来源数据合并前先用别名表做一次映射不要直接用原始字符串拼接。5.2 孤立节点让 AUC 失真如果数据集中药物的平均关联数不足 2大部分节点没有有效邻居预测分数会集中到同一数值。此时 AUC 可能虚高但推荐列表没有实际意义。建议统计每个药物的度观察直方图。对低度节点可以单独分组评估或者给邻接矩阵加一个小的自环值让每个节点至少保留部分自身信号。5.3 从预测分数到推荐列表的阈值选择预测分数矩阵生成后最终要输出一个推荐药物列表。通常用 PR 曲线选择阈值因为药物-疾病关系中的正样本往往很少而 ROC 曲线会掩盖这种不平衡。下面是常见实现from sklearn.metrics import precision_recall_curve import numpy as np prec, rec, thresholds precision_recall_curve(y_true, y_score) f1 2 * prec * rec / (prec rec 1e-9) best_idx np.argmax(f1[:-1]) best_threshold thresholds[best_idx] print(best threshold:, best_threshold)逻辑说明precision_recall_curve 返回每个阈值下的精确率和召回率f1 数组最后一位对应无阈值情况所以索引切片到 f1[:-1]。np.argmax 返回最大 F1 的位置对应阈值就是推荐列表的切分点。参数说明如果数据极度不平衡可以把 F1 换成 F2给召回率更高权重。把筛选出的药物-疾病对保存成 CSV配合 AUCPaint 画出的 ROC 曲线就是毕设里“预测结果可视化”最完整的一版素材。本文还有配套的精品资源点击获取
返回列表