【问题标题】:Remove Variables that have too many invalid/missing values删除具有太多无效/缺失值的变量
【发布时间】:2015-10-20 15:56:25
【问题描述】:

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

以下面的例子为例,变量'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”作为错误值,还包含“.”、“Invalid 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 函数中使用变量名。相反,我希望它在一个循环中完成,我可以使用循环器“i”引用变量名。我尝试使用数组“myVars(i)”,但它似乎不适用于 drop 函数。

【问题讨论】:

  • Stack Overflow 不是代码生成服务。您应该尝试解决这个问题,并提出有关您的解决方案的问题 - 而不仅仅是要求解决一个重大问题。
  • 我同意乔的观点——你似乎已经对自己想要做什么有了一个相当清晰的想法,所以先自己动手吧。如果您在某个特定步骤上遇到困难,请务必发布您的代码并寻求帮助。
  • 现在我提供了更多详细信息和我正在处理的代码,请删除你的拇指,因为我不再要求代码生成服务,@Joe
  • 感谢您改进您的问题。
  • 改进很多。谢谢。

标签: sas


【解决方案1】:

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

您需要输出一个临时数据集或视图,其中包含所有变量(包括您不想要的变量),在宏变量中建立您要删除的变量列表,然后将它们删除到后续的数据步骤。

请参阅本文和第 3 页,详细了解哪些事情是在编译期间而不是执行期间运行的:

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

【讨论】:

  • 感谢您的 cmets。这是有道理的。我正在查看您附加的链接,我会返回我的发现:)
【解决方案2】:

一般来说,您会发现使用内置 procs 可以简化这类事情 - 这是 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;

【讨论】:

  • 谢谢乔,你肯定是专业人士。
  • 我第一次看到这个问题时就想到了 freq,但后来在不了解 substr、coalescec 等函数的情况下,努力从 freq 输出表中提取我需要的确切信息。一个快速提问,在代码的倒数第二行中,为什么要在 &droplist 的末尾添加一个小点?看来,如果我使百分比非常高,最终输出仍然会删除带有#N/A 的列。
  • 我遇到的一个问题是,如果数据集 has_missiong 为空,则 varname 为空,导致 droplist 无法解析。
  • 您可以在 proc sql 之前以%let droplist=; 开头,以确保它能够解析。
猜你喜欢
  • 1970-01-01
  • 2016-04-06
  • 2018-11-25
  • 2020-05-09
  • 2018-10-02
  • 1970-01-01
  • 2019-03-04
  • 1970-01-01
  • 2011-06-30
相关资源
最近更新 更多