SAS宏程序:字符变量的长度如何设置为变量中最长字符的长度?

2022-03-14  本文已影响0人  野藤_

项目中数据递交时,为控制数据集存储大小,通常要求字符变量的长度为变量中最长字符的长度。这篇文章介绍实现设置最长长度的一种方法,文末附有完整的宏程序代码。

以SASHehlp.Class为例,字符变量Name的长度为8,但是变量值中最长的长度为7。

data class;
  set sashelp.class;
  len = length(name);
run;
Class

文章框架如下:

框架

1.获取最长长度

如何获取最长字符的长度?这个过程是一个比较长度的过程,思路是,比较Name变量当前字符长度与前一条记录长度,保留较大的长度值。这样,最后一条记录就会保存数据集中最长字符的长度。

这过程中,需要将上一条记录的长度“保留”下来,这可以通过Data步中retain语句实现。第一条记录无法与“前一条”记录进行比较,可以先设置初始值为1,直接与长度1进行比较。

data class;
  set sashelp.class;
  len = length(name);

  retain maxlen 1;
  maxlen = max(maxlen, len);
run;
Class maxlen

最长字符的长度保留在最后一条记录,可以直接将这个最长长度保存到宏变量中,方便后续调用。这需要判断数据是否到达尾行,通过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.;
maxlen

获取最大长度之后,重新设置变量Name的长度。

data class;
  length name $ &maxlen;
  set sashelp.class;
run;
Class Log

这里有一个问题,重新设置的长度必然小于等于之前的长度,当小于之前长度时,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.;
CharVarList
2.2 获取字符变量数目

批量处理还需要获取字符变量的数目,这一点可以通过计数&CharVarList中单词数目来获取。函数Countw的用法可以参考官方文档(SAS Help Center: COUNTW Function)。

%let nCharVar = %sysfunc(countw(&CharVarList.));

%put nCharVar = &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.;

tmp &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.;
Dataset Label

这样在输出数据集时,就可以设置与源数据集相同的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;
VarOrd

我们可以提取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.;
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=);

感谢阅读!若有疑问,欢迎评论区交流!

上一篇下一篇

猜你喜欢

热点阅读