一、场景
缺失值在数据管理和研究助理等各方通力合作之下很快得以完美解决(不存在的,good luck)。兴奋地准备搓搓手开始做统计了吗,又想一天出图表,两天写报告......
无效值(Invalid data)也是数据清洗路上的常见障碍。
用数值型变量和字符型变量分别举两个例子:
- 在数值型变量中如10分的数字评定量表(NRS)作为临床常用的一种指标通常用于疼痛症状的评分,其取值范围在0-10之间,如果在数据集中出现了NRS <0或NRS >10的情况则该数据就是NRS变量中的无效值;
- 在字符型变量中如性别Sex通常以Female (F) /Male (M)来表示,如果在数据集中出现了除F, M以及空值(缺失数据)之外的情况,例如Sex=“X”则为无效值。
二、数据
在之前的“缺失值识别和定位的SAS实现”中最后提到了一个数据集Patients,这个数据来自于Ron Cody的《Cody's Data Cleaning Techniques Using SAS》第二版[1]。推荐有兴趣了解更多有关使用SAS进行数据清洗操作的同好去阅读,尽管对于programming skills来说,get your hands dirty很重要,但也须知开卷有益。
以下为数据集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;
数据集包含8个变量,分别是:
- 字符型变量 “病例编号” Patno(patient number),长度3;
- 字符型变量 “性别” Gender(gender),长度1,取值范围为M/F;
- 数值型变量 “访视日期” Visit(visit date),长度8,格式为MMDDYY10.;
- 数值型变量 “心率” HR(heart rate),长度8,取值范围为60-100
- 数值型变量 “收缩压” SBP(systolic blood pressure),长度8,取值范围80-200
- 数值型变量 “舒张压” DBP(diastolic blood pressure),长度8,取值范围60-120
- 字符型变量 “诊断码” Dx(diagnosis code),长度3,取值范围1-10
- 字符型变量 “不良事件” AE(adverse event),长度1,取值范围0/1
几乎所有变量中都存在无效值,比如变量gender中本应除了M/F或缺失数据的空值外不应该再有第四种情况,但是仔细看到观测3的gender=X,观测11的gender=f,而这两个值既不属于M也不属于F,即为无效值;再比如变量HR中,观测22的HR=900,远远超出了正常的范围60-100。如果这个数据的观测不是已有的31条,而是310条或更多,那么肉眼查找无效值无异于大海捞针,既低效更无法保证结果的准确性。
三、同时识别并定位多个数值型变量中无效值的Macro:ERRORS
macro ERRORS可以在调用后同时查找并根据identifier定位多个数值型变量的无效值(这里的identifier是病例编号即Patno)。需要注意的是ERRORS需要指定若干数值型变量的取值范围,这里的范围需要与临床研究者讨论确定(比如变量HR,尽管在现实中可能心率超过100并非罕见,但需要根据研究目定义HR的取值范围,在本数据中其取值范围为60-100)
%macro errors(Var=, /* Variable to test */
Low=, /* Low value */
High=, /* High value */
Missing=ignore
/* How to treat missing values */
/* Ignore is the default. To flag */
/* missing values as errors set */
/* Missing=error */);
data tmp;
set &dsn(keep=&Idvar &Var);
length Reason $ 10 Variable $ 32;
Variable = "&Var";
Value = &Var;
if &Var lt &Low and not missing(&Var) then do;
Reason='Low';
output;
end;
%if %upcase(&Missing) ne IGNORE %then %do;
else if missing(&Var) then do;
Reason='Missing';
output;
end;
%end;
else if &Var gt &High then do;
Reason='High';
output;
end;
drop &Var;
run;
proc append base=errors data=tmp;
run;
%mend errors;
***Error Reporting Macro - to be run after ERRORS has been called
as many times as desired for each numeric variable to be tested;
%macro report;
proc sort data=errors;
by &Idvar;
run;
proc print data=errors;
title "Error Report for Data Set &Dsn";
id &Idvar;
var Variable Value Reason;
run;
proc datasets library=work nolist;
delete errors;
delete tmp;
run;
quit;
%mend report;
***Calling the ERRORS macro;
***Set two macro variables;
%let dsn=clean.patients;
%let Idvar = Patno;
%errors(Var=HR, Low=40, High=100, Missing=error)
%errors(Var=SBP, Low=80, High=200, Missing=ignore)
%errors(Var=DBP, Low=60, High=120)
***Generate the report;
%report
同时查找三个数值型变量“心率”HR,“收缩压”SBP和“舒张压”DBP的无效值并通过“病例编号”Patno进行定位,设置HR的范围为40-100,SBP的范围为80-200,DBP的范围为60-120,且对变量HR的缺失值也进行识别并进行报告和定位,但对SBP和DBP的缺失值进行忽视(该macro默认选项missing=ignore)如需要报告缺失值则需要增加选项missing=error)。
运行结果如下:
共识别出20观测,其中包含17条无效值和3条缺失值,并定位具体的病例编号。
四、通过Proc print同时识别并定位多个字符型变量中无效值
title "Listing of invalid character values";
proc print data=clean.patients;
where Gender not in ('M' 'F' ' ') or
notdigit(trim(Dx)) or missing(Dx) or /*删除Dx右侧空格符并查找缺失值*/
AE not in ('0' '1' ' ');
id Patno;
var Gender Dx AE;
run;
对于多个字符型变量无效值的识别和定位可通过proc print语句在过程步中完成,该过程同时也可以选择识别/忽视缺失值(通过增加逻辑符or/and missing(var))。
运行结果如下:
五、后记
无论是应用于数值型变量的宏程序ERRORS还是应用于字符型变量的过程步proc print都可以在识别无效值的同时识别缺失值。建模和统计分析固然重要,但如果缺少了完整可靠的数据,即使采用了复杂的模型也会得到存在偏倚的结果。
一道好菜不仅仅只靠高超的厨艺技巧,幾好嘅師傅都要靚食材。
参考资料
- Ron Cody,Cody's data cleaning techniques using SAS, second edition