SAS编程:如何将数据集批量输出到Excel?

有时为了解项目数据结构,需要查看不少原始数据。简单的记录筛选,SAS操作起来不复杂,但是对于大批量数据记录的查看,SAS筛选就显得繁琐。这种情况下,将数据集批量输出到Excel中进行查看,不失为一个很好的方法。

数据集批量输出的要达到什么效果呢?出于筛选方便的考虑,所有数据集保存在一个Excel文件中,数据集名称作为对应的Sheet名称,每个表单冻结首行,并且直接设置首行筛选

这样打开Excel文件,就可以不需要额外设置,可以直接浏览、筛选数据。

在SAS编程中,常见的批量处理的方法有两种,一是宏程序中的宏循环,二是call execute语句

下面介绍整个输出的实现过程,先从单个数据集输出开始。

1. 单个数据集的输出

SAS程序参考之前的文章SAS编程:输出SAS数据集(Listing)到EXCEL。通常,使用export过程步,进行简单的输出;使用ods excel语句以及report过程步,进行复杂的输出设置,例如,冻结标题、首行自动筛选等

ods excel file = "E:\999_test.xlsx" 
     options(sheet_name="Class"  frozen_headers="Yes" autofilter = "Yes");

proc report data = sashelp.class;
 column _all_;
run;

ods excel close;

输出结果如下:

单个数据集实现了对应格式的输出。

2. 两个数据集的输出

还是参考之前的文章,第二个ods excel语句输出不添加file=语句,这样两个数据集就输出到同一个Excel文件的不同sheet中。

ods excel file = "E:\999_test\test.xlsx" 
     options(sheet_name="Class"  frozen_headers="Yes" autofilter = "Yes");

proc report data = sashelp.class;
run;

ods excel 
     options(sheet_name="Cars" frozen_headers = "Yes" autofilter="Yes");

proc report data = sashelp.cars;
 column _all_;
run;

ods excel close;

3个及以上数据集的输出,与此类似。

3. 逻辑库内所有数据集的输出

显然,如果照着上面举例手动添加数据集的输出程序,输出逻辑库中的所有数据集的过程就太过繁琐,所以需要批量处理。

常见的批量处理的方法有两种,一是宏循环,二是call execute语句。下面来介绍这两种方法的实现。

不管哪种方法,首先需要获取逻辑库中的数据集名称,以方便引用。这里,以SASHelp逻辑库中前10个数据集进行举例。

**Get datasets' names;
data tables;
  set sashelp.vtable;
  where libname = "SASHELP";

  if _n_ <= 10;
  keep libname memname;
run;

输出结果如下:

Tables
3.1 宏循环批量输出数据集

SAS宏可以理解成是“代码生成器”,宏程序的批量处理是通过宏循环实现

参考两个数据集输出的代码,对于不同数据集,sheet_name=data=选项值是变化的。考虑到SAS数据集名称是不区分大小写,这两个选项值可以看作是相同的。

宏循环的思路是,将数据集的名称保存到各个宏变量中,这些宏变量名称的前缀相同,后缀以序号结尾。这里我将其称为,宏变量序列。获取宏变量序列后,利用循环变量,从而实现“代码生成”的效果。

3.1.1 宏变量序列的生成

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

  1. Proc SQL 中的into :语句
  2. Data步中的call symputx语句
3.1.1.1 Proc SQL生成宏变量序列

into :语句之前介绍过,可以参考SAS编程:Proc SQL生成宏变量时INTO子句的使用 。使用这个方法,需要获取数据集的记录数,这里就不介绍如何获取数据集的记录数了,直接手动赋值为10。

**Save tables' names in macro vars by sql;
proc sql noprint;
 select memname
   into :memname1- :memname10
   from tables;
quit;

%put memname1 = &memname1.;
%put memname10 = &memname10.;

输出结果如下:

3.1.1.2 Data 步中生成宏变量序列

与SQL中依靠后缀序列进行计数不同,Data步中数据集自带的自动变量_n_可以实现计数的功能。

**Save tables' names in macro vars by data step;
data _null_;
   set tables;
    call symputx("name"||strip(put(_n_, best.)), strip(memname));
run;

%put memname1 = &name1.;
%put memname10 = &name10.;

输出结果与SQL结果一致,可以看出Data步方法实现可以省去获取数据集记录数的步骤,整体上代码比较简洁

3.1.2 宏循环批量输出数据集

宏循环需要处理,第一个数据集与其他数据集输出的不同(后续数据集不需要file=选项),用判断语句进行区分。

如果想要查看宏程序具体的运行代码,可以参考文章SAS编程:检查宏程序issue思路介绍,里面有对mprint选项的介绍。

调试程序的时候,发现SASHelp.BIRTHWGT这个数据集居然有10万条记录,ods excel语句输出会出现内存不足等问题,于是使用ods tagsets.excelxp进行输出

**Export datasets to Excel file using macro loop;
%macro loop;
 
%do a = 1 %to 10;
  %if &a. = 1 %then %do;
    ods tagsets.excelxp file = "E:\999_test\test.xlsx" 
      options(sheet_name="&name1."  frozen_headers="Yes" autofilter = "Yes");

    proc report data = sashelp.&name1.;
     column _all_;
    run;
  %end;
  
  %else %do;
    ods tagsets.excelxp options(sheet_name="&&name&a." frozen_headers = "Yes" autofilter="Yes");

    proc report data = sashelp.&&name&a.;
     column _all_;
    run;
  %end;

%end;

ods tagsets.excelxp close;

%mend;

%loop;

问题处理

但使用ods tagsets.excelxp有会出现一个问题,输出文件用Excel打开的话,会显示如下内容,无法打开文件:

image.png

WPS可以正常打开输出文件,10个数据集完整的输出到Excel文件中。

也可以使用.xls作为输出文件的后缀,这时文件可以用Excel打开,不过会有Warning提示。

ods tagsets.excelxp file = "E:\999_test\test.xls" 

ods tagsets.excelxp close;

还有一个退而求其次的方法,舍弃Excel的设置,使用export过程步,进行简单的输出,不进行Excel的属性设置,后续再使用VBA对各个Sheet进行批量属性设置。

%macro loop;
  %do a = 1 %to 10;

    proc export data=sashelp.&&name&a.
        outfile='E:\999_test\test.xlsx'
        dbms=xlsx replace;
        sheet = "&&name&a.";
    run;

  %end;
%mend;

%loop;
3.2 call execute()批量输出数据集
3.2.1 call execute()的简单介绍

官方文档是这样,介绍call execute()语句的:

Resolves the argument, and issues the resolved valuefor execution at the next step boundary.
(解析参数,并发出已解析的值以便在下一步边界执行。)

call execute()是为了在Data步中执行其他完整的SAS代码,括号里面的内容是一段完整的字符串,举个简单的例子。

data _null_;
  call execute(
  '
  data class;
    set sashelp.class;
  run;
  '
 );
run;

以上的代码就是直接引号中的程序。这里读者可能会有疑问,直接写一遍引号中的代码再运行,不是更方便吗?

单从直接调用简单的SAS程序来讲,这确实是多此一举。但是,call execute()是可以直接调用数据集中的记录值,进行运行。再举个例子:

data tmp;
   a = "class";
run;

data _null_;
  set tmp;
  call execute(
  '
  data class;
    set sashelp.'||strip(a)||';
  run;
  '
 );
run;

以上程序运行的结果,是解析数据集Tmp中变量a的值,带入引号中的程序。可以这样理解,括号中的内容依旧是一个待运行的字符串,只不过这个字符串拼接了Tmp数据集中变量a的值

读者看到这里可能又会有疑问,如果数据集中有多条数据,call execute()将如何处理呢?

如果数据集中有多条数据,call execute()就会运行多次程序,按数据集记录的行数进行迭代,每一次运行的程序会更新"变量a"的值

如果Tmp数据集中有2条记录,class, cars,那么最后执行的代码如下:

 data class;
    set sashelp.class;
 run;

 data class;
    set sashelp.cars;
 run;
3.2.2 call execute()批量输出数据集到EXCEL

有了上面的介绍,直接看批量输出数据集的代码:

data _null_;
  set tables end = eof;
  if _n_ = 1 then call execute(
  '
  ods tagsets.excelxp file = "E:\999_test\test.xlsx" 
    options(sheet_name="'||strip(memname)||'"  frozen_headers="Yes" autofilter = "Yes");

  proc report data = sashelp.'||strip(memname)||';
    column _all_;
  run;
  '
  );

  else call execute(
  '
  ods tagsets.excelxp 
    options(sheet_name="'||strip(memname)||'"  frozen_headers="Yes" autofilter = "Yes");

  proc report data = sashelp.'||strip(memname)||';
    column _all_;
  run;
  '
  );

  if eof then call execute(
  '
  ods tagsets.excelxp close;
  '
  );
run;

程序中,通过_n_这个自动变量进行区分是否是第一条记录,然后进行对应的设置;end = eof选项,新建eof变量用于判断记录是否到达尾行,尾行需要将输出进行关闭。

输出结果与前面SQL一致:

总结

文章介绍了2种将数据集批量输出到Excel中的方法,宏循环和call execute()语句。宏循环,是先将所有数据集名称保存到宏变量序列中,然后通过宏循环进行调用。call execute()语句,是通过获取数据集变量值构建完整的运行程序。

本质上,宏循环和call execute()语句作用是相同的,都是“代码生成器”。

在介绍宏循环实现的过程中,还介绍了2种生成宏变量序列的方法。

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

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

推荐阅读更多精彩内容