假设我的数据集有很多缺失/无效值,如果它包含太多无效值,我想删除(或删除)整个变量(或列)。
采用以下示例,变量“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函数。
答案 0 :(得分:1)
我的理解是SAS进程在数据步骤编译期间丢弃语句,即在查看来自任何输入数据集的任何数据之前。因此,您不能使用这样的vname
函数来选择要删除的变量,因为在数据步骤完成编译并继续执行之前,它不会评估变量名称。
您需要输出包含所有变量的临时数据集或视图,包括您不想要的变量,在宏变量中构建要删除的变量列表,然后将其放入后续数据步骤。
请参阅本文和第3页,了解有关在编译期间而不是执行期间运行的内容的更多详细信息:
答案 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;