SAS中文论坛

 找回密码
 立即注册

扫一扫,访问微社区

查看: 6478|回复: 34
打印 上一主题 下一主题

SAS程序员测试(二)

[复制链接]

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
楼主
 楼主| 发表于 2004-6-3 21:47:19 | 只看该作者

SAS程序员测试(二)

要求找出以下数据中,各类Id的一段段相同vol的开始时间与结束时间。
我抓耳挠腮好半天,才弄出来,看看各位是否有更佳的解决方式。

考虑:
1 程序要有适用性,可能现有数据中某些拐角的地方没有体现。
2 转置请考虑sas最大支持的变量数:32760左右,这对于每类id某类vol总数,肯定是不够的。

data tem;
input id:$1. date:yymmdd10. vol;
format date yymmdd10.;
cards;
0 20020101 0
1 20020101 10
1 20020102 10
1 20020103 10
1 20020108 9
1 20020109 9
1 20020110 9
2 20020101 9
2 20020102 8
2 20020103 14
2 20020104 8
2 20020108 11
2 20020109 11
2 20020110 12
;run;

输出结果:
id         sdate         edate         new_vol

0     2002-01-01    2002-01-01        0
1     2002-01-01    2002-01-03       10
1     2002-01-08    2002-01-10        9
2     2002-01-01    2002-01-01        9
2     2002-01-02    2002-01-02        8
2     2002-01-03    2002-01-03       14
2     2002-01-04    2002-01-04        8
2     2002-01-08    2002-01-09       11
2     2002-01-10    2002-01-10       12
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
沙发
 楼主| 发表于 2004-6-4 03:27:58 | 只看该作者

Every SAS programmer should try this

It is definitely a very good homework assignment for every SAS programmer to work out, and I strongly recommend to put this one on the top of this list.  In our daily work, we have a lot of tasks similar to this one, so surely I have worked out my way to do it.  I will hold my program for a few days and let people to work it out from scratch.
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
板凳
 楼主| 发表于 2004-6-4 10:01:32 | 只看该作者

回复

我有个思路,但是没有完成 <!-- s:( --><img src="{SMILIES_PATH}/icon_sad.gif" alt=":(" title="Sad" /><!-- s:( -->
如果在原数据集后增加一个index字段:
data tem;
input id:$1. date:yymmdd10. vol index;
format date yymmdd10.;
cards;
0 20020101 0 1
1 20020101 10 2
1 20020102 10 2
1 20020103 10 2
1 20020108 9 3
1 20020109 9 3
1 20020110 9 3
2 20020101 9 4
2 20020102 8 5
2 20020103 14 6
2 20020104 8 7
2 20020108 11 8
2 20020109 11 8
2 20020110 12 9
;run;

用下面这段程序就可以实现:
proc means data=tem;
class id index;
var date vol;
output out=tem_result mean(vol)=vol min(date)=sdate max(date)=edate;
run;

data tem_result;
set tem_result;
where id ne '' and index ne .;
drop index _type_ _freq_;
run;
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
地板
 楼主| 发表于 2004-6-4 10:46:57 | 只看该作者

我的理解

这个问题的难点在于sas软件读数据时只能对当前记录的前面记录能够访问,而对后面的记录不能访问,所以我引入了两个变量fflag和bflag,意思是前、后记录的标记,具体说如当前记录和上一条记录相同(即id和vol相同,下同)fflag为1否则为0;同理当前记录和下一条记录相同,bflag为1否则为0。结果生成如下数据集:
id  date        vol  fflag  bflag
0  20020101   0    0        0
1  20020101  10   0        1
1  20020102  10   1        1
1  20020103  10   1        0
1  20020108   9    0        1
1  20020109   9    1        1
1  20020110   9    1        0
2  20020101   9    0        0     
2  20020102   8    0        0
2  20020103  14   0        0
2  20020104   8    0        0
2  20020108  11   0        1
2  20020109  11   1        0
2  20020110  12   0        0
然后对该数据集进行筛选即可得结果。
源代码
[code:ea82f]
/*生成fflag字段*/
data tem1;
  set tem;
  if id=lag&#40;id&#41; and vol=lag&#40;vol&#41; then fflag=1;
    else fflag=0;
proc sort data=tem1;
  by descending id descending date;
/*生成bflag字段*/
data tem2;
  set tem1;
  bflag=lag&#40;fflag&#41;;
  if _n_=1 then  bflag=0;
proc sort data=tem2;
  by id date;
/*生成最后结果*/
data tem3;
  set tem2;
  retain sdate;
  if fflag=0 then sdate=date;
  if bflag=0 then
    do;
      edate=date;
      new_vol=vol;
      output;
     end;
  format sdate yymmdd10&#46; edate yymmdd10&#46;;
  keep id sdate edate new_vol;
run;[/code:ea82f]
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
5#
 楼主| 发表于 2004-6-4 10:58:49 | 只看该作者

试试这个

[code:9e624]DATA SSS&#40;KEEP=ID DATE VOL&#41; TTT&#40;KEEP=END_DATE&#41; TT&#40;KEEP=DATE&#41;;
        SET TEM END=LAST;
        END_ID=LAG&#40;ID&#41;;
        END_DATE=LAG&#40;DATE&#41;;
        END_VOL=LAG&#40;VOL&#41;;
        IF ID=END_ID AND VOL=LAG&#40;VOL&#41; THEN DELETE;
        ELSE OUTPUT SSS TTT;
        IF LAST THEN OUTPUT TT;
DATA TTT;
        SET TTT&#40;FIRSTOBS=2&#41; TT&#40;RENAME=&#40;DATE=END_DATE&#41;&#41;;
DATA RESULT;
        MERGE SSS TTT;
RUN;
[/code:9e624]
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
6#
 楼主| 发表于 2004-6-4 12:40:51 | 只看该作者

try below code

proc sort data=tem; by id vol date;run;

data tem(keep=id sdate edate vol);
retain id ''  sdate edate  vol 0;
set tem; by id vol date;
if first.vol then sdate=date;
if last.vol then do;
edate=date;output;
end;
format sdate edate yymmdd10.;
run;

proc sort data=tem ; by id sdate;run;
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
7#
 楼主| 发表于 2004-6-4 14:51:15 | 只看该作者

回复

jimmy的代码有问题
因为id 和vol值相同的记录可能不是相邻的,但是你这样做也把他们合成一条记录了。
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
8#
 楼主| 发表于 2004-6-4 17:22:44 | 只看该作者

hi

proc sort data=tem;
by id vol;
run;
proc sql;
create table aa as select id, vol as new_vol, min(date) as sdate, max(date) as edate from tem
group by id, vol;
我不知道怎么把min(date)代表的天数转换为日期,只好用个笨办法
data final;
set aa;
format sdate:yymmdd10. edate:yymmdd10.;
run;
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
9#
 楼主| 发表于 2004-6-4 17:55:51 | 只看该作者

ONE MORE

刚才那个用了LAG,在给个用DIF的
[code:7a981]
DATA SSS&#40;KEEP=ID DATE VOL&#41; TTT&#40;KEEP=END_DATE&#41; TT&#40;KEEP=DATE&#41;;
        SET TEM END=LAST;
        RL=DIF&#40;ID&#41;+DIF&#40;VOL&#41;;
        END_DATE=LAG&#40;DATE&#41;;
        IF RL=0 THEN DELETE;
        ELSE OUTPUT SSS TTT;
        IF LAST THEN OUTPUT TT;
DATA TTT;
        SET TTT&#40;FIRSTOBS=2&#41; TT&#40;RENAME=&#40;DATE=END_DATE&#41;&#41;;
DATA RESULT;
        MERGE SSS TTT;
RUN;
[/code:7a981]
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
10#
 楼主| 发表于 2004-6-4 19:35:11 | 只看该作者

Reply

很多帖子!每个帖子都有一种思路可以让我借鉴。

希望各位能更多的考虑运行效率,以及避免拼数据。

以下是我的程序,如果有问题或建议,请及时回复。
[code:09378]
proc sort data=tem;by id date;run;

data result;
retain id sdate edate new_vol;
format sdate yymmdd10&#46; edate yymmdd10&#46;;
set tem;
by id;
lag_vol=lag&#40;vol&#41;;
lag_date=lag&#40;date&#41;;

if first&#46;id then call symput&#40;'sdate',date&#41;;
else do;
   if lag_vol^=vol then do;
      sdate=resolve&#40;'&sdate'&#41;;
      edate=lag_date;
      new_vol=lag_vol;
      output;
      call symput&#40;'sdate',date&#41;;
   end;  
end;

if last&#46;id then do;
   sdate=resolve&#40;'&sdate'&#41;;
   edate=date;
   new_vol=vol;
   output;
end;

keep id sdate edate new_vol;
run;[/code:09378]
回复 支持 反对

使用道具 举报

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

本版积分规则

QQ|小黑屋|手机版|Archiver|SAS中文论坛  

GMT+8, 2026-2-5 19:45 , Processed in 0.118112 second(s), 20 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表