SAS编程-宏:查询数据集中所有空变量

之前写过查询数据集中的空变量的宏程序,一时没找到。于是重写一版,并以此简单介绍下宏程序的构建过程。

这篇文章从功能算法讲起,然后编程实现算法逻辑,最后进行宏程序的构建,宏程序完整代码在文章第4部分汇总。

希望这篇文章可以对读者日常SAS编程工作有所帮助。

先展示宏程序输出的效果:

更多临床试验SAS编程内容,欢迎关注:SAS茶谈。

1. 程序算法设计

宏程序的构思设计,从最小功能单位开始。对于查询数据集中的空变量,我们从单个数据集的单个变量的判断做起。

演示数据集使用SASHELP.Class,进行新增空变量处理。

***test dataset;
data class;
  set sashelp.class;
  a = "";
  b = .;
run;

目前数据集中有两个完全为空的变量,宏程序的目的就是把这两个变量找出来。用什么程序语言来表示空变量,这个需要程序员自己摸索和尝试。我选用的是,变量不为空的记录数为0。听起来有些拗口,但程序实现起来比较简单。

2. 编程实现算法

Proc SQL和Data步都能够实现非空记录数的统计,但因为SQL的聚合函数跨记录处理相对方便,我以SQL语句进行演示,先统计变量Name不为空的记录数:

***Get number of non-missing records;
proc sql noprint;
  create table result1 as
    select "CLASS" as Dataset length=50, "NAME" as Var length=50, sum(not missing(name)) as Sum
    from class;
quit;

依次类推,我们可以统计出每个变量不为空的记录数,然后依次将各个结果纵向拼接在一起。

SQL中纵向拼接的查询表达是outer union,默认是按两个查询表依次位置拼接的,相同变量拼接需加上关键字corresponding/corr,语法细节参考SAS官方文档:SAS Help Center: query Expression

***Get number of non-missing records for all variables;
proc sql noprint;
  create table result1 as
    select "CLASS" as Dataset length=50, "NAME" as Var length=50, sum(not missing(NAME)) as Sum
    from class

    outer union corr
    select "CLASS" as Dataset length=50, "SEX" as Var length=50, sum(not missing(SEX)) as Sum
    from class

    outer union corr
    select "CLASS" as Dataset length=50, "AGE" as Var length=50, sum(not missing(AGE)) as Sum
    from class

    outer union corr
    select "CLASS" as Dataset length=50, "HEIGHT" as Var length=50, sum(not missing(HEIGHT)) as Sum
    from class

    outer union corr
    select "CLASS" as Dataset length=50, "WEIGHT" as Var length=50, sum(not missing(WEIGHT)) as Sum
    from class

    outer union corr
    select "CLASS" as Dataset length=50, "A" as Var length=50, sum(not missing(A)) as Sum
    from class

    outer union corr
    select "CLASS" as Dataset length=50, "B" as Var length=50, sum(not missing(B)) as Sum
    from class;
quit;

再获取数据集中所有变量不为空的记录数后,我们只需要筛选记录数为0的记录就可以获取空变量的信息。为展示方便,我们可以把两个变量信息综合在一起,这个可以通过转置后横向拼接实现。

***Display result;
proc transpose data = result1 out=result2  prefix=emp_;
  by dataset;
  var Var;
  where sum = 0;
run;

data result3;
  set result2;

  length Empvar $2000;
  Empvar = catx(", ", of emp_:);
  keep dataset empvar;
run;
result2
result3

这样处理看起来比较简洁,也方便后续多数据集检查空变量结果的拼接。

关于程序,多解释一点,SAS中函数批量处理变量序列时,需要在变量序列前添加of。如果不批量处理,也可以手动输入每一变量名称,例如:

 empvar = catx(", ", emp_1, emp_2);

如果不知道输出变量的数目,使用特定的前缀对变量进行标记,再用函数批量处理,这个过程会变得简洁许多。

3. 宏程序的构建

在手动编程将算法实现后,就可以着手构建宏程序。就我个人SAS编程经验来讲,宏程序的作用主要有两个:

  • 单个功能的重复调用;
  • 宏循环的批量处理。

关于这两个作用,读者可以与自己的宏程序编程经历对照理解,这里就不过多展开。

从以上手动编程的过程中可以看出,程序主要“重复”的地方在于各个变量不为空记录的统计。除了变量名称,拼接程序完全相同。如果我们将需要处理的变量名称保存到宏变量序列中,就可以通过宏循环依次进行调用,并通过宏循环批量构建程序。

3.1 生成宏变量(序列)

宏循环处理的关键,在于循环次数的获取以及变量名称宏变量序列。

宏变量的生成常用有2种方法:

  1. Proc SQL 中的into :语句
  2. Data步中的call symputx语句

SAS数据集的元数据信息保存在SAS字典中,这里我以Data步中的call symputx语句进行举例。

变量数目保存到宏变量中:

***Get the number of vairiables;
data tmp1;
  set sashelp.vtable;
  where libname = "WORK" and memname = "CLASS";
  call symputx("nvar", strip(put(nvar,best.)));
run;

%put nvar= &nvar.;

变量名称保存到宏变量序列中:

***Get variables' name;
data tmp2;
  set sashelp.vcolumn;
  where libname = "WORK" and memname = "CLASS";
  call symputx("var"||strip(put(varnum, best.)), strip(name));
run;

%put var1= &var1.;
%put var7= &var7.;

这里一些读者可能有这样的想法:这里完全可以使用一个Data步,从SASHELP.vcolumn数据集获取最后一条数据的varnum作为宏变量nvar的取值。类似这样的处理:

***Get variables' name and nvar;
data tmp3;
  set sashelp.vcolumn end=eof;
  where libname = "WORK" and memname = "CLASS";
  call symputx("var"||strip(put(varnum, best.)), strip(name));
  if eof then call symputx("nvar1", strip(put(varnum, best.)));

  %put nvar1= &nvar1.;
run;

以当前演示数据集来看,这两种方式处理结果相同。但如果一个数据集中没有任何变量,这时候SASHELP.vcolumn中是没有记录的,而SASHELP.vtable中是有nvar=0的记录。此时后者无法抓取数据集的变量数。

如果感兴趣,读者可以用以下空数据集进行测试:

data test;
run;

3.2 宏循环的实现

宏循环需要在宏程序中进行,宏程序的构建尽可能包含可能的情形。这里根据变量数目进行分类处理。

***Temp macro;
%macro check_empty_var;
  
%if &nvar. = 0 %then %do;
  data result;
    length Dataset $50 empvar $2000;
    dataset = "CLASS";
    empvar = "There is no variable in the dataset Class!";
  run;
%end;

%else %if &nvar. > 0 %then %do;
  %if &nvar. = 1 %then %do;
    proc sql noprint;
       create table result1 as
         select "CLASS" as domain length=50, "&var1." as Var length=50, sum(not missing(&var1.)) as Sum
         from class
      ;
    quit;
  %end;

  %if &nvar. > 1 %then %do;
    proc sql noprint;
       create table result1 as
         select "CLASS" as Dataset length=50, "&var1." as Var length=50, sum(not missing(&var1.)) as Sum
         from class

    %do i = 2 %to &nvar.;
      outer union corr
      select "CLASS" as Dataset length=50, "&&var&i." as Var length=50, sum(not missing(&&var&i.)) as Sum
        from class
    %end;
      ;
    quit;
  %end;

  *Display result;
  proc transpose data = result1 out=result2 prefix=emp_;
    by dataset;
    var Var;
    where sum = 0;
   run;
  
  data result;
    set result2;
    
    length Empvar $2000;
    Empvar = catx(", ", of emp_:);
    keep dataset empvar;
  run;
   
%end;

%mend check_empty_var;

%check_empty_var;

以上宏程序运行结果如下,与手动编程结果保持一致。

3.3 宏参数的设置

宏参数一般有3类:

  1. 输入内容(变量/数据集)
  2. 输出内容(变量/数据集)
  3. 特定条件

这个宏程序从简,直接以Reslt数据集输出,不需要额外的筛选条件,只需设置输入数据集就好。为了省事,也不在宏里判断输入数据集的逻辑库名称、数据集名称,直接定义到宏参数中。

%macro check_empty_var(libname=WORK, memname=);

...

%mend check_empty_var;

确定宏参数后,需要在之前初步宏程序中进行内容替换,这样方便以后对不同参数对象的处理调用。

4. 宏程序代码汇总

完整宏程序需综合以上内容,并尽可能考虑多种可能情况,以求宏程序运行稳定。例如,输入数据集不存在的情况;数据集没有空变量的情况。

如果输入数据集不存在,最好能在Log中输出一条Warning记录作为提醒。为避免一些程序文本检查机制的误判,War ning最好能拆开处理下。

其他处理细节就不再展开描述,汇总程序如下:

%macro  check_empty_var(libname=WORK, memname=);
  
***Dataset exists;
%if %sysfunc(exist(&libname..&memname.)) = 1 %then %do;

***Get the number of vairiables;
data _null_;
  set sashelp.vtable;
  where libname = upcase("&libname.") and memname = upcase("&memname.");
  call symputx("nvar", strip(put(nvar, best.)));
run;

%put nvar= &nvar.;

***Macro loop;
%if &nvar. = 0 %then %do;
  data result;
    length Dataset $50 Empvar $2000;
    dataset = upcase("&libname..&memname.");
    Empvar= "There is no variable in the dataset %sysfunc(upcase(&libname..&memname.)).";
  run;
%end;

%else %if &nvar. > 0 %then %do;

**Get variables name;
data _null_;
  set sashelp.vcolumn;
  where libname = upcase("&libname.") and memname = upcase("&memname.");
  call symputx("var"||strip(put(varnum, best.)), strip(name));
run;

%put var1= &var1.;

  %if &nvar. = 1 %then %do;
    proc sql noprint;
       create table result1 as
         select upcase("&libname..&memname.") as Dataset length=50, "&var1." as Var length=50, sum(not missing(&var1.)) as Sum
         from &libname..&memname.
      ;
    quit;
  %end;

  %if &nvar. > 1 %then %do;
    proc sql noprint;
       create table result1 as
         select upcase("&libname..&memname.") as Dataset length=50, "&var1." as Var length=50, sum(not missing(&var1.)) as Sum
         from &libname..&memname.

    %do i = 2 %to &nvar.;
      outer union corr
      select upcase("&libname..&memname.") as Dataset length=50, "&&var&i." as Var length=50, sum(not missing(&&var&i.)) as Sum
        from &libname..&memname.
    %end;
      ;
    quit;
  %end;

  *Display result;
  proc transpose data = result1 out=result2 prefix=emp_;
    by dataset;
    var Var;
    where sum = 0;
   run;
  
  data result;
    set result2;
    
    length emp_1 $50 Empvar$2000;
    if not missing(emp_1) then empvar = catx(", ", of emp_:);
    else do;
      dataset = upcase("&libname..&memname.");
      empvar = "There is no empty variable in the dataset %sysfunc(upcase(&libname..&memname.)).";
    end;
    keep dataset empvar;
  run;
   
%end;

%end;

***Dataset does not exist;
%if %sysfunc(exist(&libname..&memname.)) ne 1 %then %do;
  %put %sysfunc(compress(War ning)): Dataset %sysfunc(upcase(&libname..&memname.)) does not exist. ;
%end;

%mend check_empty_var;

%check_empty_var(libname=work, memname=class);
%check_empty_var(libname=work, memname=Yeteng);

如果数据集不存在,最后显示结果如下:

5. 总结

文章梳理了查询数据集中所有空变量宏程序构建过程,希望能够对读者有所帮助。单个数据集处理的完成,也方便后续对多个数据集批量处理

感谢阅读, 欢迎关注:SAS茶谈!
若有疑问,欢迎评论交流!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容