![9823a224cdacdff4af5b6dc4a3f378f2.png](https://i-blog.csdnimg.cn/blog_migrate/849b3182cf2c2d06a3a50c10a12a5430.png)
一、场景
在经历了长达数十月甚至数年之久的临床研究终于完成了最后一例受试者随访资料的搜集,希望的曙光就在眼前,仿佛看见了下面这种整整齐齐的数据在向你招手,似乎可以一天出图表,两天写报告......
![f248d3bcb7faf86c2c980c3b0fe1a7f3.png](https://i-blog.csdnimg.cn/blog_migrate/9a03691007ff57014ed479044d5dd6bb.jpeg)
然而现实中的数据可能是下面这样的
![b0e06a6b9fa439cb870780beb936d5b9.png](https://i-blog.csdnimg.cn/blog_migrate/d72e33fa561d1e4900ae0c6937ab6488.png)
查找缺失值的过程是很重要的,有些与主要研究目的不相关的数据缺失(如电话号码、通讯地址、邮政编码等)可能并不重要,但如果是重要资料(如解释变量或被解释变量的缺失),无论是否后期继续对其进行方法学处理(如末次结转或其他各种结转方法、线性插值、多重填补等),首要的一步是先查找是否存在缺失值,如果有,这些缺失值它们又定位在哪里?
二、查找是否存在缺失值
为了方便我们直接用SAS自带的数据集class(图1),并描述数据集class的内容
/*利用SAS自带的数据集sashelp.class*/
data class;
set sashelp.class;
run;
/*描述数据集class的内容*/
proc contents data=class;
run;
![a0591e94dfe52eba2f351d3c0b142388.png](https://i-blog.csdnimg.cn/blog_migrate/b16830fa6b0670b0ddce530e6e41b506.jpeg)
利用proc means和proc freq语句分别查找所有数值变量和字符变量的缺失值
title "Missing value check for the patients data set";/*输出结果增加标题*/
proc means data=class n nmiss;/*也可增加var语句选择感兴趣的变量*/
run;
proc format;
value $misscnt ' ' = 'Missing'
other = 'Nonmissing';
run;
proc freq data=class;
tables _character_ / nocum missing;/*table语句后也可选择感兴趣的变量*/
format _character_ $misscnt.;
run;
![2bd2307658a912fba674aeb3e368649b.png](https://i-blog.csdnimg.cn/blog_migrate/b3406fb50a94ff80823dc5d7f6ba7a8f.png)
在数据集class的基础上通过调用符合正态分布的随机数创建含有缺失数据的数据集classmis
data classmis;
set class;
if rannor(20200718) <0 then call missing(sex);
if rannor(20200718) <0 then call missing(age);
if rannor(20200718) <0 then call missing(height);
if rannor(20200718) <0 then call missing(weight);
run;
结果如图2所示,然后再次调用刚才的proc means和proc freq看看结果如何
title "Missing value check for the patients data set";
proc means data=classmis n nmiss;
run;
proc freq data=classmis;
tables _character_ / nocum missing;
format _character_ $misscnt.;
run;
![56f51c903420ef638933701e5fa55041.png](https://i-blog.csdnimg.cn/blog_migrate/5d568a07e5b08cb0cdf17bd4cd833cc2.jpeg)
proc means的结果显示在数值变量中,age有11个缺失值,height有12个缺失值,weight有9个缺失值;proc freq的结果显示在字符变量中,name没有缺失值,sex有8个缺失值。
上面通过proc means和proc freq过程的目的是回答“是否存在?”以及“存在多少?”缺失值这两个问题,这个过程可以定性(是否存在)以及定量(存在多少)。这个问题的回答有以下两种情况:
- 如果不存在缺失值,则查找缺失值的过程结束(不要高兴太早,还有异常值等其他可能的坑等着填);
- 如果存在缺失值,则视数据集规模而定,如果像上面的class例子只有19个观测,则完全可以肉眼定位。但是通常情况,我们还是当自己是盲人吧,需要定位这些缺失值在哪里。
三、定位缺失值
继续使用前面的数据集classmis,确定缺失值的位置。
nunndata _null_;
set classmis end=last;
file print;
if missing(sex) then do;
put "Missing sex for ID " name;
N_sex + 1;
end;
if missing(age) then do;
put "Missing age for ID " name;
N_age + 1;
end;
if missing(Height) then do;
put "Missing height for ID " name;
N_Height + 1;
end;
if missing(Weight) then do;
put "Missing weight for ID " name;
N_Weight + 1;
end;
if last then
put // "Summary of missing values" /
25*'-' /
"Number of missing sex = " N_sex /
"Number of missing age = " N_age /
"Number of missing height = " N_Height /
"Number of missing weight = " N_Weight;
run;
![17d2a28c23a93e2292e3ba589d872dff.png](https://i-blog.csdnimg.cn/blog_migrate/83ebecc557c6512ee3928351363b6177.jpeg)
![1ed3a135c959dbaaf9e35dfc2775acc9.png](https://i-blog.csdnimg.cn/blog_migrate/1d7fd42282c13552fcfb17e1d22b72e9.jpeg)
![3928cb8128aa24984aa96b40767750ee.png](https://i-blog.csdnimg.cn/blog_migrate/946e8c73a08f2b11543a1607eda2f212.jpeg)
四、利用数组识别并定位多个变量的缺失值
上面的代码尽管可以详细的查找是否存在缺失值以及定位缺失数据,但是在实际应用中更推荐以下由array数组和do循环构成的方法,由于采用了SAS特殊名称列表_numeric_和_character_所以无需修改,使用时将数据集名替换即可。以数据集patients为例,执行后将会在数据集中创建新变量"mvarlist",列出每条观测的缺失值清单,以便数据管理人员核查确认。
![bcbfcd586aea7b9e4fa84303b9be230a.png](https://i-blog.csdnimg.cn/blog_migrate/caf4863acc8c1cf9af8ba909571c6b06.jpeg)
data patients;
set clean.patients;
length mvarlist $ 300;
array num{*} _numeric_;
array char{*} _character_;
do n=1 to dim(num);
if missing(num{n}) then
mvarlist=catx("," ,mvarlist,vname(num{n}));
end;
do c=1 to dim(char);
if missing(char{c}) then mvarlist=catx(",", mvarlist,vname(char{c}));
end;
run;
如果在数据集patients中体验以上的数组代码可以参考以下附件:
以下为数据集patients的内容,复制粘贴虚线内数据至桌面创建txt文档,命名为patients。
---------------------------
001M11/11/1998 88140 80 10
002F11/13/1998 84120 78 X0
003X10/21/1998 68190100 31
004F01/01/1999101200120 5A
XX5M05/07/1998 68120 80 10
006 06/15/1999 72102 68 61
007M08/32/1998 88148102 0
M11/11/1998 90190100 0
008F08/08/1998210 70
009M09/25/1999 86240180 41
010f10/19/1999 40120 10
011M13/13/1998 68300 20 41
012M10/12/98 60122 74 0
013208/23/1999 74108 64 1
014M02/02/1999 22130 90 1
002F11/13/1998 84120 78 X0
003M11/12/1999 58112 74 0
015F 82148 88 31
017F04/05/1999208 84 20
019M06/07/1999 58118 70 0
123M15/12/1999 60 10
321F 900400200 51
020F99/99/9999 10 20 8 0
022M10/10/1999 48114 82 21
023f12/31/1998 22 34 78 0
024F11/09/199876 120 80 10
025M01/01/1999 74102 68 51
027FNOTAVAIL NA 166106 70
028F03/28/1998 66150 90 30
029M05/15/1998 41
006F07/07/1999 82148 84 10
------------END------------
/*创建永久数据库clean,并从桌面将数据集patients导入其中*/
libname clean 'C:Users123Desktopclean';
data clean.patients;
infile 'C:Users123Desktoppatients.txt'
lrecl=30 truncover;
input @1 Patno $3. @4 gender $1.
@5 Visit mmddyy10.
@15 HR 3.
@18 SBP 3.
@21 DBP 3.
@24 Dx $3.
@27 AE $1.;
LABEL Patno = "Patient Number"
Gender = "Gender"
Visit = "Visit Date"
HR = "Heart Rate"
SBP = "Systolic Blood Pressure"
DBP = "Diastolic Blood Pressure"
Dx = "Diagnosis Code"
AE = "Adverse Event?";
format visit mmddyy10.;
run;
参考资料
- Ron Cody,Cody's data cleaning techniques using SAS, second edition
- 谷鸿秋,SAS编程演义