删除包含太多无效/缺失值的变量

时间:2015-10-20 15:56:25

标签: sas

假设我的数据集有很多缺失/无效值,如果它包含太多无效值,我想删除(或删除)整个变量(或列)。

采用以下示例,变量“gender”具有相当多的“#N / A”。如果某个百分比的数据点位于“#N / A”,超过50%,超过30%,我想删除该变量。

此外,我想使百分比成为可配置值,即如果超过该变量下x%的观察结果为“#N / A”,我愿意删除整个变量。而且我还希望能够定义无效值是什么,可能是“#N / A”,可能是“无效值”,可能是“”,可能是我预先定义的任何其他内容。

data dat;
  input id score gender $;
  cards;
  1 10 1
  1 10 1
  1 9 #N/A
  1 9 #N/A
  1 9 #N/A
  1 8 #N/A
  2 9 #N/A
  2 8 #N/A
  2 9 #N/A
  2 9 2
  2 10 2
  ;
run;

请尽可能推广解决方案。例如,如果真实数据集包含数千个变量,我需要能够遍历所有这些变量,而不是逐个引用它们的变量名。此外,数据集可能包含的不仅仅是“#N / A”作为错误值,还有“。”,“无效的Obs”,“N.A”等其他内容。也可以同时存在。

PS:实际上我想到了一种让这个问题更容易的方法。我们可能会将所有数据点读入数值,以便所有“#N / A”,“N.A。”,“”内容变为“。”,这使得丢弃标准更容易。希望能帮助你解决这个问题...

更新:下面是我正在处理的代码。卡在最后一个街区。

data dat;
  input id $ score $ gender $;
  cards;
  1 10 1
  1 10 1
  1 9 #N/A
  1 9 #N/A
  1 9 #N/A
  1 8 #N/A
  2 9 #N/A
  2 8 #N/A
  2 9 #N/A
  2 9 2
  2 10 2
  ;
run;

proc contents data=dat out=test0(keep=name type) noprint; 

/*A DATA step is used to subset the test0 data set to keep only the character */
/*variables and exclude the one ID character variable.  A new list of numeric*/ 
/*variable names is created from the character variable name with a "_n"     */
/*appended to the end of each name.                                          */                                                        

data test0;                                                
set test0;                                                 
if type=2;                  
newname=trim(left(name))||"_n";                                                                               

/*The macro system option SYMBOLGEN is set to be able to see what the macro*/
/*variables resolved to in the SAS log.                                    */                                                       

options symbolgen;                                        

/*PROC SQL is used to create three macro variables with the INTO clause.  One  */
/*macro variable named c_list will contain a list of each character variable   */
/*separated by a blank space.  The next macro variable named n_list will       */
/*contain a list of each new numeric variable separated by a blank space.  The */
/*last macro variable named renam_list will contain a list of each new numeric */
/*variable and each character variable separated by an equal sign to be used on*/ 
/*the RENAME statement.                                                        */                                                        

proc sql noprint;                                         
select trim(left(name)), trim(left(newname)),             
       trim(left(newname))||'='||trim(left(name))         
into :c_list separated by ' ', :n_list separated by ' ',  
     :renam_list separated by ' '                         
from test0;
quit;                                                                                                               


/*The DATA step is used to convert the numeric values to character.  An ARRAY  */
/*statement is used for the list of character variables and another ARRAY for  */
/*the list of numeric variables.  A DO loop is used to process each variable   */
/*to convert the value from character to numeric with the INPUT function.  The */
/*DROP statement is used to prevent the character variables from being written */
/*to the output data set, and the RENAME statement is used to rename the new   */
/*numeric variable names back to the original character variable names.        */                                                        

data test2;                                               
set dat;                                                 
array ch(*) $ &c_list;                                    
array nu(*) &n_list;                                      
do i = 1 to dim(ch);                                      
  nu(i)=input(ch(i),8.);                                  
end;                                                      
drop i &c_list;                                           
rename &renam_list;                                                                                      
run; 




data test3;                                               
set test2;                                                 
array myVars(*) &c_list;                               
countTotal=1;
do i = 1 to dim(myVars);
    myCounter = count(.,myVars(i));
/*  if sum(countMissing)/sum(countTotal) lt 0.5 then drop VNAME(myVars(i)); */
end;

run;

我遇到的问题是,我无法删除我想要删除的变量。原因是因为我不想在drop函数中使用变量名。相反,我希望它在一个循环中完成,我可以用looper“i”引用变量名。我试图使用数组“myVars(i)”但它似乎不适用于drop函数。

2 个答案:

答案 0 :(得分:1)

我的理解是SAS进程在数据步骤编译期间丢弃语句,即在查看来自任何输入数据集的任何数据之前。因此,您不能使用这样的vname函数来选择要删除的变量,因为在数据步骤完成编译并继续执行之前,它不会评估变量名称。

您需要输出包含所有变量的临时数据集或视图,包括您不想要的变量,在宏变量中构建要删除的变量列表,然后将其放入后续数据步骤。

请参阅本文和第3页,了解有关在编译期间而不是执行期间运行的内容的更多详细信息:

http://www.lexjansen.com/nesug/nesug11/ds/ds04.pdf

答案 1 :(得分:0)

一般情况下,您会发现使用内置过程简化了这种事情 - 这是SAS的面包和黄油。你只需要重述一下这个问题。

你想要的是丢弃变量,其中%的丢失/坏数据高于50%,所以你需要一个变量频率表,对吗?

所以 - 使用PROC FREQ。这是简化版本(仅查找" #N / A"),但应该很容易修改最后一步以使其查找其他值(并总结它们的百分比)。或者,就像您在链接问题(从我对问题的评论)中看到的那样,您可以使用一种特殊格式,将所有无效值放入一个格式化值,将所有有效值放入另一个格式化值。 (您必须构建此格式。)

概念:使用PROC FREQ获取频率表,然后查看该数据集以查找带有>的行。 50%的行和F_列中的无效值。

这不会与实际失踪一起工作(""或。);如果你有这些选项,你需要将/MISSING选项添加到PROC FREQ。

data dat;
  input id $ score $ gender $;
  cards;
  1 10 1
  1 10 1
  1 9 #N/A
  1 9 #N/A
  1 9 #N/A
  1 8 #N/A
  2 9 #N/A
  2 8 #N/A
  2 9 #N/A
  2 9 2
  2 10 2
  ;
run;

*shut off ODS for the moment, and only use ODS OUTPUT, so we do not get a mess in our results window;
ods exclude all;
ods output onewayfreqs=freq_tables;
proc freq data=dat;
  tables id score gender;
run;
ods output close;
ods exclude none;

*now we check for variables that match our criteria;    
data has_missing;
  set freq_tables;
  if coalescec(of f_:) ='#N/A' and percent>50;
  varname = substr(table,7);
run;

*now we put those into a macro variable to drop;
proc sql;
  select varname 
    into :droplist separated by ' '
    from has_missing;
quit;

*and we drop them;
data dat_fixed;
  set dat;
  drop &droplist.;
run;