题目:给定一个SAS数据集a,它只含有一个字符变量col1,比如:
- data a;
- input col1 $;
- cards;
- abc
- 1234
- ef
- ;
要求产生一个数据集b,它也只含有一个名为col1的字符变量,要求a中的字符串在b中出现,且出现次数为其自身的字符串长度,即:
Obs col1
1 abc
2 abc
3 abc
4 1234
5 1234
6 1234
7 1234
8 ef
9 ef
问产生数据集b的代码如何写?
----------------------------------------------------------------------------------------------------------------------------------------------------
以下抛砖引玉,我给出3种不同的方法:
**************************************
方法(1)——用output来解决问题:
- data b;
- set a;
- do i=1 to length(col1);
- output;
- end;
- drop i;
- run;
用一次output,系统就将当前的col1变量值写到b中一次,重复length(col1)次,结束当前的col1变量值,换a集的下一个变量值再重复此过程,直到a中col1的所有内容取尽为止。
这是最简单的方法。
**************************************
方法(2)——用保留数组来做:
我们的目的是产生一个保留数组,比如名为colcol的数组,其内容为:
colcol1 colcol2 colcol3 colcol4 colcol5 colcol6 colcol7 colcol8 colcol9
abc abc abc 1234 1234 1234 1234 ef ef
之后再将这个数组(数组都是横着的)转置成竖着的方向,冠名为col1。
第一步,我们要知道需要创建的colcol数组究竟应该有多少个元素,比如上例中为9,这个9就是a的变量col1中字符串长度的总和,所以,我们应该先统计a的变量col1中字符串长度的总和,并将其储存起来,以便其他程序调用。程序如下:
- data _null_;
- set a end=final;
- rowsum+length(col1);
- if final then call symput('number',trim(left(put(rowsum,best.))));
- run;
第二步,产生colcol 保留数组,并填入我们想要的内容:
- data b;
- array colcol[&number] $;
- retain index_col colcol1-colcol&number;
- set a end=final;
- if _n_=1 then index_col=1;
- do i=index_col to length(col1)+index_col-1;
- colcol[i]=col1;
- end;
- index_col=length(col1)+index_col;
- if final;
- drop i col1 index_col;
- run;
其中index_col 为保留变量,它的功能是数组的指针,在每次data b大循环开始时,它总指向数组中需要开始填写的地方。
这个程序结束后,我们能得到如下的b集:
Obs colcol1 colcol2 colcol3 colcol4 colcol5 colcol6 colcol7 colcol8 colcol9
1 abc abc abc 1234 1234 1234 1234 ef ef
第三步,转置b集,并将竖着的字符串们统一冠名为col1,具体程序为:
- proc transpose data=b out=b(drop=_NAME_) prefix=col;
- var colcol1-colcol&number;
- run;
之后,我们就得到了如题目所要求的结果。
**************************************
方法(3)——这是我想了很长时间才想出来的疯狂方法,这个方法很怪,是我的“杀手锏”,它是以transpose为核心的方法:
第一步,查清楚a中col1 对应的所有字符串中最长的那个字符串的长度,并将其储存起来:
- proc sql noprint;
- select max(length(col1)) into :len_max
- from a
- ;
- quit;
我们将这个长度最大值赋给宏变量len_max,以便其他程序调用。
len_max的值按题目给出的a集来看应该是4。
第二步,产生如下的b集:
Obs col1 colcol1 colcol2 colcol3 colcol4
1 abc 1 1 1 .
2 1234 2 2 2 2
3 ef 3 3 . .
其中,colcol 变量行若有值,其值为对应行数,且colcol 系列变量中每一行究竟有多少个变量有数值,这取决于每一行col1字符串的长度。
产生如上结果的程序为:
- data b;
- set a;
- array colcol[&len_max];
- do i=1 to length(col1);
- colcol[i]=_n_;
- end;
- drop i;
- run;
现在你也许看不懂为什么要这样做,但看完第三步你就应该清楚了,这都是为了运用transpose产生理想的结果!
第三步,运用transpose产生与题目中所要求的答案相近似的结果:
- proc sort data=b out=b;
- by col1;
- proc transpose data=b out=b(drop=_NAME_) let;
- id col1;
- by col1;
针对第二步的结果,运用上述transpose会产生如下效果:
Obs col1 _1234 abc ef
1 1234 2 . .
2 1234 2 . .
3 1234 2 . .
4 1234 2 . .
5 abc . 1 .
6 abc . 1 .
7 abc . 1 .
8 abc . . .
9 ef . . 3
10 ef . . 3
11 ef . . .
12 ef . . .
注意产生的新b集中变量的名称及内容!这就是在transpose中同时运用id及by的结果!
我们已经很接近题目的要求了!
上述结果唯一的不足在于transpose操作破坏了原有col1中字符串的排序,不过没有关系,第二步中的colcol=_n_;语句就是为此设置的,它使得上述结果中最后3列的数字成功显示其对应字符串原来的顺序!这样做是以便日后排序之用!
第四步,查明新b集中除col1之外所有的变量名,并将其作为字符列表(就是字符串)储存起来,以便调用:
- proc sql noprint;
- select name
- into :variable separated by ' '
- from dictionary.columns
- where libname='WORK' and memname='B' and name^='col1'
- ;
- quit;
被赋值的variable宏变量的内容应该为:_1234 abc ef
第五步,将第三步产生的新b集的后3列相加,算出行和sumnum,若sumnum不为空则行记录保留,若其为空,则删除该行记录,另外,删除多余变量:
- data b;
- set b;
- sumnum=sum(of &variable);
- if sumnum^=.;
- drop &variable;
- run;
结果为:
Obs col1 sumnum
1 1234 2
2 1234 2
3 1234 2
4 1234 2
5 abc 1
6 abc 1
7 abc 1
8 ef 3
9 ef 3
第六步,对上述b集按sumnum排序,恢复col1本来的顺序,并删除sumnum变量:
- proc sort data=b out=b(drop=sumnum);
- by sumnum;
这样我们就得到了题目所要求的结果!
**************************************
总结:
以下三段代码是等价的,它们都可以解决问题:
(1)
- data b;
- set a;
- do i=1 to length(col1);
- output;
- end;
- drop i;
- run;
(2)
- data _null_;
- set a end=final;
- rowsum+length(col1);
- if final then call symput('number',trim(left(put(rowsum,best.))));
- run;
- data b;
- array colcol[&number] $;
- retain index_col colcol1-colcol&number;
- set a end=final;
- if _n_=1 then index_col=1;
- do i=index_col to length(col1)+index_col-1;
- colcol[i]=col1;
- end;
- index_col=length(col1)+index_col;
- if final;
- drop i col1 index_col;
- run;
- proc transpose data=b out=b(drop=_NAME_) prefix=col;
- var colcol1-colcol&number;
- run;
(3)
- proc sql noprint;
- select max(length(col1)) into :len_max
- from a
- ;
- quit;
- data b;
- set a;
- array colcol[&len_max];
- do i=1 to length(col1);
- colcol[i]=_n_;
- end;
- drop i;
- run;
- proc sort data=b out=b;
- by col1;
- proc transpose data=b out=b(drop=_NAME_) let;
- id col1;
- by col1;
- proc sql noprint;
- select name
- into :variable separated by ' '
- from dictionary.columns
- where libname='WORK' and memname='B' and name^='col1'
- ;
- quit;
- data b;
- set b;
- sumnum=sum(of &variable);
- if sumnum^=.;
- drop &variable;
- run;
- proc sort data=b out=b(drop=sumnum);
- by sumnum;
**************************************
除以上三种外,应该还有很多其他方法,希望众位牛人可以发表自己的方法,越奇怪越好!
期待中...



雷达卡




京公网安备 11010802022788号







