1.缺失值处理
由于各种各样原因,现实中的许多数据集包含缺失数据,这样的数据是无法直接用于训练的,比如UCI数据集中的Adult数据集、Annealing数据集、Lung-Cancer数据集等都存在个别数据缺失,对此我们需要对缺失值进行处理。
处理缺失值的方法
处理缺失值的方法有很多并且没有统一的较好处理方式,对于缺失值的处理方法下图给出了较为详细的解答。最简单粗暴的方法就是把含有缺失值的样本丢弃,这样可以避免人为填充带来的噪声。但这样做可能会丢失一些很重要的信息,特别是数据量不多或者数据价值很高的数据来说,直接丢弃就太浪费了。因此我们可以旋转某种合适的策略对缺失值进行适当的填充。
一般来说,我们可以使用平均值、中值、分位数、众数等替代。 如果想要更好的填充效果,可以考虑利用无缺失值的数据建立模型,通过模型来选择一个最适合的填充值,但如果缺失的属性对于模型可有可无,那么得出来的填充值也将不准确。我们还可以使用KNN来选择最相似的样本进行填充。除此以外,缺失信息也可以作为一种特殊的特征表达,例如人的性别,男、女、不详可能各自有着不同的含义。
读取数据集文件
光这么说可能太宽泛,这里以一个简单的数据集:Lung-Cancer为例,从官网地址下载该数据集并将文件“lung-cancer.data”保存在自定义文件夹下,用MatlabMatlab打开文件部分数据截图如下图所示:
仔细观察上面的文本数据可以发现部分数据缺失,UCI数据集对于缺失数据用“?”,这是为了保持文本数据中的格式统一性。首先我们先去读取数据集中的属性和标签数据,在该目录下新建MatlabMatlab文件“read_dataset.m”并在编辑器中键入如下代码:
java % author:思绪无限, date:2020.2.29, website:https://wuxian.blog.csdn.net % lung-cancer clear; clc; data_name = 'lung-cancer'; fprintf('读取数据集: %s ...\n', data_name); n_entradas= 56; % 数据集属性个数 n_clases= 3; % 类别数 n_fich= 1; % 文件个数 fich{1}= 'lung-cancer.data'; % 文件路径 n_patrons(1)= 32; % 数据量 n_max= max(n_patrons); x = zeros(n_max, n_entradas); % 存储属性 cl= zeros(n_max, n_fich); % 存储标签 % 用于显示进度 n_patrons_total = sum(n_patrons); n_iter=0; for i_fich=1:n_fich f=fopen(fich{i_fich}, 'r'); % 打开文件 if -1==f error('打开文件出错 %s\n', fich{i_fich}); end for i=1:n_patrons(i_fich) % 显示进度 n_iter=n_iter+1; fprintf('%5.1f%%\r', 100*n_iter/n_patrons_total); temp = fscanf(f,'%i',1) - 1; % 读取数据标签(这里标签从0开始,与原标签相差1) cl(i, i_fich) = temp; % 读取每一列属性 for j = 1:n_entradas fscanf(f,'%c',1); % 跳过各属性间的逗号 t = fscanf(f,'%c',1); % 读取属性数值 if t ~= '?' % 判断是否为缺失数据 fseek(f,-1,0); x(i,j) = fscanf(f, '%i',1); else x(i,j) = NaN; % 缺失数据保存为空值 % x(i,j) = missing; % 缺失数据保存为空值(MATLAB R2019 可保存为missing) end end end fclose(f); end save('lung-cancer-raw.mat', 'x','cl') % 将读取出的数据保存
以上代码通过循环调用scanf( )函数逐个读入文本文件中的每个数据,外循环代码(第28-51行)遍历每行数据,内循环(第37-49行)遍历一行中每一个属性数据,通过移动文件指针跳过两个属性间的逗号(第38行),最终得到纯数值数据。其中代码第41-47行判断每个属性数据是否为“?”,由于Matlab中将缺失值保存为“NaN”(Matlab R2019a及以上还可保存为missing),这里加条件判断缺失数据赋值为NaN。代码最后将读取到的属性和标签数据(x, cl)保存在“lung-cancer-raw.mat”中,在命令行打印x的值部分截图如下:
查找、替换缺失数据
按照前面的介绍的方法这里使用各列属性的平均值替换缺失值,首先我们需要计算缺失数据所在属性的均值。这里可以先使用isnan( )指示缺失值位置进行查看,由于存在NaN数值,若直接使用Matlab内置函数mean( )计算将得到NaN的结果。常规思路是先找到NaN值的位置然后删去后计算均值,但其实许多 Matlab函数都可以忽略缺失值,我们不必首先显式定位、填充或删除它们。我们可以结合使用mean函数和'omitnan'选项来直接忽略和中的NaN,例如如下的代码和结果。
powershell >> xDouble=[0 1 1 3 nan 2] xDouble = 0 1 1 3 NaN 2 >> meanNan = mean(xDouble, 'omitnan') meanNan = 1.4000
对于缺失数据的填充MatlabMatlab有专门的填充函数fillmissing函数,使用方法如下引用,更多功能可以访问fillmissing官方文档。这里我们使用F = fillmissing(A,'constant',v)为缺失值填充常量,常量v的值为上面计算得到的均值。需要注意的是,Lung-Cancer数据集的每列属性均为0-3的整数,为保持数据类型的统一性,这里先对计算得到的均值向下取值然后替换缺失值。
fillmissing
填充缺失值
语法
F = fillmissing(A,'constant',v)
F = fillmissing(A,method)
F = fillmissing(A,movmethod,window)
说明
F = fillmissing(A,'constant',v) 使用常量值 v 填充缺失的数组或表条目。
F = fillmissing(A,method) 使用 method 指定的方法填充缺失的条目。
F = fillmissing(A,movmethod,window) 使用窗口长度为 window 的移动窗口均值或中位数填充缺失条目。
——MATLAB官方文档
根据上面的分析编写程序使用均值填充缺失值,在该目录下新建MatlabMatlab文件“replace_missing.m”并在编辑器中键入如下代码:
java % 读取原始数据,采用均值填充 clear; clc load('lung-cancer-raw.mat','x','cl'); % 导入数据 nanData = x; TF = isnan(nanData); % 缺失数据位置 meanData = mean(nanData, 'omitnan'); % 计算每列上数据的均值(忽略NaN) intMean = floor(meanData); % 由于lung-cancer数据的属性均为0-3的整数,这里对向下取值 x = fillmissing(nanData,'constant',intMean); % 填充缺失数据为均值 % x = fillmissing(nanData,'constant',0); % 也可填充缺失数据为0 save('lung-cancer-fill.mat', 'x', 'cl'); % 保存填充后的数据
以上代码最终将填充后的结果保存在文件“lung-cancer-fill.mat”中,再次输出x的值可以看到NaN的位置数据已替换为该列属性的均值,在命令行打印x的值部分截图如下。如此缺失值填充完成,当然处理方式并无最佳的统一方法,比较简单的方式还可以直接补0,如上代码第13行。
处理完成后工作区的情况如下图:
2. 代码资源获取
这里对博文中涉及的数据及代码文件做一个整理,本文涉及的代码文件如下图所示。所有代码均在MatlabMatlabR2016bR2016b中调试通过,点击即可运行。
后续博文会继续分享数据处理系列的代码,敬请关注博主的机器学习数据处理分类专栏。
【资源获取】
若您想获得博文中介绍的填充缺失数据涉及的完整程序文件(包含数据集的原始文件、整理数据集程序代码文件及整理好的文件),可点击以下卡片“AI技术研究与分享”,并回复“DP20200301”(建议复制红色字体)获取,后续系列的代码也会陆续分享打包在里面。