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

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

作者: 野藤_ | 来源:发表于2022-03-14 19: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=);
    

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

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/eiojdrtx.html