有时为了解项目数据结构,需要查看不少原始数据。简单的记录筛选,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;
输出结果如下:
3.1 宏循环批量输出数据集
SAS宏可以理解成是“代码生成器”,宏程序的批量处理是通过宏循环实现。
参考两个数据集输出的代码,对于不同数据集,sheet_name=
与data=
选项值是变化的。考虑到SAS数据集名称是不区分大小写,这两个选项值可以看作是相同的。
宏循环的思路是,将数据集的名称保存到各个宏变量中,这些宏变量名称的前缀相同,后缀以序号结尾。这里我将其称为,宏变量序列。获取宏变量序列后,利用循环变量,从而实现“代码生成”的效果。
3.1.1 宏变量序列的生成
宏变量序列的生成常用也有2种方法:
- Proc SQL 中的
into :
语句- 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打开的话,会显示如下内容,无法打开文件:
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茶谈!
若有疑问,欢迎评论交流!