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

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

以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=);

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容