楼主: meishanjia1900
1904 5

[原创博文] 程序征集:同一题目的不同方法,越多越好 [推广有奖]

  • 0关注
  • 12粉丝

副教授

44%

还不是VIP/贵宾

-

威望
0
论坛币
3138 个
通用积分
45.8507
学术水平
193 点
热心指数
203 点
信用等级
161 点
经验
25351 点
帖子
703
精华
0
在线时间
988 小时
注册时间
2009-5-17
最后登录
2025-10-12

楼主
meishanjia1900 发表于 2011-11-30 23:54:10 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币
我想做一个测试,看看以下这个简单的题目到底能有多少种不同的编程方法:

题目:给定一个SAS数据集a,它只含有一个字符变量col1,比如:

  1. data a;
  2.   input col1 $;
  3. cards;
  4. abc
  5. 1234
  6. ef
  7. ;
复制代码

要求产生一个数据集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来解决问题:

  1. data b;
  2.   set a;
  3.   do i=1 to length(col1);
  4.     output;
  5.   end;
  6.   drop i;
  7. 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中字符串长度的总和,并将其储存起来,以便其他程序调用。程序如下:

  1. data _null_;
  2.   set a end=final;
  3.   rowsum+length(col1);
  4.   if final then call symput('number',trim(left(put(rowsum,best.))));
  5. run;
复制代码
rowsum就是一个长度累加器,在取尽a中col1变量值后,我们将这个累加值赋给number宏变量,这样其他程序即可调用该值。

第二步,产生colcol 保留数组,并填入我们想要的内容:

  1. data b;
  2.   array colcol[&number] $;
  3.   retain index_col colcol1-colcol&number;
  4.   set a end=final;
  5.   if _n_=1 then index_col=1;
  6.   do i=index_col to length(col1)+index_col-1;
  7.     colcol[i]=col1;
  8.   end;
  9.   index_col=length(col1)+index_col;
  10.   if final;
  11.   drop i col1 index_col;
  12. 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,具体程序为:

  1. proc transpose data=b out=b(drop=_NAME_) prefix=col;
  2.   var colcol1-colcol&number;
  3. run;
复制代码

之后,我们就得到了如题目所要求的结果。

**************************************

方法(3)——这是我想了很长时间才想出来的疯狂方法,这个方法很怪,是我的“杀手锏”,它是以transpose为核心的方法:

第一步,查清楚a中col1 对应的所有字符串中最长的那个字符串的长度,并将其储存起来:

  1. proc sql noprint;
  2.   select max(length(col1)) into :len_max
  3.   from a
  4.   ;
  5. 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字符串的长度。

产生如上结果的程序为:

  1. data b;
  2.   set a;
  3.   array colcol[&len_max];
  4.   do i=1 to length(col1);
  5.     colcol[i]=_n_;
  6.   end;
  7.   drop i;
  8. run;
复制代码

现在你也许看不懂为什么要这样做,但看完第三步你就应该清楚了,这都是为了运用transpose产生理想的结果!

第三步,运用transpose产生与题目中所要求的答案相近似的结果:

  1. proc sort data=b out=b;
  2.   by col1;
  3. proc transpose data=b out=b(drop=_NAME_) let;
  4.   id col1;
  5.   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之外所有的变量名,并将其作为字符列表(就是字符串)储存起来,以便调用:

  1. proc sql noprint;
  2.   select name
  3.   into :variable separated by ' '
  4.   from dictionary.columns
  5.   where libname='WORK' and memname='B' and name^='col1'
  6.   ;
  7. quit;
复制代码

被赋值的variable宏变量的内容应该为:_1234 abc ef

第五步,将第三步产生的新b集的后3列相加,算出行和sumnum,若sumnum不为空则行记录保留,若其为空,则删除该行记录,另外,删除多余变量:

  1. data b;
  2.   set b;
  3.   sumnum=sum(of &variable);
  4.   if sumnum^=.;
  5.   drop &variable;
  6. 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变量:

  1. proc sort data=b out=b(drop=sumnum);
  2.   by sumnum;
复制代码

这样我们就得到了题目所要求的结果!


**************************************

总结:

以下三段代码是等价的,它们都可以解决问题:

(1)

  1. data b;
  2.   set a;
  3.   do i=1 to length(col1);
  4.     output;
  5.   end;
  6.   drop i;
  7. run;
复制代码

(2)

  1. data _null_;
  2.   set a end=final;
  3.   rowsum+length(col1);
  4.   if final then call symput('number',trim(left(put(rowsum,best.))));
  5. run;

  6. data b;
  7.   array colcol[&number] $;
  8.   retain index_col colcol1-colcol&number;
  9.   set a end=final;
  10.   if _n_=1 then index_col=1;
  11.   do i=index_col to length(col1)+index_col-1;
  12.     colcol[i]=col1;
  13.   end;
  14.   index_col=length(col1)+index_col;
  15.   if final;
  16.   drop i col1 index_col;
  17. run;

  18. proc transpose data=b out=b(drop=_NAME_) prefix=col;
  19.   var colcol1-colcol&number;
  20. run;
复制代码

(3)

  1. proc sql noprint;
  2.   select max(length(col1)) into :len_max
  3.   from a
  4.   ;
  5. quit;

  6. data b;
  7.   set a;
  8.   array colcol[&len_max];
  9.   do i=1 to length(col1);
  10.     colcol[i]=_n_;
  11.   end;
  12.   drop i;
  13. run;

  14. proc sort data=b out=b;
  15.   by col1;
  16. proc transpose data=b out=b(drop=_NAME_) let;
  17.   id col1;
  18.   by col1;

  19. proc sql noprint;
  20.   select name
  21.   into :variable separated by ' '
  22.   from dictionary.columns
  23.   where libname='WORK' and memname='B' and name^='col1'
  24.   ;
  25. quit;

  26. data b;
  27.   set b;
  28.   sumnum=sum(of &variable);
  29.   if sumnum^=.;
  30.   drop &variable;
  31. run;

  32. proc sort data=b out=b(drop=sumnum);
  33.   by sumnum;
复制代码

**************************************

除以上三种外,应该还有很多其他方法,希望众位牛人可以发表自己的方法,越奇怪越好!

期待中...
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:Dictionary Transpose Variable proc sql separate 字符串 程序

沙发
ademons 发表于 2011-12-1 15:11:47
proc iml,
根据字符串长度repeat矩阵。

藤椅
70后假行家 发表于 2011-12-1 15:25:44
顶一个

板凳
bobguy 发表于 2011-12-4 06:21:39
The first one is the right one. Others are clumsy.

报纸
meishanjia1900 发表于 2011-12-4 14:09:19
bobguy 发表于 2011-12-4 06:21
The first one is the right one. Others are clumsy.
我发这个帖的初衷是想训练一下我自己的SAS语言运用能力。

所以,可能目标就不是“找出能解决问题的最佳代码”,而是“找出能解决问题的所有可能代码”。

调动所有可能的技术及方法完成同一道题,可以有效的提升SAS编程技能。

比如方法(3),我绞尽脑汁终于发现,其实用transpose也可以解决问题。

虽然它可能不是最佳的,但它给了我一次熟悉transpose中id及by用法的机会。

要想找出b集中所有变量的名字,这不得不使我上网搜索sql 的具体用法。

用最少的题目覆盖尽可能多的SAS方法,是最经济的练习方法。

比如楼上说还可以用iml ,这很好,确实可以,我就没有想到。

方法上,不求最好,但求最怪、最多。

这可能更加偏重考察一个人的创造力。

地板
bobguy 发表于 2011-12-5 00:58:51
meishanjia1900 发表于 2011-12-4 14:09
我发这个帖的初衷是想训练一下我自己的SAS语言运用能力。

所以,可能目标就不是“找出能解决问题的最佳 ...
I understand your motivation.

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2026-1-2 10:18