1. 从竞赛题目到实战项目一次完整的数据分析旅程去年带学生备赛又翻出了2022年高教社杯国赛C题《古代玻璃制品的成分分析与鉴别》。这道题很有意思它不像很多纯数学推导的题目而是把一个真实的考古学问题包装成了一个典型的数据挖掘与机器学习任务。题目给了你一批古代玻璃文物的化学成分数据比如二氧化硅、氧化钠、氧化钾这些氧化物的含量百分比然后让你去分析它们的风化情况、分类规律甚至预测未知样品的类别。很多队伍拿到题的第一反应是“套模型”但真正做下来才发现从数据清洗到特征工程再到模型选择与解释每一步都藏着坑。今天我就以Python为工具抛开竞赛的限时压力从头到尾、掰开揉碎地聊聊如何把这样一道赛题做成一个扎实、可复现的数据分析项目。无论你是想学习数据科学实战流程还是对考古材料的科学分析感兴趣这篇文章都能给你提供一个清晰的路线图。这道题的核心是数据但给你的数据往往是“脏”的。比如成分数据是成分点分析数据各成分相加之和本应在85%~105%之间但原始数据里就有大量样本的和不在这个范围这直接影响了后续所有分析的可靠性。再比如玻璃类型分为高钾玻璃和铅钡玻璃风化与否又是个二分类问题但风化样本的化学成分已经发生了变化如何从变化中找规律这些都不是调个sklearn的RandomForestClassifier就能解决的。我们需要像侦探一样先理解数据的“语言”再选择合适的方法去“对话”。接下来我会按照一个完整数据分析项目的逻辑数据理解与清洗 - 探索性数据分析(EDA) - 特征工程与预处理 - 模型构建与评估 - 结果分析与可视化来拆解整个流程并在每个环节分享我实际做的时候遇到的坑和总结的技巧。2. 数据清洗奠定可靠分析的基石拿到竞赛数据通常是Excel或CSV格式第一步绝对不是急着跑模型。我曾见过有团队直接导入数据就做聚类结果完全不可信问题就出在清洗不到位。对于古代玻璃成分数据清洗工作尤为关键因为检测手段如X射线荧光光谱法和年代久远导致的样品不均一性都会引入噪声和异常。2.1 理解数据字典与业务逻辑首先必须彻底理解每个字段的含义。通常数据会包含文物编号样本ID。文物类型高钾玻璃或铅钡玻璃这是我们的预测目标之一。风化情况风化或未风化另一个预测目标。化学成分SiO2, Na2O, K2O, CaO, MgO, Al2O3, Fe2O3, CuO, PbO, BaO等十余种氧化物的含量单位%。这里隐藏的第一个业务逻辑是各化学成分含量之和应有一个合理范围。题目中明确提到是85%~105%。这是因为检测的是成分点并非整体且某些挥发性成分可能未计入。清洗时必须首先计算每个样本的化学成分总和。import pandas as pd import numpy as np # 假设数据已加载到DataFrame df中 # 列出所有化学成分的列名 chemistry_cols [‘SiO2‘, ‘Na2O‘, ‘K2O‘, ‘CaO‘, ‘MgO‘, ‘Al2O3‘, ‘Fe2O3‘, ‘CuO‘, ‘PbO‘, ‘BaO‘, ‘P2O5‘, ‘SrO‘, ‘SnO2‘, ‘SO2‘] # 计算总和注意可能存在缺失值 df[‘sum_chemistry‘] df[chemistry_cols].sum(axis1, skipnaFalse) # 检查总和分布 print(df[‘sum_chemistry‘].describe()) import matplotlib.pyplot as plt plt.figure(figsize(10,6)) plt.hist(df[‘sum_chemistry‘].dropna(), bins30, edgecolor‘black‘) plt.axvline(x85, color‘r‘, linestyle‘--‘, label‘Lower Bound (85%)‘) plt.axvline(x105, color‘g‘, linestyle‘--‘, label‘Upper Bound (105%)‘) plt.xlabel(‘Sum of Chemical Compositions (%)‘) plt.ylabel(‘Frequency‘) plt.legend() plt.title(‘Distribution of Total Composition Sum‘) plt.show()2.2 处理缺失值与异常总和可视化后你可能会发现一部分样本的总和明显超出合理范围或者存在大量缺失值在竞赛数据中可能表示为“NaN”或空值。这里就需要制定策略剔除严重异常样本对于总和远低于85%或远高于105%例如70%或120%的样本这可能是数据录入错误或检测严重失效应考虑直接剔除。但需要记录剔除的数量和原因。合理化修正对于总和在合理范围附近但略有偏差的样本比如在80%-110%之间一个常见的做法是进行归一化处理。即将每个化学成分按比例缩放使其总和等于100%。这假设了偏差是由于系统误差或微量未检测成分造成的。# 对总和在[80, 110]范围内的样本进行归一化至100% mask_reasonable (df[‘sum_chemistry‘] 80) (df[‘sum_chemistry‘] 110) df.loc[mask_reasonable, chemistry_cols] df.loc[mask_reasonable, chemistry_cols].div(df.loc[mask_reasonable, ‘sum_chemistry‘], axis0) * 100 # 重新计算总和 df.loc[mask_reasonable, ‘sum_chemistry‘] df.loc[mask_reasonable, chemistry_cols].sum(axis1)处理缺失值化学成分的缺失值不能简单用0或均值填充。因为0代表该成分不存在而均值填充会扭曲样本本身的特性。更合理的策略是分类型填充分别计算高钾玻璃和铅钡玻璃在未风化状态下各成分的中位数或众数用对应类型的统计量填充。这利用了“同类样品成分相似”的先验知识。视为未检出对于微量元素如CuO, SnO2如果缺失比例很高可以考虑将缺失值视为低于检测限填充为一个极小的正值如0.001但需要注明因为这会影响到后续取对数的操作。使用机器学习模型预测填充如果数据量足够可以构建一个回归模型用其他成分来预测缺失成分。但这在竞赛时间有限的情况下复杂度较高。实操心得不要追求一步到位的完美清洗。我通常采用迭代式清洗先处理明显的异常总和然后进行初步的EDA观察处理后的数据分布再决定如何处理缺失值。清洗的每一步决策都要记录在代码注释或文档中这本身就是数据分析报告的重要组成部分。2.3 衍生关键特征风化指示指数题目要求分析风化点与未风化点的成分差异。一个直接的想法是计算风化前后某些成分的比值或差值。但数据中可能是一个样本只有一个状态风化或未风化。我们可以利用统计方法构建一个“风化倾向”指数。例如风化通常会导致碱金属氧化物Na2O, K2O流失而某些氧化物如SiO2, Al2O3相对富集。我们可以计算一个(Na2OK2O) / (SiO2Al2O3)的比值理论上风化样本的这个比值会更低。但注意高钾玻璃和铅钡玻璃的基体不同这个规律需要分别验证。# 计算可能的风化敏感指标 df[‘alkali_to_network‘] (df[‘Na2O‘] df[‘K2O‘]) / (df[‘SiO2‘] df[‘Al2O3‘]) # 注意处理除零错误 df[‘alkali_to_network‘].replace([np.inf, -np.inf], np.nan, inplaceTrue)清洗后的数据才是我们所有后续分析的起点。这一步花费的时间通常占整个项目的30%以上但绝对物有所值。3. 探索性数据分析用视觉发现故事数据清洗后我们进入EDA阶段。这个阶段的目标不是建模而是熟悉数据、验证假设、发现规律、寻找异常。对于古代玻璃数据我们关心几个核心问题两类玻璃高钾、铅钡在成分上有何本质区别风化究竟改变了什么哪些成分是分类的关键3.1 成分分布与玻璃类型的关系首先我们可以分别绘制高钾玻璃和铅钡玻璃各化学成分的分布直方图或箱线图。箱线图尤其适合对比。import seaborn as sns # 假设‘type‘列是玻璃类型 fig, axes plt.subplots(4, 4, figsize(20, 16)) # 根据成分数量调整子图 axes axes.ravel() chemistry_cols [‘SiO2‘, ‘Na2O‘, ‘K2O‘, ‘CaO‘, ‘MgO‘, ‘Al2O3‘, ‘Fe2O3‘, ‘CuO‘, ‘PbO‘, ‘BaO‘] # 选取主要成分 for idx, col in enumerate(chemistry_cols): if idx len(axes): sns.boxplot(x‘type‘, ycol, datadf, axaxes[idx]) axes[idx].set_title(f‘Distribution of {col} by Glass Type‘) axes[idx].tick_params(axis‘x‘, rotation45) plt.tight_layout() plt.show()通过这个图你可以立刻发现铅钡玻璃顾名思义PbO和BaO的含量显著高于高钾玻璃这几乎是决定性的区分特征。高钾玻璃K2O的含量显著更高而Na2O含量可能较低。其他成分如SiO2、CaO等在两类玻璃中分布可能重叠但中位数和离散度可能有差异。这个直观认识非常重要它告诉你如果用一个简单的规则分类看PbO和K2O可能就非常有效。但我们的模型需要更精细。3.2 相关性分析与热图接下来看看各化学成分之间的相关性。特别是在风化样本和非风化样本中相关性结构是否发生了变化这能提示风化过程的化学机制。# 计算相关系数矩阵 corr_matrix df[chemistry_cols].corr(method‘pearson‘) plt.figure(figsize(12,10)) sns.heatmap(corr_matrix, annotTrue, fmt‘.2f‘, cmap‘coolwarm‘, center0, squareTrue) plt.title(‘Correlation Matrix of Chemical Compositions‘) plt.show() # 分别绘制风化与未风化样本的相关性热图 fig, (ax1, ax2) plt.subplots(1, 2, figsize(20, 8)) weathered_corr df[df[‘weathering‘]‘风化‘][chemistry_cols].corr() unweathered_corr df[df[‘weathering‘]‘未风化‘][chemistry_cols].corr() sns.heatmap(weathered_corr, annotTrue, fmt‘.2f‘, cmap‘coolwarm‘, center0, squareTrue, axax1) ax1.set_title(‘Correlation (Weathered Samples)‘) sns.heatmap(unweathered_corr, annotTrue, fmt‘.2f‘, cmap‘coolwarm‘, center0, squareTrue, axax2) ax2.set_title(‘Correlation (Unweathered Samples)‘) plt.tight_layout() plt.show()你可能会发现在风化样本中Na2O、K2O与SiO2的负相关性增强因为碱金属流失硅相对富集或者PbO和BaO的相关性发生变化。这些都为后续构建特征提供了思路。3.3 降维可视化PCA与t-SNE当特征维度高时我们需要降维技术来观察样本在低维空间的聚集情况。主成分分析是经典方法。from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler # 先标准化数据因为化学成分量纲一致但方差可能不同 scaler StandardScaler() X_scaled scaler.fit_transform(df[chemistry_cols].fillna(0)) # 简单填充实际应用更精细 # 执行PCA pca PCA(n_components2) # 先看前两个主成分 X_pca pca.fit_transform(X_scaled) df[‘PC1‘] X_pca[:, 0] df[‘PC2‘] X_pca[:, 1] print(fExplained variance ratio: {pca.explained_variance_ratio_}) # 用前两个主成分绘图按类型和风化着色 plt.figure(figsize(12,5)) plt.subplot(1,2,1) scatter plt.scatter(df[‘PC1‘], df[‘PC2‘], cpd.factorize(df[‘type‘])[0], cmap‘viridis‘, alpha0.7) plt.xlabel(‘PC1 ({:.1f}%)‘.format(pca.explained_variance_ratio_[0]*100)) plt.ylabel(‘PC2 ({:.1f}%)‘.format(pca.explained_variance_ratio_[1]*100)) plt.title(‘PCA - Colored by Glass Type‘) plt.colorbar(scatter, ticks[0,1], label‘Type‘).set_ticklabels([‘High-K‘, ‘Lead-Barium‘]) plt.subplot(1,2,2) scatter plt.scatter(df[‘PC1‘], df[‘PC2‘], cpd.factorize(df[‘weathering‘])[0], cmap‘plasma‘, alpha0.7) plt.xlabel(‘PC1 ({:.1f}%)‘.format(pca.explained_variance_ratio_[0]*100)) plt.ylabel(‘PC2 ({:.1f}%)‘.format(pca.explained_variance_ratio_[1]*100)) plt.title(‘PCA - Colored by Weathering‘) plt.colorbar(scatter, ticks[0,1], label‘Weathering‘).set_ticklabels([‘Unweathered‘, ‘Weathered‘]) plt.tight_layout() plt.show()通过PCA图你可以直观看到两类玻璃是否在主成分空间上能被明显分开通常可以因为PbO/BaO和K2O的贡献很大。风化和未风化的样本是混杂在一起还是形成了子簇这能初步判断“风化”这个标签是否在成分空间上有清晰的边界。如果PCA显示两类玻璃区分明显但内部风化状态混杂可以尝试对每类玻璃单独做PCA观察其内部的风化趋势。或者使用非线性降维方法如t-SNE它对局部结构更敏感可能揭示出PCA未能展现的聚类模式。踩坑提醒t-SNE的超参数困惑度perplexity对结果影响巨大且每次运行结果可能略有不同。它更适合展示不适合作为稳定的特征输入。在竞赛报告中可以同时展示PCA和t-SNE的结果并解释其一致性或差异性。EDA阶段是产生洞见的黄金时期。多画图多从不同角度切分数据按类型、按风化、按出土地点等把数据“看透”你才能对后续建模该做什么、不该做什么心中有数。4. 特征工程与预处理为模型准备“食材”经过EDA我们对数据有了感性认识。现在需要将这些认识转化为模型能更好利用的特征。特征工程的好坏往往比模型选择本身更能决定最终效果的上限。4.1 基于化学知识的特征构造这是本项目最具特色的一环。我们不能只把原始氧化物百分比扔给模型而要结合古代玻璃工艺和风化化学的知识构造更有意义的特征。类型判别特征PbO/BaO ratio铅钡玻璃中PbO和BaO的比例可能隐含了矿料来源或工艺差异的信息。(K2O)/(K2ONa2O)即钾碱度对于高钾玻璃这个值接近1对于铅钡玻璃通常钠含量也低这个值可能也有区分度。Is_high_Pb_Ba一个布尔特征标记PbOBaO是否超过某个阈值如20%。风化判别特征碱金属流失指数(Na2O_original - Na2O_current) / Na2O_original。但我们的数据只有当前状态。可以改用相对富集系数。假设风化过程中SiO2和Al2O3等网络形成体相对稳定而碱金属流失那么(Na2OK2O) / (SiO2Al2O3)这个比值在风化样本中应该降低。我们已经在清洗阶段构造过类似特征。元素对比值风化可能导致某些元素对的比例发生变化。例如CaO/MgO石灰与菱镁矿来源、Fe2O3/Al2O3铁铝土杂质等。这些比值的变化可能与环境条件有关。模拟“未风化”成分一个更高级的思路是如果我们假设同类未风化玻璃的成分分布是稳定的可以用同类未风化玻璃各成分的中位数作为该风化样本的“原始成分”估计然后计算每个成分的(实测值-估计原始值)/估计原始值得到一系列风化增减量特征。这需要数据中有足够多的未风化样本作为参考。交互项与多项式特征某些氧化物之间可能存在协同或拮抗效应。例如PbO的存在可能会影响其他氧化物的溶解度或析晶行为。可以尝试添加一些重要的交互项如PbO * SiO2、K2O * Al2O3等。但要注意这会增加特征维度可能引发过拟合最好结合领域知识或通过特征选择来控制。4.2 数据标准化与编码标准化虽然化学成分都是百分比量纲相同但方差差异很大例如SiO2可能从60%到80%而CuO可能只有0.0x%。对于基于距离的模型如KNN、SVM、PCA和神经网络标准化Z-score或归一化Min-Max是必要的。对于树模型如随机森林、XGBoost则不一定需要但做了通常也无害。我通常使用StandardScaler。from sklearn.preprocessing import StandardScaler feature_cols chemistry_cols [‘alkali_to_network‘, ‘PbO_to_BaO‘] # 加上构造的特征 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train[feature_cols]) X_test_scaled scaler.transform(X_test[feature_cols]) # 注意用训练集的参数转换测试集标签编码目标变量“玻璃类型”和“风化情况”是分类变量需要编码。对于二分类问题直接用0/1编码即可。对于多分类如果还有其他类型可以使用LabelEncoder或pd.factorize。from sklearn.preprocessing import LabelEncoder le_type LabelEncoder() y_type le_type.fit_transform(df[‘type‘]) # 例如高钾-0 铅钡-1 le_weather LabelEncoder() y_weather le_weather.fit_transform(df[‘weathering‘]) # 未风化-0 风化-14.3 处理类别不平衡在考古数据中两类玻璃的数量或者风化与未风化的数量很可能是不平衡的。例如可能铅钡玻璃样本远多于高钾玻璃。直接训练模型会导致模型偏向多数类。解决策略在评估时使用合适的指标不要只看准确率Accuracy更要看精确率Precision、召回率Recall、F1-score尤其是少数类的F1。混淆矩阵Confusion Matrix是必须展示的。在算法层面处理对于逻辑回归、SVM等可以设置class_weight‘balanced‘让算法自动调整类别权重。对于树模型如随机森林也可以使用class_weight参数。在数据层面处理过采样使用SMOTESynthetic Minority Over-sampling Technique为少数类合成新样本。注意SMOTE在特征空间生成样本对于成分数据这种物理化学量生成的“合成玻璃”成分在化学上是否合理需要谨慎评估。可以先在小范围内尝试并检查合成样本的特征值是否在合理范围内。欠采样随机减少多数类样本但可能会丢失重要信息。我的经验对于这类成分数据我倾向于优先使用算法层面的class_weight因为它不改变原始数据的分布。如果效果不佳再谨慎尝试SMOTE并密切监控模型在验证集上的表现防止过拟合到人工合成的样本上。5. 模型构建、评估与优化现在我们有了干净的数据、构造好的特征可以开始建模了。任务本质上是两个分类问题1) 玻璃类型分类高钾 vs. 铅钡2) 风化状态分类风化 vs. 未风化。我们可以分别构建模型。5.1 模型选择与基准线对于这种样本量可能不大通常竞赛数据几百个样本、特征维度适中十几个到二十几个的结构化数据以下模型值得尝试逻辑回归非常好的基准模型。它简单、可解释性强。通过查看系数大小和正负可以直接知道哪些成分对分类贡献大例如PbO的系数为正且很大说明PbO高更可能是铅钡玻璃。使用L1或L2正则化可以防止过拟合。支持向量机特别是线性SVM效果通常不错。对于可能线性可分的问题如两类玻璃SVM能找到一个鲁棒性较好的决策边界。可以尝试不同的核函数线性、RBF但要注意RBF核可能在小数据集上过拟合。随机森林强大的集成方法能自动处理特征交互对异常值不敏感通常能取得不错的性能。更重要的是它能提供特征重要性排序这对于我们理解“哪些化学成分最关键”非常有价值。XGBoost/LightGBM梯度提升树在众多竞赛中表现优异。它们能高效处理数据并自动进行特征选择和组合。如果追求最高预测精度这是首选。但模型可解释性比随机森林稍差虽然也有特征重要性。多层感知机如果特征经过精心构造且数据量足够简单的神经网络也可能有不错的表现。但对于小数据集容易过拟合。建议的建模流程将数据按7:3或8:2划分为训练集和测试集。务必使用分层抽样train_test_split中的stratify参数以保证训练集和测试集中两类玻璃、两种风化状态的比例与全集一致。先用逻辑回归和随机森林建立基准线。使用交叉验证如5折或10折在训练集上评估模型选择主要评估指标如F1-score的宏平均。对表现好的模型进行超参数调优如GridSearchCV或RandomizedSearchCV。5.2 以玻璃类型分类为例的代码实现from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from sklearn.svm import SVC from sklearn.metrics import classification_report, confusion_matrix, accuracy_score, f1_score # 假设 X 是特征DataFrame y_type 是编码后的类型标签 X_train, X_test, y_train, y_test train_test_split(X, y_type, test_size0.2, random_state42, stratifyy_type) # 基准模型逻辑回归 lr LogisticRegression(max_iter1000, class_weight‘balanced‘, random_state42) lr_scores cross_val_score(lr, X_train, y_train, cv5, scoring‘f1_macro‘) print(fLogistic Regression CV F1 Macro: {lr_scores.mean():.3f} (/- {lr_scores.std():.3f})) # 训练并查看测试集表现 lr.fit(X_train, y_train) y_pred_lr lr.predict(X_test) print(Logistic Regression Test Performance:) print(classification_report(y_test, y_pred_lr, target_namesle_type.classes_)) # 查看特征系数 lr_coef pd.DataFrame({‘feature‘: X.columns, ‘coefficient‘: lr.coef_[0]}) lr_coef[‘abs_coef‘] np.abs(lr_coef[‘coefficient‘]) print(lr_coef.sort_values(by‘abs_coef‘, ascendingFalse).head(10)) # 基准模型随机森林 rf RandomForestClassifier(n_estimators100, class_weight‘balanced‘, random_state42) rf_scores cross_val_score(rf, X_train, y_train, cv5, scoring‘f1_macro‘) print(f\nRandom Forest CV F1 Macro: {rf_scores.mean():.3f} (/- {rf_scores.std():.3f})) rf.fit(X_train, y_train) y_pred_rf rf.predict(X_test) print(Random Forest Test Performance:) print(classification_report(y_test, y_pred_rf, target_namesle_type.classes_)) # 查看特征重要性 rf_importance pd.DataFrame({‘feature‘: X.columns, ‘importance‘: rf.feature_importances_}) print(rf_importance.sort_values(by‘importance‘, ascendingFalse).head(10))5.3 模型解释与对比运行上述代码后你会得到两个模型的性能报告和特征重要性/系数。逻辑回归的系数正系数表示该特征值增大会使样本更可能被预测为“1”假设铅钡玻璃为1。你会发现PbO、BaO的系数很大且为正K2O的系数可能为负这与我们的化学知识完全吻合。这增强了模型的可信度。随机森林的特征重要性它显示的是每个特征在减少模型不纯度方面的平均贡献。通常PbO、BaO、K2O、SiO2等会排在前列。如果某个构造的特征如alkali_to_network重要性很高说明它有效捕捉了分类信息。对比与选择如果两个模型在测试集上的F1分数相差不大比如0.02我倾向于选择逻辑回归因为它更简单、更可解释在论文中更容易说清楚道理。如果随机森林或XGBoost明显更优则选择性能更好的模型。但需要在报告中解释其决策过程可以通过SHAP等工具进行事后解释。5.4 风化状态分类的特殊性风化分类可能比类型分类更难因为风化是一个连续过程且受埋藏环境影响大成分变化可能不如类型差异那么显著。此时特征工程的作用更大。策略分而治之分别对高钾玻璃和铅钡玻璃建立风化预测模型。因为两者的风化机理可能不同铅钡玻璃可能更易风化且风化产物不同。重点关注构造的特征之前构造的“碱金属流失指数”、“相对富集系数”等特征应该成为模型的核心输入。尝试集成学习如果单一模型效果不佳可以尝试Stacking或Voting将逻辑回归、SVM、随机森林的预测结果作为元特征训练一个次级分类器。5.5 超参数调优示例以随机森林为例from sklearn.model_selection import RandomizedSearchCV # 定义参数分布 param_dist { ‘n_estimators‘: [50, 100, 200, 300], ‘max_depth‘: [None, 10, 20, 30], ‘min_samples_split‘: [2, 5, 10], ‘min_samples_leaf‘: [1, 2, 4], ‘max_features‘: [‘auto‘, ‘sqrt‘, ‘log2‘], ‘bootstrap‘: [True, False] } rf RandomForestClassifier(class_weight‘balanced‘, random_state42) rf_random_search RandomizedSearchCV(estimatorrf, param_distributionsparam_dist, n_iter50, cv5, scoring‘f1_macro‘, verbose2, random_state42, n_jobs-1) rf_random_search.fit(X_train, y_train) print(fBest parameters: {rf_random_search.best_params_}) print(fBest CV score: {rf_random_search.best_score_:.3f}) # 用最佳模型评估测试集 best_rf rf_random_search.best_estimator_ y_pred_best best_rf.predict(X_test) print(classification_report(y_test, y_pred_best, target_namesle_type.classes_))调优心得不要过度调优。对于小数据集复杂的模型和精细的调参很容易导致过拟合。交叉验证的分数是参考最终一定要看模型在完全未参与训练和调参的测试集上的表现。如果调参后测试集性能提升不明显甚至下降说明可能过拟合了应回到更简单的模型或参数。6. 结果可视化与报告撰写模型建好了性能也不错最后一步是如何清晰、有说服力地展示你的工作。这对于竞赛或项目报告至关重要。6.1 核心结果可视化混淆矩阵热图直观展示模型在每个类别上的分类情况。from sklearn.metrics import ConfusionMatrixDisplay fig, (ax1, ax2) plt.subplots(1, 2, figsize(12,5)) ConfusionMatrixDisplay.from_estimator(lr, X_test, y_test, display_labelsle_type.classes_, axax1, cmap‘Blues‘) ax1.set_title(‘Logistic Regression Confusion Matrix‘) ConfusionMatrixDisplay.from_estimator(best_rf, X_test, y_test, display_labelsle_type.classes_, axax2, cmap‘Blues‘) ax2.set_title(‘Optimized Random Forest Confusion Matrix‘) plt.tight_layout() plt.show()特征重要性/系数条形图这是体现你工作深度的关键。# 绘制随机森林特征重要性 top_n 15 rf_importance_sorted rf_importance.sort_values(by‘importance‘, ascendingFalse).head(top_n) plt.figure(figsize(10,6)) plt.barh(range(top_n), rf_importance_sorted[‘importance‘]) plt.yticks(range(top_n), rf_importance_sorted[‘feature‘]) plt.xlabel(‘Feature Importance‘) plt.title(‘Top 15 Feature Importance for Glass Type Classification (Random Forest)‘) plt.gca().invert_yaxis() plt.tight_layout() plt.show()决策边界可视化针对两个最重要的特征如果特征维度高可以选取最重要的两个特征如PbO和K2O绘制样本散点图并叠加模型的决策边界。这对于线性模型如逻辑回归、线性SVM非常直观。# 选取两个最重要的特征 top_features rf_importance_sorted[‘feature‘].iloc[:2].tolist() X_top X_train[top_features].values # 训练一个只基于这两个特征的逻辑回归模型用于可视化 lr_viz LogisticRegression() lr_viz.fit(X_top, y_train) # ... 创建网格预测绘制等高线和散点图 ...6.2 撰写分析报告的核心要点在报告或论文中你需要清晰地阐述以下内容问题重述与数据概况简要说明任务并给出数据的基本统计描述样本数、特征数、缺失情况、类别分布。数据预处理流程详细说明你是如何清洗数据总和校正、缺失值处理、构造新特征的并解释每一步的理由。这是体现你思考过程的地方。探索性数据分析发现用图表展示关键发现如两类玻璃的成分分布差异、主要成分的相关性、PCA/t-SNE的聚类效果。并给出文字解读。建模方法与评估说明你选择了哪些模型为什么选择它们例如逻辑回归用于可解释性基准随机森林用于捕捉复杂关系。说明数据划分方式、评估指标为什么用F1而不仅仅是准确率。给出交叉验证和测试集的结果表格包括准确率、精确率、召回率、F1-score。展示最优模型的参数。结果分析与讨论模型性能分析哪个模型最好好在哪里是少数类召回率高还是整体更平衡特征重要性分析结合化学知识解释为什么这些特征最重要。例如“PbO和BaO在随机森林中重要性最高这与铅钡玻璃的定义相符而K2O的负重要性则支持了高钾玻璃的判定。”误判样本分析找出被模型错误分类的样本尝试分析原因。是数据本身有噪声还是这些样本处于两类玻璃的过渡区域或者是风化严重干扰了成分结论与展望总结你的主要发现例如“基于化学成分机器学习模型可以高精度区分高钾玻璃和铅钡玻璃其中PbO、BaO和K2O是最具判别力的指标。”。指出模型的局限性例如“对于严重风化的样本分类性能下降说明风化过程会模糊类型特征。”。提出可能的改进方向例如“未来可以引入微量元素比值或同位素数据以进一步提高分类精度和溯源能力。”。通过这样一套从数据到模型再到解释和报告的完整流程你不仅解决了竞赛题目更掌握了一个处理类似成分分析、材料鉴别问题的标准数据科学方法论。这个过程里最大的收获不是调出了一个多高的分数而是学会了如何让数据说话如何将领域知识这里是考古化学与机器学习工具紧密结合做出既有预测能力又有解释深度的分析。