矿物类型分类实战:从数据预处理到多模型对比

在地质勘探与矿物分析领域,基于矿物成分数据实现自动化分类是提升分析效率的关键。本文将完整复盘一个矿物类型分类项目,涵盖数据清洗、缺失值填充、样本均衡处理,并对比 6 种传统机器学习模型与 2 种深度学习模型的分类效果,为类似数据驱动的分类任务提供可复用的解决方案。

一、项目背景与数据概况

1. 项目目标

基于矿物的化学成分特征(如镁、铝、硅等元素含量),将矿物分为 A、B、C、D 四类(已剔除特殊类别 E),通过对比不同数据预处理方法与模型,找到最优分类方案。

2. 数据特点

  • 原始数据存储于矿物数据.xls,包含 “序号”“矿物类型” 及 7 个化学成分特征列;
  • 存在三类数据问题:字符串格式数值(如 "12.3")、特殊符号(如 "")、空格与缺失值(NaN);
  • 样本存在类别不均衡问题,后续需通过 SMOTE 算法处理;
  • 部分特征列缺失率较高,需针对性选择缺失值填充方法。

二、完整数据预处理流程

数据预处理直接影响模型效果,本项目采用 “清洗→填充→标准化→均衡” 四步流程,确保数据质量。

1. 数据清洗:统一格式与剔除异常

首先处理格式混乱问题,将非数值数据转为 NaN,便于后续填充:

import pandas as pd

# 读取数据并剔除类别E(仅1条,无统计意义)
data = pd.read_excel("矿物数据.xls")
data = data[data['矿物类型'] != 'E']

# 提取特征与标签(删除序号列,避免干扰模型训练)
X_whole = data.drop('矿物类型', axis=1).drop('序号', axis=1)
y_whole = data['矿物类型']

# 处理异常格式:字符串数值/特殊符号→NaN(仅保留可转为float的值)
for col in X_whole.columns:
    X_whole[col] = pd.to_numeric(X_whole[col], errors='coerce')

# 标签编码(将文字标签转为数字,适配模型输入:A→0, B→1, C→2, D→3)
label_dict = {"A": 0, "B": 1, "C": 2, "D": 3}
y_whole = pd.Series([label_dict[label] for label in y_whole], name='矿物类型')

2. 缺失值填充:6 种方法完整代码实现

缺失值是本数据的核心问题之一,我们设计了传统统计方法机器学习方法两类共 6 种填充策略,并封装为可直接调用的函数,适配训练集与测试集(测试集需用训练集参数填充,避免数据泄露)。

2.1 工具函数封装:fill_data.py 完整代码
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor

"""
1. CCA(Complete Case Analysis):直接删除含缺失值的行
适用场景:缺失率极低(<5%),数据量充足时使用
"""
def cca_train_fill(train_data, train_label):
    # 合并特征与标签,便于统一删除含缺失值的行
    data = pd.concat([train_data, train_label], axis=1).reset_index(drop=True)
    # 删除所有含NaN的行
    df_filled = data.dropna()
    # 拆分回特征与标签
    return df_filled.drop('矿物类型', axis=1), df_filled['矿物类型']

def cca_test_fill(train_data, train_label, test_data, test_label):
    # 测试集处理逻辑与训练集一致(仅删除自身缺失行,不依赖训练集)
    data = pd.concat([test_data, test_label], axis=1).reset_index(drop=True)
    df_filled = data.dropna()
    return df_filled.drop('矿物类型', axis=1), df_filled['矿物类型']


"""
2. 平均值填充:按矿物类别计算均值,填充同类缺失值
适用场景:特征分布近似正态,无极端异常值
"""
def _mean_fill_method(data):
    # 计算每列均值(按类别分组后内部计算)
    fill_values = data.mean()
    return data.fillna(fill_values)

def mean_train_fill(train_data, train_label):
    # 合并数据并按类别拆分(A/B/C/D四类分别填充)
    data = pd.concat([train_data, train_label], axis=1).reset_index(drop=True)
    class_A = data[data['矿物类型'] == 0]
    class_B = data[data['矿物类型'] == 1]
    class_C = data[data['矿物类型'] == 2]
    class_D = data[data['矿物类型'] == 3]
    
    # 按类别填充后合并
    filled_A = _mean_fill_method(class_A)
    filled_B = _mean_fill_method(class_B)
    filled_C = _mean_fill_method(class_C)
    filled_D = _mean_fill_method(class_D)
    df_filled = pd.concat([filled_A, filled_B, filled_C, filled_D]).reset_index(drop=True)
    
    return df_filled.drop('矿物类型', axis=1), df_filled['矿物类型']

def mean_test_fill(train_data, train_label, test_data, test_label):
    # 训练集:按类别计算均值(作为测试集填充依据)
    train_all = pd.concat([train_data, train_label], axis=1).reset_index(drop=True)
    train_A = train_all[train_all['矿物类型'] == 0]
    train_B = train_all[train_all['矿物类型'] == 1]
    train_C = train_all[train_all['矿物类型'] == 2]
    train_D = train_all[train_all['矿物类型'] == 3]
    
    # 测试集:按类别拆分,用训练集同类均值填充
    test_all = pd.concat([test_data, test_label], axis=1).reset_index(drop=True)
    test_A = test_all[test_all['矿物类型'] == 0].fillna(train_A.mean())
    test_B = test_all[test_all['矿物类型'] == 1].fillna(train_B.mean())
    test_C = test_all[test_all['矿物类型'] == 2].fillna(train_C.mean())
    test_D = test_all[test_all['矿物类型'] == 3].fillna(train_D.mean())
    
    df_filled = pd.concat([test_A, test_B, test_C, test_D]).reset_index(drop
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值