前段时间有人给小编提了一个需求,找出数据集中长度超过200字节的变量,并对变量进行拆分...这个需求当然不难,但是还是分享给大家~主要最近没写啥程序,也就没学到啥新的技能...关于变量长度的拆分,我想也是一个常见的问题。
实现方法
小编每拿到一个需求的时候
最先考虑的是如何实现
因为不同的办法决定了代码的多少
以及运行效率的高低
不过
真正忙起来的时候哪有时间去思考那么多方法...
能够在第一时间解决问题的方法就是好方法
...
以此为例,小编最先想到实现这个需求的办法是啥:
1.找出数据集中字符变量(各观测存储字符串最长长度超过200)的变量...
2.根据实际储存最长长度进行计算需要新衍生变量个数并进行衍生...
3.强制转化变量属性大于200个长度但存储最长长度未达200字符的变量...
4.调整数据集中变量顺序及删除乱七八糟的衍生过程文件...
实现步骤基本上就是这样了,然后就进行细节的填充。细节的填充就是SAS程序进行各步骤的实现,接下来看看小编的实现方法..
写这个程序的时候,我开始打算开放好几个宏参数...
程序写着、写着就写懒了....不想弄太多功能了.
正所谓“杀猪焉用宰牛刀”,太多功能了,也用不到...
下面与小编看看这个程序的代码:
首先定义了3个宏参数:
1.inds :输入需要处理的数据集
2.maxlen:指定超过的长度...默认为200,这个就是写懒了的典型例子..
写了一半就不想开放了...然后又懒的修改前面的,也就弄一个默认值这里
而且后面的计算规则也基本都是依据200长度来写的
...
3.cnt:拆分后衍生变量之间的间隔符号,默认为空
下面这段程序的功能就是check一下输入的数据集的格式是否正确,
不正确的话会跳出宏的执行(%return;跳出宏的执行)
如果正确的话,就重新定义了几个Local宏变量
%macro aut_dev_var(inds=,maxlen=%str(200),cnt=%str( ));
%local libname memname;
%if &inds.= %then %do;
%put note:plesce check your dataset name;
%return;
%end;
%if %length(%sysfunc(compress("&inds.","."))) ne %length(%sysfunc(compress("&inds.",""))) %then %do;
%let libname=%scan("&inds.",1,".");
%let memname=%scan("&inds.",2,".");
%end;
%else %do;
%let libname=work;
%let memname=&inds.;
%end;
然后就到了对输入的数据集进行处理的阶段了~
获取数据集的变量名,变量类型,变量长度等数据集的属性等...
并筛选出超过200字符长度字符变量....
如果不存在这样的变量,则直接跳转到宏的结尾阶段(%goto语句跳转)
/*情况一:inds 输入为单个数据集 */
proc contents data=&libname..&memname. out=_varstemp10(keep=memname name label varnum type length ) directory noprint memtype=data centiles;
proc sort data=_varstemp10 out=_varstemp10 sortseq=linguistic(numeric_collation=on);by memname varnum ;
quit;
/*找出需要转置处理的变量 */
data _varstemp11;
set _varstemp10;
if input(&maxlen.,??best.)<length and type=2 ;
run;
%let dsid=%sysfunc(open(_varstemp11));
%let nobs=%sysfunc(attrn(&dsid,nobs));
%let rc= %sysfunc(close(&dsid));
%if &nobs. eq 0 %then %do;
%put ******************************************************************************;
skip 2;
%put &memname.不存在超过限定长度的变量以及观测...将跳出后续处理;
skip 2;
%put ******************************************************************************;
%goto exit;
%end;
接着,如果存在变量属性超过200长度变量,则将这样变量塞入宏变量中
同时利用_N_给每条观测添加一个行号....
接着就给数据集做一个transpose,将每个变量的值变成纵向的结构
并找出存储值超过指定长度的观测(本来打算将这样的记录做一个输出、也就这儿为啥用transpose的原因...后来想了想还是算了,输出也没啥用...应该没人看)
proc sql noprint;
select name into:translist separated by " " from _varstemp11 ;
quit;
/*衍生数据集行号:作为索引变量,数据集转置key变量*/
data _varstemp17;
set &libname..&memname.;
temp_line_id=_n_;
run;
proc transpose data=_varstemp17 out=_varstemp17 prefix=value;
by temp_line_id;
var &translist.;
run;
/*进行筛选,将超过指定长度的观测挑选出来*/
data _varstemp17;
length domain value1 $3000.;
set _varstemp17;
length=length(value1);
domain=compress(%upcase("&memname."));
if input(&maxlen.,??best.)<length;
run;
/*获取超出指定长度的记录数*/
%let dsid=%sysfunc(open(_varstemp17));
%let nobs=%sysfunc(attrn(&dsid,nobs));
%let rc= %sysfunc(close(&dsid));
%if &nobs. eq 0 %then %do;
%put ******************************************************************************;
skip 2;
%put &memname.不存在超过限定长度的变量以及观测...将跳出后续处理;
skip 2;
%put ******************************************************************************;
%goto exit;
%end;
接着,在上面的基础上找出各变量存储的最长长度...
/*找出数据集中变量超过指定长度的变量的最长字符个数*/
proc sql undo_policy=none;
create table _varstemp16 as select distinct domain,_name_,_label_,max(length) as max from _varstemp17
group by domain,_name_;
quit;
接着还是创建宏变量...
下面的宏变量就是用来存放衍生程序的语句
新生成的变量,小编采用的ksubstr来拆分变量,为什么用Ksubstr
这个地方不好言传,可以慢慢意会...
也可以说是避免将一个汉字从中劈开,造成乱码这种情况...
然后一个超过长变量到底衍生新生成几个变量合适呢?
这个是由下面程序的do语句控制的~
data _varstemp12;
set _varstemp16;
do i=1 to (max*2)/(&maxlen.)+1;
newvar=compress(strip(_name_)||strip("_&cnt.")||strip(i)||strip("=ksubstr(strip(")||strip(_name_)||strip("),")||ifn(i=1,1,(i-1)*100+1)||strip(",100)"));
newlabl=compress(strip(_name_)||strip("_&cnt.")||strip(i)||strip('="')||strip(_label_)||strip(i)||strip('"'));
output;
end;
run;
为更好的看到这一步的效果:小编决定插入一个执行过程中的截图:
比如IETEST这个变量最长长度269个字符,我在此处进行拆分3个语句...
然后将这个数据集merge到总的数据结构的数据集中
这一步操作是为了retain变量在数据集中出现的顺序号
因为我后面还会在set数据集前length变量长度,会修改变量出现的顺序
同事衍生变量的时候新生成变量一般都在最后,
而要做到新生成变量出现的位置在原始的变量那个地方...
因此就需要retain一下数据中的变量....
proc sort data=_varstemp12(rename=(domain=memname _name_=name)) out=_varstemp13 sortseq=linguistic(numeric_collation=on);by memname name i;quit;
proc sort data=_varstemp10 out=_varstemp10 sortseq=linguistic(numeric_collation=on);by memname name varnum ;quit;
data _varstemp14;
length memname name $3000.;
merge _varstemp10 _varstemp13;
by memname name;
if ^missing(i) then name=compress(strip(name)||strip("_&cnt.")||strip(i));
run;
merge后的效果大概就是下面的:待会会在定义宏变量的时候 order by varnum,i
接着,将这些语句啊,变量啊...塞进相应的宏变量中
通过调用宏变量的方式,实现程序语句的批量处理...
proc sql noprint;
select newvar into:new_varlist separated by ";" from _varstemp12 ;
select newlabl into:new_lablist separated by " " from _varstemp12 ;
select name into:new_ordlist separated by " " from _varstemp14 order by varnum,i ;
select name into:new_lenlist separated by " " from _varstemp14(where=(^missing(i)));
select strip("max(length(")||strip(name)||strip("))")||" as "||strip("len_")||strip(name) into:droplist separated by ","
from _varstemp14(where=(^missing(i))) ;
select NAME into:chg_lenlist separated by " " from _varstemp14(where=(type=2 AND length>200 ));
quit;
/*
option error 关闭黑色的数据error 主要由于变量进行ksubstr进行截取的时候出现位置问题.这里可以关闭,不显示
同样varlenchk=nowarn 也是关闭警告,在变量长度发生改变的时候关闭警告
*/
option error=0 varlenchk=nowarn;
data &memname.;
set &libname..&memname.;
length &new_lenlist. $200.;format &new_lenlist. $200.;informat &new_lenlist. $200.;
&new_varlist.;;
label &new_lablist.;
run;
data &memname.;
length &chg_lenlist. $200.;
set &memname.;
run;
data &memname.(keep=&new_ordlist.);
retain &new_ordlist.;
set &memname.;
run;
这里有俩个option是很有趣的option
一个是error=0;
另外一个就是varlenchk=nowarn;
至于作用,可以见上面代码中注释的部分...
特别建议大家在SASHELP中输入nowarn
我想一定会有新大陆发现...
一些小的option,可以让日志更美观....
有时候还能帮助你隐藏或者提醒编程中的错误....
言归正传,接着上面的代码来说...
做完上面的操作,我们的需求基本完成了...
已经衍生生成了新变量,同时添加了标签
也改变了变量出现的位置顺序,而且还修改了变量的长度...
但是呢...由于前面的do语句以及ksubstr的作用
是否有多余的变量生成呢...
也就是衍生的变量可能下面全是空的...
这个时候就需要进行一步操作,drop掉多余的变量...
proc sql undo_policy=none;
create table _varstemp15 as select distinct &droplist. from &memname. ;
quit;
proc transpose data=_varstemp15 out=_varstemp18 ;
var _all_;
run;
data _varstemp18;
set _varstemp18;
length domain var $200.;
domain=upcase("&memname.");
var=substr(_name_,5);
drop _name_;
if col1 in (0 1);
run;
/*获取多余变量名称*/
%let dsid=%sysfunc(open(_varstemp18));
%let nobss=%sysfunc(attrn(&dsid,nobs));
%let rc= %sysfunc(close(&dsid));
%if &nobss. eq 0 %then %do;
%put ******************************************************************************;
skip 2;
%put &memname.不存在多余变量...程序将跳出...
skip 2;
%put ******************************************************************************;
%goto exit;
%end;
proc sql noprint;
select var into:dp_list separated by " " from _varstemp18 ;
quit;
data &memname.(drop=&dp_list.);
set &memname.;
run;
做完这一步就算是真的操作完了...
当然还是还在简单的处理一下
删除过程中乱七八糟的过程文件....
%symdel 删除全局宏变量,这里又有/nowarn;
如果droplista这个宏变量不存在,没有/nowarn是会绿色的警告的...
有了这个就不会有警告...
/*删除过程中的衍生数据集*/
%symdel droplista/nowarn;
%global droplista;
proc contents data=work._all_ out=_varstemp10(where=(index(memname,"_VARSTEMP")) keep=memname) directory noprint memtype=data centiles;
proc sort data=_varstemp10 out=_varstemp10 sortseq=linguistic(numeric_collation=on) nodupkey;by memname ;
run;
proc sql noprint;
select distinct memname into:droplista separated by " " from _varstemp10;
quit;
proc delete data= &droplista ;quit;
%mend;
然后呢
上面的这个macro是针对单个数据进行处理的
写个循环调用这个宏就可以批量操作了
也都比较简单
....
其实,我写完后就发现我写麻烦了...为啥这么说呢
因为完全可以不计算变量储存的最长长度
接着用变量属性的长度...
然后最后也删除一下多余的衍生变量,就可以了
今天就这么多了,后续内容,敬请期待~