运用4种填充方法补充缺失数据的案例
代码实现:
import pandas as pd
import fill_data
# 导入数据
data = pd.read_excel('矿物数据.xls')
# 删除结果为E的行
data = data[data['矿物类型']!='E']
# 提取X和y值
X_whole = data.drop('序号',axis=1).drop('矿物类型',axis=1)
y_whole = data['矿物类型']
# 将结果中的中文字符改为英文字符
label_dict = {'A':0,'B':1,'C':2,'D':3}
xg_label = [label_dict[label] for label in y_whole]
y_whole = pd.Series(xg_label,name="矿物类型")
# 处理特征中不合法的数值,并改成nan
for column_name in X_whole.columns:X_whole[column_name] = pd.to_numeric(X_whole[column_name], errors='coerce')
# (z标准化)
from sklearn.preprocessing import StandardScaler
sds = StandardScaler()
X_whole_z = sds.fit_transform(X_whole)
X_whole = pd.DataFrame(X_whole_z,columns=X_whole.columns)
# 切分测试集和训练集
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(X_whole,y_whole,test_size=0.3,random_state=2000)
'''6中填充方法'''
# 1、 删除空值行
# x_train_fill,y_train_fill = fill_data.cca_train_fill(x_train,y_train)
# x_test_fill,y_test_fill = fill_data.cca_test_fill(x_train_fill,y_train_fill,x_test,y_test)
# 2、 平均值填充
# x_train_fill,y_train_fill = fill_data.mean_train_fill(x_train,y_train)
# x_test_fill,y_test_fill = fill_data.mean_test_fill(x_train_fill,y_train_fill,x_test,y_test)
# 3、 中位数填充
# x_train_fill,y_train_fill = fill_data.median_train_fill(x_train,y_train)
# x_test_fill,y_test_fill = fill_data.median_test_fill(x_train_fill,y_train_fill,x_test,y_test)
# 4、 众数填充
# x_train_fill,y_train_fill = fill_data.mode_train_fill(x_train,y_train)
# x_test_fill,y_test_fill = fill_data.mode_test_fill(x_train_fill,y_train_fill,x_test,y_test)# smote拟合数据
from imblearn.over_sampling import SMOTE
oversampler =SMOTE(k_neighbors=1,random_state=42)
os_x_train, os_y_train = oversampler.fit_resample(x_train_fill,y_train_fill)
# 将数据存入excel
data_train = pd.concat([os_y_train,os_x_train],axis=1).sample(frac=1,random_state=4)
data_test = pd.concat([y_test_fill,x_test_fill],axis=1)data_train.to_excel(r'.//temp_data//训练数据集[随机森林填充].xlsx',index=False)
data_test.to_excel(r'.//temp_data//测试数据集[随机森林填充].xlsx',index=False)
引用的函数:
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
def cca_train_fill(train_data,train_label):data = pd.concat([train_data,train_label],axis =1)data = data.reset_index(drop=True)df = data.dropna()return df.drop('矿物类型', axis=1),df.矿物类型
def cca_test_fill(train_data,train_label,test_data,test_label):data = pd.concat([test_data,test_label], axis=1)data = data.reset_index(drop=True)df = data.dropna()return df.drop('矿物类型', axis=1),df.矿物类型def mean_train_method(data):pjz = data.mean()return data.fillna(pjz)
def mean_train_fill(train_data,train_label):data = pd.concat([train_data,train_label], axis=1)data = data.reset_index(drop=True)A = data[data['矿物类型'] == 0]B = data[data['矿物类型'] == 1]C = data[data['矿物类型'] == 2]D = data[data['矿物类型'] == 3]A = mean_train_method(A)B = mean_train_method(B)C = mean_train_method(C)D = mean_train_method(D)df = pd.concat([A,B,C,D])df = df.reset_index(drop=True)return df.drop('矿物类型',axis=1),df.矿物类型def mean_test_method(train_data,test_data):pjz = train_data.mean()return test_data.fillna(pjz)
def mean_test_fill(train_data,train_label,test_data,test_label):train_data_all = pd.concat([train_data,train_label], axis=1)train_data_all = train_data_all.reset_index(drop=True)test_data_all = pd.concat([test_data,test_label], axis = 1)test_data_all = test_data_all.reset_index(drop=True)A_train = train_data_all[train_data_all['矿物类型'] == 0]B_train = train_data_all[train_data_all['矿物类型'] == 1]C_train = train_data_all[train_data_all['矿物类型'] == 2]D_train = train_data_all[train_data_all['矿物类型'] == 3]A_test = test_data_all[test_data_all['矿物类型'] == 0]B_test = test_data_all[test_data_all['矿物类型'] == 1]C_test = test_data_all[test_data_all['矿物类型'] == 2]D_test = test_data_all[test_data_all['矿物类型'] == 3]A = mean_test_method(A_train,A_test)B = mean_test_method(B_train,B_test )C = mean_test_method(C_train,C_test)D = mean_test_method(D_train,D_test)df = pd.concat([A,B,C,D])df = df.reset_index(drop=True)return df.drop('矿物类型',axis=1),df.矿物类型def median_train_method(data):zwz = data.median()return data.fillna(zwz)
def median_train_fill(train_data,train_label):data = pd.concat([train_data,train_label],axis=1)A = data[data['矿物类型'] == 0]B = data[data['矿物类型'] == 1]C = data[data['矿物类型'] == 2]D = data[data['矿物类型'] == 3]A = median_train_method(A)B = median_train_method(B)C = median_train_method(C)D = median_train_method(D)df = pd.concat([A,B,C,D])df = df.reset_index(drop=True)return df.drop('矿物类型',axis=1),df.矿物类型def median_test_method(train_data,test_data):zwz = train_data.median()return test_data.fillna(zwz)
def median_test_fill(train_data,train_label,test_data,test_label):train_data_all = pd.concat([train_data, train_label], axis=1)test_data_all = pd.concat([test_data, test_label], axis=1)A_train = train_data_all[train_data_all['矿物类型'] == 0]B_train = train_data_all[train_data_all['矿物类型'] == 1]C_train = train_data_all[train_data_all['矿物类型'] == 2]D_train = train_data_all[train_data_all['矿物类型'] == 3]A_test = test_data_all[test_data_all['矿物类型'] == 0]B_test = test_data_all[test_data_all['矿物类型'] == 1]C_test = test_data_all[test_data_all['矿物类型'] == 2]D_test = test_data_all[test_data_all['矿物类型'] == 3]A = median_test_method(A_train, A_test)B = median_test_method(B_train, B_test)C = median_test_method(C_train, C_test)D = median_test_method(D_train, D_test)df = pd.concat([A,B,C,D])df = df.reset_index(drop=True)return df.drop('矿物类型',axis=1),df.矿物类型def mode_train_method(data):zs = data.apply(lambda x: x.mode().iloc[0] if len(x.mode())>0 else None)return data.fillna(zs)
def mode_train_fill(train_data,train_label):data = pd.concat([train_data,train_label],axis=1)A = data[data['矿物类型'] == 0]B = data[data['矿物类型'] == 1]C = data[data['矿物类型'] == 2]D = data[data['矿物类型'] == 3]A = mode_train_method(A)B = mode_train_method(B)C = mode_train_method(C)D = mode_train_method(D)df = pd.concat([A, B, C, D])df = df.reset_index(drop=True)return df.drop('矿物类型', axis=1), df.矿物类型
def mode_test_method(train_data,test_data):zs = train_data.apply(lambda x: x.mode().iloc[0] if len(x.mode())>0 else None)return test_data.fillna(zs)
def mode_test_fill(train_data,train_label,test_data,test_label):train_data_all = pd.concat([train_data, train_label], axis=1)test_data_all = pd.concat([test_data, test_label], axis=1)A_train = train_data_all[train_data_all['矿物类型'] == 0]B_train = train_data_all[train_data_all['矿物类型'] == 1]C_train = train_data_all[train_data_all['矿物类型'] == 2]D_train = train_data_all[train_data_all['矿物类型'] == 3]A_test = test_data_all[test_data_all['矿物类型'] == 0]B_test = test_data_all[test_data_all['矿物类型'] == 1]C_test = test_data_all[test_data_all['矿物类型'] == 2]D_test = test_data_all[test_data_all['矿物类型'] == 3]A = mode_test_method(A_train, A_test)B = mode_test_method(B_train, B_test)C = mode_test_method(C_train, C_test)D = mode_test_method(D_train, D_test)df = pd.concat([A,B,C,D])df = df.reset_index(drop=True)return df.drop('矿物类型',axis=1),df.矿物类型
这段代码是一个矿物分类任务的数据预处理 pipeline,主要实现了从原始数据读取、清洗、标准化到缺失值处理、类别平衡,最终生成训练集和测试集的完整流程。以下是代码解析:
1. 库导入与数据读取
import pandas as pd
import fill_data # 自定义缺失值填充工具模块
# 导入数据
data = pd.read_excel('矿物数据.xls')
导入 pandas
用于数据处理,fill_data
是自定义模块(包含多种缺失值填充方法)。
读取原始矿物数据表格(矿物数据.xls
),存储在 data
变量中(包含特征和标签)。
2. 数据清洗:过滤无效类别
# 删除结果为E的行
data = data[data['矿物类型']!='E']
- 过滤掉标签为
'E'
的样本(可能是无效类别、异常值或不需要的类别),仅保留需要分类的A/B/C/D
四类样本,减少噪声干扰。
3. 特征与标签分离
# 提取X和y值
X_whole = data.drop('序号',axis=1).drop('矿物类型',axis=1) # 特征数据
y_whole = data['矿物类型'] # 标签数据
特征(X):删除无关的 '序号'
列(仅为编号,无实际意义)和标签列 '矿物类型'
,剩余列作为模型输入的特征(如矿物的化学成分、物理属性等)。
标签(y):提取 '矿物类型'
列作为分类目标(即需要预测的矿物类别)。
4. 标签编码:文字转数字
# 将结果中的中文字符改为英文字符(实际是文字标签转数字编码)
label_dict = {'A':0,'B':1,'C':2,'D':3}
xg_label = [label_dict[label] for label in y_whole]
y_whole = pd.Series(xg_label,name="矿物类型")
机器学习模型通常需要数值型标签,因此通过字典映射将原始文字标签('A'/'B'/'C'/'D'
)转换为数字(0/1/2/3
)。
转换后标签存储为 pd.Series
,保持数据结构一致性,方便后续处理。
5. 特征清洗:处理无效数值
# 处理特征中不合法的数值,并改成nan
for column_name in X_whole.columns:X_whole[column_name] = pd.to_numeric(X_whole[column_name], errors='coerce')
遍历所有特征列,使用 pd.to_numeric(..., errors='coerce')
将非数值型数据(如字符串、特殊符号等)强制转换为 NaN
(缺失值)。
确保所有特征均为数值类型,避免后续模型训练因数据类型错误报错。
6. 特征标准化:Z 标准化
# (z标准化)
from sklearn.preprocessing import StandardScaler
sds = StandardScaler()
X_whole_z = sds.fit_transform(X_whole)
X_whole = pd.DataFrame(X_whole_z,columns=X_whole.columns)
Z 标准化:将特征转换为均值为 0、标准差为 1 的分布(公式:(x - 均值) / 标准差
)。
作用:消除不同特征的量纲差异(如有的特征单位是百分比,有的是浓度值),使模型更易收敛(对 SVM、KNN 等基于距离的模型尤为重要)。
转换后的数据重新包装为 DataFrame
,保留原始列名,便于追溯特征含义。
7. 数据集划分:训练集与测试集
# 切分测试集和训练集
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(X_whole,y_whole,test_size=0.3,random_state=2000)
使用 train_test_split
将数据按 7:3 划分为训练集(70%)和测试集(30%)。
参数说明:test_size=0.3
:测试集占比 30%;
random_state=2000
:固定随机种子,确保每次运行划分结果一致(可复现性)。
划分后变量:x_train/x_test
(训练 / 测试特征)、y_train/y_test
(训练 / 测试标签)。
8. 缺失值填充:多种方法可选(当前未启用)
'''4种填充方法'''
# 1、 删除空值行
# x_train_fill,y_train_fill = fill_data.cca_train_fill(x_train,y_train)
# x_test_fill,y_test_fill = fill_data.cca_test_fill(x_train_fill,y_train_fill,x_test,y_test)
# 2、 平均值填充
# x_train_fill,y_train_fill = fill_data.mean_train_fill(x_train,y_train)
# x_test_fill,y_test_fill = fill_data.mean_test_fill(x_train_fill,y_train_fill,x_test,y_test)
# 3、 中位数填充
# x_train_fill,y_train_fill = fill_data.median_train_fill(x_train,y_train)
# x_test_fill,y_test_fill = fill_data.median_test_fill(x_train_fill,y_train_fill,x_test,y_test)
# 4、 众数填充
# x_train_fill,y_train_fill = fill_data.mode_train_fill(x_train,y_train)
# x_test_fill,y_test_fill = fill_data.mode_test_fill(x_train_fill,y_train_fill,x_test,y_test)
代码提供了 64种缺失值填充方法(如删除空值行、平均值填充、中位数填充等),但当前均被注释(未启用)。
核心逻辑:先填充训练集(用训练集自身信息),再用训练集的填充规则填充测试集(避免数据泄露,即测试集不能使用自身信息填充)。
注意:由于此处未启用任何填充方法,后续代码可能因存在 NaN
报错,实际使用时需取消对应方法的注释。
4种填充方法函数解析:
这段代码是一个自定义缺失值填充工具模块(fill_data
),专为矿物分类数据设计了 4 类缺失值处理方法(删除空值法、平均值填充、中位数填充、众数填充),每种方法都包含训练集填充和测试集填充两个函数,严格遵循 “用训练集信息填充测试集” 的原则(避免数据泄露)。以下是详细解析:
*1. 删除空值法(cca_ 函数)**
直接删除包含缺失值的样本,是最简单的缺失值处理方式。
# 训练集删除空值
def cca_train_fill(train_data,train_label):# 合并特征和标签,重置索引data = pd.concat([train_data,train_label],axis =1).reset_index(drop=True)# 删除所有含缺失值的行df = data.dropna()# 返回填充后的特征和标签(分离)return df.drop('矿物类型', axis=1), df.矿物类型# 测试集删除空值
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 = data.dropna()# 返回填充后的特征和标签(分离)return df.drop('矿物类型', axis=1), df.矿物类型
适用场景:缺失值比例极低(如 < 5%),且样本量充足的情况。
优缺点:简单直接,但会丢失数据(尤其缺失值多时,可能导致样本量不足)。
*2. 平均值填充(mean_ 函数)**
按矿物类别计算特征的平均值,用该平均值填充同类别样本的缺失值(训练集用自身均值,测试集用训练集同类别均值)。
# 训练集填充逻辑:计算同类别平均值并填充
def mean_train_method(data):pjz = data.mean() # 计算当前数据中各特征的平均值return data.fillna(pjz) # 用平均值填充缺失值def mean_train_fill(train_data,train_label):# 合并训练集特征和标签data = pd.concat([train_data,train_label], axis=1).reset_index(drop=True)# 按矿物类型(0-3)分组(A=0, B=1, C=2, D=3)A = data[data['矿物类型'] == 0]B = data[data['矿物类型'] == 1]C = data[data['矿物类型'] == 2]D = data[data['矿物类型'] == 3]# 每组内用自身平均值填充缺失值A = mean_train_method(A)B = mean_train_method(B)C = mean_train_method(C)D = mean_train_method(D)# 合并所有组,重置索引df = pd.concat([A,B,C,D]).reset_index(drop=True)# 返回填充后的特征和标签return df.drop('矿物类型',axis=1), df.矿物类型# 测试集填充逻辑:用训练集同类别平均值填充
def mean_test_method(train_data,test_data):pjz = train_data.mean() # 用训练集的平均值作为填充参数return test_data.fillna(pjz) # 填充测试集的缺失值def mean_test_fill(train_data,train_label,test_data,test_label):# 合并训练集和测试集的特征与标签train_data_all = pd.concat([train_data,train_label], axis=1).reset_index(drop=True)test_data_all = pd.concat([test_data,test_label], axis = 1).reset_index(drop=True)# 按矿物类型分组(训练集和测试集各分4组)A_train = train_data_all[train_data_all['矿物类型'] == 0]B_train = train_data_all[train_data_all['矿物类型'] == 1]C_train = train_data_all[train_data_all['矿物类型'] == 2]D_train = train_data_all[train_data_all['矿物类型'] == 3]A_test = test_data_all[test_data_all['矿物类型'] == 0]B_test = test_data_all[test_data_all['矿物类型'] == 1]C_test = test_data_all[test_data_all['矿物类型'] == 2]D_test = test_data_all[test_data_all['矿物类型'] == 3]# 测试集每组用训练集同组的平均值填充A = mean_test_method(A_train,A_test)B = mean_test_method(B_train,B_test )C = mean_test_method(C_train,C_test)D = mean_test_method(D_train,D_test)# 合并测试集所有组,重置索引df = pd.concat([A,B,C,D]).reset_index(drop=True)# 返回填充后的特征和标签return df.drop('矿物类型',axis=1), df.矿物类型
核心逻辑:同一类矿物的特征分布更相似,用同类别均值填充比全局均值更精准。
适用场景:特征为连续值,且分布近似正态(均值能代表集中趋势)。
*3. 中位数填充(median_ 函数)**
逻辑与平均值填充完全一致,仅将 “平均值” 替换为 “中位数”,更抗极端值干扰。
# 训练集用同类别中位数填充
def median_train_method(data):zwz = data.median() # 计算中位数return data.fillna(zwz)def median_train_fill(train_data,train_label):# 逻辑同mean_train_fill,仅用median_train_method替换mean_train_method...# 测试集用训练集同类别中位数填充
def median_test_method(train_data,test_data):zwz = train_data.median() # 用训练集中位数return test_data.fillna(zwz)def median_test_fill(...):# 逻辑同mean_test_fill,仅用median_test_method替换mean_test_method...
核心优势:中位数对异常值(如矿物特征中的极端测量值)更稳健,适合特征分布有偏态的场景。
*4. 众数填充(mode_ 函数)**
用特征中出现次数最多的值(众数)填充缺失值,适合离散特征(如分类变量)。
# 训练集用同类别众数填充
def mode_train_method(data):# 计算众数:若存在众数则取第一个,否则为Nonezs = data.apply(lambda x: x.mode().iloc[0] if len(x.mode())>0 else None)return data.fillna(zs)def mode_train_fill(train_data,train_label):# 逻辑同mean_train_fill,仅用mode_train_method替换mean_train_method...# 测试集用训练集同类别众数填充
def mode_test_method(train_data,test_data):zs = train_data.apply(lambda x: x.mode().iloc[0] if len(x.mode())>0 else None)return test_data.fillna(zs)def mode_test_fill(...):# 逻辑同mean_test_fill,仅用mode_test_method替换mean_test_method...
核心逻辑:众数是最频繁出现的值,适合离散特征(如矿物的某分类属性:“有 / 无光泽”)。
注意:x.mode().iloc[0]
处理了多众数的情况(取第一个众数)。
9. 类别平衡处理:SMOTE 过采样
# smote拟合数据
from imblearn.over_sampling import SMOTE
oversampler = SMOTE(k_neighbors=1,random_state=42)
os_x_train, os_y_train = oversampler.fit_resample(x_train_fill,y_train_fill)
问题背景:矿物数据可能存在类别不平衡(如某些矿物样本极少),导致模型偏向多数类,少数类识别效果差。
SMOTE 过采样:通过生成少数类的合成样本(基于近邻样本插值),平衡各分类的样本数量。
参数说明:k_neighbors=1
:生成合成样本时参考 1 个近邻样本;
random_state=42
:固定随机种子,确保过采样结果可复现。
处理后变量:os_x_train
(平衡后的训练特征)、os_y_train
(平衡后的训练标签)。
10. 数据保存:输出预处理后数据集
# 将数据存入excel
data_train = pd.concat([os_y_train,os_x_train],axis=1).sample(frac=1,random_state=4) # 拼接并打乱训练集
data_test = pd.concat([y_test_fill,x_test_fill],axis=1) # 拼接测试集data_train.to_excel(r'.//temp_data//训练数据集[随机森林填充].xlsx',index=False)
data_test.to_excel(r'.//temp_data//测试数据集[随机森林填充].xlsx',index=False)
数据拼接:将标签列与特征列按列拼接(axis=1
),形成完整的训练集和测试集。
打乱训练集:sample(frac=1)
将训练集样本顺序打乱,避免模型受样本顺序影响。
保存文件:将预处理后的数据集保存到 temp_data
文件夹,文件名注明 “随机森林填充”(尽管当前未启用该方法,可能是历史遗留命名),index=False
表示不保存行索引。