项目中数据递交时,为控制数据集存储大小,通常要求字符变量的长度为变量中最长字符的长度。这篇文章介绍实现设置最长长度的一种方法,文末附有完整的宏程序代码。
以SASHehlp.Class为例,字符变量Name的长度为8,但是变量值中最长的长度为7。
data class;
set sashelp.class;
len = length(name);
run;
文章框架如下:
1.获取最长长度
如何获取最长字符的长度?这个过程是一个比较长度的过程,思路是,比较Name变量当前字符长度与前一条记录长度,保留较大的长度值。这样,最后一条记录就会保存数据集中最长字符的长度。
这过程中,需要将上一条记录的长度“保留”下来,这可以通过Data步中retain
语句实现。第一条记录无法与“前一条”记录进行比较,可以先设置初始值为1,直接与长度1进行比较。
data class;
set sashelp.class;
len = length(name);
retain maxlen 1;
maxlen = max(maxlen, len);
run;
最长字符的长度保留在最后一条记录,可以直接将这个最长长度保存到宏变量中,方便后续调用。这需要判断数据是否到达尾行,通过set
语句的end=
选项可以实现。
data _null_;
set sashelp.class end = last;
len = length(name);
retain maxlen 1;
maxlen = max(maxlen, len);
if last then call symputx("maxlen", put(maxlen, best.));
run;
%put maxlen=&maxlen.;
获取最大长度之后,重新设置变量Name的长度。
data class;
length name $ &maxlen;
set sashelp.class;
run;
这里有一个问题,重新设置的长度必然小于等于之前的长度,当小于之前长度时,SAS默认会报Warning。这个Warning可以通过设置系统选项varlenchk=
来移除,长度设置结束后,再将系统选项恢复成默认选项。
options varlenchk=nowarn;
data class;
length name $ &maxlen;
set sashelp.class;
run;
options varlenchk=warn;
2. 批量设置字符变量长度
2.1 获取字符变量名称
以上只是对Name一个字符变量进行处理,我们最终处理的要求是对数据集中所有字符变量进行重新设置长度。所有字符变量名称可以通过SAS字典来获取,SQL生成宏变量的,可以参考SAS编程:Proc SQL生成宏变量时INTO子句的使用。
proc sql noprint;
select name into: CharVarList separated by "!"
from dictionary.columns
where libname = "SASHELP" and memname = "CLASS" and type = "char";
quit;
%put CharVarList = &CharVarList.;
2.2 获取字符变量数目
批量处理还需要获取字符变量的数目,这一点可以通过计数&CharVarList中单词数目来获取。函数Countw
的用法可以参考官方文档(SAS Help Center: COUNTW Function)。
%let nCharVar = %sysfunc(countw(&CharVarList.));
%put nCharVar = &nCharVar.;
2.3 建立宏循环批量处理
下面设置宏循环来获取每个字符变量的最大长度。我们设置字符变量的长度,最基本的SAS语句是length
语句,例如length name $ 7;
。如果是设置两个变量的长度,就是length name $ 7 sex $ 1;
。
这要求,批量处理需要提前设置好length
语句需要的变量名称以及长度。具体是将字符变量的名称,与其对应的最大字符长度拼接起来,最后将所有字符变量的拼接信息汇总,保存到宏变量中方便调用。
%macro relen;
**options for remove length warning;
options varlenchk=nowarn;
**Get names of character vars;
proc sql noprint;
select name into: CharVarList separated by "!"
from dictionary.columns
where libname = "SASHELP" and memname = "CLASS" and type = "char";
quit;
%put CharVarList = &CharVarList.;
**Get num of character vars;
%let nCharVar = %sysfunc(countw(&CharVarList.));
%put nCharVar = &nCharVar.;
**Get length information;
data tmp;
set sashelp.class end = last;
%do i = 1 %to &nCharVar.;
len_&i. = length(%sysfunc(scan(&CharVarList., &i., ! )));
retain maxlen_&i. 1;
maxlen_&i.= max(maxlen_&i., len_&i.);
**Contents for Length statements;
lenvar_&i. = catx(" $ ", "%sysfunc(scan(&CharVarList., &i., ! ))" , put(maxlen_&i., best.));
%end;
if last then call symputx("lenvar", catx(" ", of lenvar_:) );
run;
%put lenvar = &lenvar.;
**Reset length;
data class;
length &lenvar.;
set sashelp.class;
run;
**Restore default options;
options varlenchk=warn;
%mend relen;
%relen;
以下是中间数据集tmp与宏变量lenvar
的内容。tmp数据集通常不需要保留,调试完毕后可以替换为_null_
。tmp数据集最后一条记录的长度设置内容,会保存到宏变量lenvar
。后续使用Length语句时,可以直接调用,length &lenvar.;
。
3. 输出数据集的设置
3.1 输出数据集的命名
前面展示的重新设置长度的数据集,直接保存在临时逻辑库的Class数据集中。在实际应用时,我们应该保存在源数据集中。这一点很好实现,为这个宏添加两个宏参数,一个表示源数据集所在的逻辑库,另一个代表源数据的名称。这样就可以实现将重新设置长度后的数据集,输出到源数据集中。
%macro relen(lib=, dtnam=);
...
...
**Get names of character vars;
proc sql noprint;
select name into: CharVarList separated by "!"
from dictionary.columns
where libname = upcase("&lib.") and memname = upcase("&dtnam.") and type = "char";
quit;
%put CharVarList = &CharVarList.;
...
...
**Get length information;
data tmp;
set &lib..&dtnam. end = last;
...
...
**Reset length;
data &lib..&dtnam.;
length &lenvar.;
set &lib..&dtnam.;
run;
...
...
%mend relen;
3.2 输出数据集Label的设置
3.1部分最后输出部分是直接新建一个数据集,替换之前的源数据集。通常SDTM、ADaM数据集都是有Label的,新建输出程序时如果不重新设置Label,Label信息就会被抹去。
源数据集的Label信息,可以从SAS字典中获取。
**Get the label of the source dataset;
proc sql noprint;
select memlabel into: DtLab
from dictionary.tables
where libname = "SASHELP" and memname= "CLASS"
;
quit;
%put DtLab = &DtLab.;
这样在输出数据集时,就可以设置与源数据集相同的Label了。
data &lib..&dtnam.(label = "&DtLab.");
length &lenvar.;
set &lib..&dtnam.;
run;
3.3 输出数据集变量的顺序设置
以上举例使用SASHelp.Class数据集,有一个巧合是它的字符变量是最前面两位。一般SDTM、ADaM数据集中,字符变量和数值变量的排列都是相互混杂的,最后输出的Length语句会造成所有字符变量排列到前面,与源数据集中的排列顺序不同,这一点也需要额外设置。
数据集变量的排列顺序,也保存在SAS字典中:
proc sql noprint;
create table varord as
select *
from dictionary.columns
where libname = "SASHELP" and memname = "CLASS";
quit;
我们可以提取SAS字典中的排序信息,在最后输出数据集内容时,设置一下变量的排列顺序。
**Get the vars' order of the source dataset;
proc sql noprint;
select name into: varord separated by " "
from dictionary.columns
where libname = upcase("sashelp") and memname = upcase("class");
quit;
%put varord = &varord.;
获取排列顺序后,可以使用retain
语句,进行设置:
data &lib..&name.(label = "&DtLab.");
retain &VarOrd.;
length &lenvar.;
set &lib..&name.;
run;
总结
以上就是,设置字符变量长度为变量中最长字符的长度的一种实现方法。主要内容涉及最大长度的获取,字符长度的批量设置以及输出数据集的相关设置。
所有代码汇总如下:
%macro relen(lib= , dtnam=);
**1.options for remove length warning;
options varlenchk=nowarn;
**2.Get the label of the source dataset;
proc sql noprint;
select memlabel into: DtLab
from dictionary.tables
where libname = upcase("&lib.") and memname = upcase("&dtnam.")
;
quit;
%put DtLab = &DtLab.;
**3.Get the vars order of the source dataset;
proc sql noprint;
select name into: varord separated by " "
from dictionary.columns
where libname = upcase("&lib.") and memname = upcase("&dtnam.") ;
quit;
%put varord = &varord.;
**4.Get names of character vars;
proc sql noprint;
select name into: CharVarList separated by "!"
from dictionary.columns
where libname = upcase("&lib.") and memname = upcase("&dtnam.") and type = "char";
quit;
%put CharVarList = &CharVarList.;
**5.Get num of character vars;
%let nCharVar = %sysfunc(countw(&CharVarList.));
%put nCharVar = &nCharVar.;
**6.Get length information;
data _null_;
set &lib..&dtnam. end = last;
%do i = 1 %to &nCharVar.;
len_&i. = length(%sysfunc(scan(&CharVarList., &i., ! )));
retain maxlen_&i. 1;
maxlen_&i.= max(maxlen_&i., len_&i.);
*Contents for Length statements;
lenvar_&i. = catx(" $ ", "%sysfunc(scan(&CharVarList., &i., ! ))" , put(maxlen_&i., best.));
%end;
*Comnbine all the Length statements and save into macro Var;
if last then call symputx("lenvar", catx(" ", of lenvar_:) );
run;
%put lenvar = &lenvar.;
**7.Reset length;
data class_(label = "&DtLab.");
retain &VarOrd.;
length &lenvar.;
set &lib..&dtnam.;
run;
**8.Restore default options;
options varlenchk=warn;
%mend relen;
%relen(lib=, dtnam=);