SAS给宏变量赋值大体来说其实就3种方式:%let语句、data步中的call symput语句和proc sql过程中into语句。
这里根据变量和观测的数量分情况进行如下讨论,即:
- 单变量单观测变单宏变量
- 单变量单观测变多宏变量
- 单变量多观测变多宏变量
单变量单观测变单宏变量
提出问题如下:
/*problem1:将组名信息赋值给宏变量group,各组之间\分割,单变量一观测变一宏变量*/
数据集如下:
data example1;
group = "太极组\围棋组\古筝组\书法组";
run;
分别使用如下3种方法进行宏变量赋值:
/*method1: let statement*/
%let group1 = 太极组\围棋组\古筝组\书法组;
%put &group1.;
/*method2:data步 call symput statement*/
data _null_;
set example1;
call symput("group2",group);
run;
%put &group2.;
/*method3:proc sql statement*/
proc sql;
select group into: group3
from example1
;
quit;
%put &group3.;
- 说明
proc sql;
/* create table macros as*/
select *
from dictionary.macros
;
quit;
上述语句是获得环境中macros变量信息的语句,很实用。
在单变量赋值单宏变量时,%let语句相比其他两种要简单,不必基于数据集;而proc sql语句则必须基于数据集中的变量产生;call symput则均可以实现。
单变量单观测变多宏变量
- 问题
/*problem2:将组名信息分别依次赋值给宏变量term1,term2,...,单变量一观测变多宏变量*/
- 数据集如下:
data example2;
group = "太极组\围棋组\古筝组\书法组";
run;
分别使用如下3种方法进行宏变量赋值:
/*method1: let statement*/
%let term1_1 = 太极组;
%let term1_2 = 围棋组;
%let term1_3 = 古筝组;
%let term1_4 = 书法组;
%macro batch;
%do i = 1 %to 4;
%let _term1_&i. = %qscan("太极组\围棋组\古筝组\书法组" , &i. , "\");
%end;
proc sql;
/* create table macros as*/
select *
from dictionary.macros
where scope = "BATCH"
;
quit;
%mend;
%batch;
/*method2:data步 call symput statement*/
data _null_;
set example2
do i = 1 to 4;
call symput(cats("term2_", put(i , best.)) , scan(group , i, "\") );
end;
run;
/*method3:proc sql statement*/
data m3;
set example2
array term (4) $ term1 - term4;
do i =1 to 4;
term(i) = scan(group , i, "\") ;
end;
run;
proc sql;
select term1, term2, term3, term4 into :term3_1 ,:term3_2 ,:term3_3,:term3_4
from m3
;
quit;
proc sql;
/* create table macros as*/
select *
from dictionary.macros
;
quit;
- 说明
- 这种情况就可以看出 %let语句的劣势了,无法批量操作,为了批量操作我写了个简单宏才实现;
- call symput语句的实现则很简单,这是因为只涉及到单个变量的操作,是data步的优势所在,代码量很少;
- 而proc sql给宏变量赋值的优势在这里体现的不明显,但是也可以看出它针对数据集层面,批量化宏变量赋值操作的便捷性。
单变量多观测变多宏变量
- 问题
/*problem3:将分组变量中组名观测依次赋值给宏变量term1,term2,...,单变量多观测变多宏变量*/
- 数据集
data example3;
length group $200.;
input group;
cards;
太极组
围棋组
古筝组
书法组
;
proc print;
run;
- 使用3种方法赋值宏变量
/*method1:%let statement*/
proc transpose data= example3 out=_out3;
var group;
run;
%macro m1;
data test;
set _out3;
call symput("group" ,catx("\", of col1 - col4) );
run;
%do i = 1 %to 4;
%let _1term_&i. = %qscan( "&group.", &i.,"\");
%end;
proc sql;
create table macros as
select *
from dictionary.macros
where scope = "M1"
;
quit;
%mend;
%m1;
使用%let解决这个问题,需要先转置,使问题变为基于变量,也就是列,弥补基础语句基于观测处理问题能力偏弱的短板;
然后构建宏,在宏里还要再把数据集里的多个变量赋值为单个宏变量,这是因为%let是在变量层面解决问题的,数据集无处下爪;
之后使用循环,弥补%let批量处理问题的短板。
/*method2:call symput statement*/
proc transpose data= example3 out=_out3;
var group;
run;
data M2;
set _out3;
array terms(4) col1 -col4;
do i = 1 to 4;
call symput(cats("_2term_", put(i , best.)),terms(i) );
end;
run;
proc sql;
select *
from dictionary.macros
;
quit;
- 说明
可以看到,使用call symput解决这个问题,同样需要经历转置步骤转变为处理变量的问题;然后在data步使用数组循环,还是比较繁琐的。
- 而proc sql的优势就体现出来了,一个代码块就达成目的,如下:
/*method3:proc sql statement*/
/*单变量多观测变多个宏变量*/
proc sql;
select group into :_3term1 -: _3term99
from example3
;
quit;
/*单变量多观测变一宏变量*/
proc sql;
select group into :_3term separated by " "
from example3
;
quit;
proc sql;
/* create table macros as*/
select *
from dictionary.macros
;
quit;
- 说明
- 这种情况%let的实现一样要借助于宏,并且因为涉及到了数据集层面的操作,%let就更加不太擅长了;
- 同时,call symput的实现也不太容易,SAS data步长于变量操作弱于观测操作的弊端便体现出来了。
- 在这种情况下,proc sql的批量化和观测层面操作的优势便体现出来了,如果是有多变量多观测赋值多宏变量的情况,那proc sql的优势就会更加明显了;
proc sql给宏变量赋值的情况大致可以分为如下几种情况
-
单变量单个观测值赋值给单个宏变量:
proc sql; select var into :mvar from dtin ; quit;
,这种情况用proc sql
不具有优势;
-
多变量单观测值赋值给多个宏变量:
proc sql; select var1, var2, var3, var4 into :mvar1, mvar2, mvar3, mvar4 from dtin ; quit;
-
单变量多观测赋值给多个宏变量:
proc sql; select var1into :mvar1 -:mvar99 from dtin ; quit;
,其中,mvar99
中的数值设置的较大值,是在未知观测值数量时的简便做法;
-
单变量多观测赋值给单个宏变量:
proc sql; select var1into :mvar separated by "sep" from dtin ; quit;
,其中,sep
是分隔符;
-
多变量多观测值赋值给多个宏变量:
proc sql; select var1, var2, var3, var4 into :var1m1 -:var1m99, var2m1 -:var2m99, var3m1 -:var3m99, var4m1 -:var4m99 from dtin ; quit;
,其中的4个变量均有多个观测值;
- 举例
proc sql;
select name, weight, height into :name1 -:name99,
:weight1 -:weight99,
:height1 -: height99
from sashelp.class(where = (sex = "女"))
;
quit;
-
多变量多观测值赋值给多个宏变量,但变量内观测值拼接:
proc sql; select var1, var2, var3, var4 into :var1m1 -:var1m99, var2m1 -:var2m99, var3m1 -:var3m99, var4m1 -:var4m99 from dtin ; quit;
,其中的4个变量均有多个观测值;
proc sql;
select name, weight, height into :name separated by " ", :weight separated by " " ,:height separated by " "
from sashelp.class(where = (sex = "女"))
;
quit;
总结
不涉及数据集的单变量的宏变量赋值,%let很实用;
基于数据集且多在变量层面操作,call symput和proc sql均适用;
基于数据集且涉及多个变量和观测层面的操作,proc sql比较有优势,其他两种实现起来就需要更多的代码量;