SAS中文论坛

标题: 练手系列2:移动平均计算 [打印本页]

作者: shiyiming    时间: 2007-9-12 23:52
标题: 练手系列2:移动平均计算
[code:a4b04]data a;
infile datalines;
input cid month $ balance;
datalines;
1 200001 1
1 200002 1
1 200003 2
1 200004 .
1 200005 1
1 200006 1
1 200007 1
1 200008 1
1 200009 1
1 200010 1
1 200011 1
1 200012 1
1 200101 1
1 200102 1
1 200103 1
1 200104 1
1 200105 1
1 200106 1
1 200107 1
1 200108 1
1 200109 1
2 200001 1
2 200002 1
2 200003 2
2 200004 .
2 200005 1
2 200006 1
2 200007 1
2 200008 1
2 200009 1
;
run;[/code:a4b04]
cid表示客户号,要求计算每个客户在每个月的前6个月和前12个月的移动平均余额。
月份是从开户月至销户月是连续的,但是可能会有余额值的缺失。
如果不足6个月,则按照实际的月数求移动平均。如客户1在200001之前没有开户,所以没有余额,那么200001的6个月的移动平均就是空;在200002要求200001的平均余额;在200003要求200001到200002的平均余额;200007要求200001到200006的平均余额;200109要求200103到200108的平均余额。12个月的平均余额以次类推。
客户2的计算逻辑相同。如第1个月的移动平均是空;第2个月是第1个月的平均;以此类推。
平均数的逻辑采用与SAS的mean函数逻辑完全一致的方式,即需要去除缺失值。
请参见附件中的结果。
作者: shiyiming    时间: 2007-9-14 10:40
标题: Re: 练手系列2:移动平均计算
能提供一下程序吗?谢谢
作者: shiyiming    时间: 2007-9-14 15:55
标题: Re: 练手系列2:移动平均计算
考虑一下用lag函数
作者: shiyiming    时间: 2007-9-14 18:24
标题: Re: 练手系列2:移动平均计算
为什么不练习下混合模型呢。。。。。。
作者: shiyiming    时间: 2007-9-25 11:43
标题: Re: 练手系列2:移动平均计算
我想到的两个方法,一个就是用retain,一个就是用lag函数,retain比较麻烦,lag就比较直接一点,比如
lagb1=lag(balance);
lagb2=lag2(balance);
lagb3=lag3(balance);
lagb4=lag4(balance);
然后直接用函数得到结果就可以了。
不知道有没有其他更方便一点的做法?
作者: shiyiming    时间: 2007-9-25 22:26
标题: Re: 练手系列2:移动平均计算
大家为什么不动手试试看,写一个完整的程序,别只停留在耍耍嘴皮子。绝对没有你想象中简单,但是一旦你写出来以后,你会发现在金融行业非常实用,可重用性很高。你将从中受益颇多。
作者: shiyiming    时间: 2007-9-26 20:23
标题: Re: 练手系列2:移动平均计算
data b;
set a;

lagb1=lag(balance);
lagb2 =lag2 (balance);
lagb3 =lag3 (balance);
lagb4 =lag4 (balance);
lagb5 =lag5 (balance);
lagb6 =lag6 (balance);
lagb7 =lag7 (balance);
lagb8 =lag8 (balance);
lagb9 =lag9 (balance);
lagb10=lag10(balance);
lagb11=lag11(balance);
lagb12=lag12(balance);

mean6=mean(of lagb1-lagb6);
mean12=mean(of lagb1 - lagb12);

keep cid month balance mean6 mean12;
run;
proc print data=b;
run;
作者: shiyiming    时间: 2007-9-26 22:30
标题: to xsmile
当第二个客户号出现的时候你的方法就失灵咯,呵呵呵。
这就是我在数据中特意安排2个客户的原因,就是为了阻止简单使用lag1-lag12来实现。
再动动脑筋 <!-- s:lol: --><img src="{SMILIES_PATH}/icon_lol.gif" alt=":lol:" title="Laughing" /><!-- s:lol: -->
作者: shiyiming    时间: 2007-9-27 09:17
标题: Re: 练手系列2:移动平均计算
<!-- s:oops: --><img src="{SMILIES_PATH}/icon_redface.gif" alt=":oops:" title="Embarassed" /><!-- s:oops: --> 经验不足,没有想到这个问题,加上个排序和计数变量应该就可以了。
proc sort data = a;
  by cid;
run;

data a1;
  set a;
  count + 1;
  by cid;
  if first.cid then count = 1;
run;

data b;
set a1;

if(count&gt;1)lagb1=lag(balance);
if(count&gt;2)lagb2 =lag2 (balance);
if(count&gt;3)lagb3 =lag3 (balance);
if(count&gt;4)lagb4 =lag4 (balance);
if(count&gt;5)lagb5 =lag5 (balance);
if(count&gt;6)lagb6 =lag6 (balance);
if(count&gt;7)lagb7 =lag7 (balance);
if(count&gt;8)lagb8 =lag8 (balance);
if(count&gt;9)lagb9 =lag9 (balance);
if(count&gt;10)lagb10=lag10(balance);
if(count&gt;11)lagb11=lag11(balance);
if(count&gt;12)lagb12=lag12(balance);

mean6=mean(of lagb1-lagb6);
mean12=mean(of lagb1 - lagb12);

keep cid month balance mean6 mean12;
run;
proc print data=b;
run;


公司没有sas软件,所以也没有调试一下是否正确,不过我想思路应该就是这样的。
作者: shiyiming    时间: 2007-9-27 09:39
标题: to xsmile
这次程序的调试结果比上次更不理想。这个测试貌似简单,其实蛮费脑子的。
楼上的TX很不错,已经想到了lag和first两个要点了,不过离成功还差好多哦。“思路”和实现是两回事儿,希望有条件调试的TX都来参加测试。
凡是1小时之内能够完全实现的TX说明你的经验火候编程技巧都过关了;半小时之内实现,且代码不超过30行的TX可以说是SAS和业务双料高手了。
作者: shiyiming    时间: 2007-9-27 10:25
标题: Re: 练手系列2:移动平均计算
学习中~ 回家再修改,争取过关,呵呵
作者: shiyiming    时间: 2007-9-27 23:42
标题: Re: 练手系列2:移动平均计算
proc sort data = a;
by cid;
run;
data b;
set a;
cnt + 1;
by cid;
if first.cid then cnt = 1;
array lagb(12) lagb1 - lagb12;
i=1;
lagb1=lag(balance);
if(cnt=1)then lagb1=.;

do until (i&gt;11);
i+1;
lagb(i)=lag(lagb(i-1));
end;

j=12;
do until (j=0);
if(cnt&lt;j) then lagb(j)=.;
j=j-1;
end;
mean6=mean(of lagb1-lagb6);
mean12=mean(of lagb1 - lagb12);
keep cid month balance mean6 mean12;
run;
作者: shiyiming    时间: 2007-9-27 23:45
标题: Re: 练手系列2:移动平均计算
再简化的话就可以不用j,直接用i就可以了.
作者: shiyiming    时间: 2007-9-28 07:32
标题: to xsmile
强的!恭喜过关! <!-- s:D --><img src="{SMILIES_PATH}/icon_biggrin.gif" alt=":D" title="Very Happy" /><!-- s:D -->
作者: shiyiming    时间: 2007-9-28 20:40
标题: Re: 练手系列2:移动平均计算
[code:bf694]
proc delete data=_all_;run;
proc sort data=a;by cid;run;
%macro tt(g);
data b&amp;g;
set a(where=(cid=&amp;g));

lagb1=lag(balance);
lagb2 =lag2 (balance);
lagb3 =lag3 (balance);
lagb4 =lag4 (balance);
lagb5 =lag5 (balance);
lagb6 =lag6 (balance);
lagb7 =lag7 (balance);
lagb8 =lag8 (balance);
lagb9 =lag9 (balance);
lagb10=lag10(balance);
lagb11=lag11(balance);
lagb12=lag12(balance);

mean6=mean(of lagb1-lagb6);
mean12=mean(of lagb1 - lagb12);

keep cid month balance mean6 mean12;
run;

%mend;

data _null_;
set a;by cid;
if first&#46;cid then call execute('%tt('||cid||')');
run;

proc sql;
select count(distinct cid) into &#58;cnt from a;
quit;

%macro test;
%do j=1 %to &amp;cnt;
proc append base=b0 data=b&amp;j force;
run;
%end;
%mend;
%test
proc print data=b0;run;[/code:bf694]
作者: shiyiming    时间: 2007-9-29 12:00
标题: Re: 练手系列2:移动平均计算
比较复杂,看的不太懂,楼上的TX是把每个客户的数据单独处理后在merge一起的?
作者: shiyiming    时间: 2007-9-29 21:53
标题: Re: 练手系列2:移动平均计算
waterlwh的程序通俗易懂,就是每个客户单独处理,最后把结果摞起来。这样很容易维护,唯一的不足就是大数据量性能不佳,不可实际用于生产环境。还是要赞一个!
xsmile的答案我觉得基本是正解了。

我的参考程序供大家一起讨论,不知道还没有高手再改进一下:
[code:0d6e2]%let period=24;
data b(drop=i lastn1-lastn&amp;period);
set a;
retain lastn1-lastn&amp;period;
by cid;
array lastn{&amp;period};
do i=&amp;period to 2 by -1;
  lastn{i}=lastn{i-1};
end;
lastn{1}=lag(balance);
if first&#46;cid then do i=1 to &amp;period;
        lastn{i}=&#46;;
end;
mean6=mean(of lastn1-lastn6);
mean12=mean(of lastn1-lastn12);
mean24=mean(of lastn1-lastn24);
run;[/code:0d6e2]
作者: shiyiming    时间: 2007-10-1 09:30
标题: Re: 练手系列2:移动平均计算
简练~ 学习一下,还有其他练习题目吗?
作者: shiyiming    时间: 2007-10-9 19:00
标题: Re: 练手系列2:移动平均计算
如何获取下一行的数据呢,与lag功能相反?
作者: shiyiming    时间: 2007-10-18 10:30
标题: Re: 练手系列2:移动平均计算
这个问题简单了点,呵呵,用行号降序排列再用lag就是了
作者: shiyiming    时间: 2007-10-26 22:57
标题: Re: 练手系列2:移动平均计算
方法一:(此方法效率方面可能不是最优)
data tmpTable(drop=i);
        do i=1 to 12;
                do cid=1 to 2;
                        month=&quot;0&quot;;
                        balance=.;
                        output;
                end;
        end;
run;
data tmpTable;
        set a tmpTable;
run;
proc sort data=tmpTable;
        by cid month;
run;
data resultTable;
        set tmpTable;
        array a[*] b1-b12;
        a[1]=lag(balance);
                do i=2 to 12;
                     a[i]=lag(a[i-1]);
        end;
        if month=&quot;0&quot; then delete;
        mean6=mean(of b1-b6);
        mean12=mean(of b1-b12);
        drop b1-b12 i;
run;
作者: shiyiming    时间: 2007-11-9 17:25
标题: Re: 练手系列2:移动平均计算
傻办法,创建12张表作left join。就是冗余太厉害。
作者: shiyiming    时间: 2008-7-15 17:16
标题: Re: 练手系列2:移动平均计算
data test(keep=cid month mean6 mean12);
set a;
by cid;
array lag_baln(12) lag_baln1-lag_baln12;
retain lag_baln(12);
do i=12 to 2 by -1;
   lag_baln(i)=lag_baln(i-1);
end;
lag_baln(1)=lag(balance);
if first.cid then
do i=1 to 12;
  lag_baln(i)=.;
end;
mean6=mean(of lag_baln1-lag_baln6);
mean12=mean(of lag_baln1-lag_baln12);
run;
proc print data=test;
run;
作者: shiyiming    时间: 2008-7-15 17:25
标题: Re: 练手系列2:移动平均计算
data u1 u2;
set a;
if cid = 1 then output u1;
if cid = 2 then output u2;
run;
%MACRO Blag(user);
set u&amp;user;
%do i=1 to 12;
b&amp;i=lag&amp;i(balance);
%end;
%MEND;
data b1(keep = cid month balance mean6 mean12);
set u1;
by cid;
%Blag(1);
mean6 = mean (of b1-b6);
mean12 = mean (of b1-b12);
run;
data b2(keep = cid month balance mean6 mean12);
set u2;
by cid;
%Blag(2);
mean6 = mean (of b1-b6);
mean12 = mean (of b1-b12);
run;
data result;
   set b1 b2;
run;
proc print data = result;
run;
这道题采用分治id1和id2,然后把处理结果再合并的方法最不费脑子,但是实际用户较多的话可能会在性能方面打折扣。
作者: shiyiming    时间: 2008-7-24 23:01
标题: Re: 练手系列2:移动平均计算
我的答案 <!-- s:D --><img src="{SMILIES_PATH}/icon_biggrin.gif" alt=":D" title="Very Happy" /><!-- s:D -->  <!-- s:lol: --><img src="{SMILIES_PATH}/icon_lol.gif" alt=":lol:" title="Laughing" /><!-- s:lol: -->
data a;
infile datalines;
input cid  month $ balance;
datalines;
1 200001 1
1 200002 1
1 200003 2
1 200004 .
1 200005 1
1 200006 1
1 200007 1
1 200008 1
1 200009 1
1 200010 1
1 200011 1
1 200012 1
1 200101 1
1 200102 1
1 200103 1
1 200104 1
1 200105 1
1 200106 1
1 200107 1
1 200108 1
1 200109 1
2 200001 1
2 200002 1
2 200003 2
2 200004 .
2 200005 1
2 200006 1
2 200007 1
2 200008 1
2 200009 1
;
data result(keep=x1 x2);
set a;
by cid  ;
retain total1 0;
retain total2 0;
retain totalmiss 0;
retain miss1 0;
retain miss2 0;
if first.cid  then do; totalmiss=0; total1=0; total2=0 ;count=0;miss1=0;miss2=0;end;
count+1;
if balance&gt;0 then  miss=1;else  miss=0;
if miss1&gt;0 then x1=total1/miss1 ;
if miss2&gt;0 then x2=total2/miss2;
totalmiss=totalmiss+miss;
lag6=lag6(totalmiss);
lag12=lag12(totalmiss);
lag66=lag6(balance);
lag1212=lag12(balance);
if balance&gt;0 then do;

if count gt 6 then do;
total1=sum(total1,balance,-lag66) ;
miss1=sum(totalmiss,-lag6);end;
else  do; total1=total1+balance;miss1=totalmiss; end;
if count gt 12 then do;
total2=sum(total2,balance,-lag1212);
miss2=sum(totalmiss,-lag12); end;
else  do ; miss2=totalmiss; total2=total2+balance;end;
end;
else do;

if count gt 6 then do;
total1=sum(total1,-lag66) ;
miss1=sum(totalmiss,-lag6);end;
else  do ;miss1=totalmiss; end;
if count gt 12 then do;
total2=sum(total2,-lag1212);
miss2=sum(totalmiss,-lag12); end;
else  do ;miss2=totalmiss;end;
end;

run;
proc print data=result;
run;
作者: shiyiming    时间: 2008-9-26 14:06
标题: Re: 练手系列2:移动平均计算
通过最近努力学习,发现将retain和lag结合计算移动平均,效率最高,程序也容易理解,缺点是会损失一行数据。
proc sort data = a;
by cid;
run;

data a1;
set a;
retain sum_balance 0;
sum_balance=sum_balance+balance;
mean6=(sum_balance-lag6(sum_balance))/6;
mean12=(sum_balance-lag12(sum_balance))/12;
run;
作者: shiyiming    时间: 2008-9-27 15:43
标题: Re: 练手系列2:移动平均计算
[code:thmsu4t3]proc sort data=a;
     by cid month;
run;

data a;
     set a;
         by cid month;
         if first&#46;cid then num=0;
         num+1;
run;

data avg1;
     input avg1 8&#46;3;
         cards;
run;

data avg2;
     input avg2 8&#46;3;
         cards;
run;

data aa;
     set a;
         call execute(&quot;data temp;
                   set a; where cid=&quot;||cid||&quot; and num&lt;&quot;||num||&quot; and num&gt;=&quot;||num||&quot;-6;
                   run;
                   proc means data=temp noprint;
                                        var balance; output out=tmp mean=avg1; run;
                   data avg1; set avg1 tmp;  run;&quot;);
run;

data aa;
     set a;
         call execute(&quot;data temp;
                   set a; where cid=&quot;||cid||&quot; and num&lt;&quot;||num||&quot; and num&gt;=&quot;||num||&quot;-12;
                   run;
                   proc means data=temp noprint;
                                        var balance; output out=tmp mean=avg2; run;
                   data avg2; set avg2 tmp;  run;&quot;);
run;

data avg;
     set a;
         by cid month;
         if first&#46;cid then delete;
run;

data avg;
     merge avg avg1 avg2;
         label avg1='6 month mean'
               avg2='12 month mean';
        keep cid month avg1 avg2;
run;

data final;
     merge a avg;
         by cid month;
         drop num;
run;[/code:thmsu4t3]
作者: shiyiming    时间: 2010-12-19 16:21
标题: Re: 练手系列2:移动平均计算
很有趣,这个是我常常问的面试题,不过不止计算移动平均这么简单。

2008年的帖子,居然今天才看到,算是挖坟行为 <!-- s8) --><img src="{SMILIES_PATH}/icon_cool.gif" alt="8)" title="Cool" /><!-- s8) -->

我面试的时候常常问:从1--10,10算最好, 你自己觉得自己的SAS编程水平几分?或者是不是常常用SAS做时间序列分析?凡是回答是,或者答5-6的我就问这种移动平均,答7-8的我就问移动相关系数,答9以上的我就问以变量值为基准的移动窗口统计量计算。目前为止遇到的能答上来的回答都跟大家一样,硬算!

[code:bwp97i7i]
data a;
infile datalines;
input cid month yymmn6&#46; balance;
format month yymm&#46;;
datalines;
1 200001 1
1 200002 1
1 200003 2
1 200004 &#46;
1 200005 1
1 200006 1
1 200007 1
1 200008 1
1 200009 1
1 200010 1
1 200011 1
1 200012 1
1 200101 1
1 200102 1
1 200103 1
1 200104 1
1 200105 1
1 200106 1
1 200107 1
1 200108 1
1 200109 1
2 200001 1
2 200002 1
2 200003 2
2 200004 &#46;
2 200005 1
2 200006 1
2 200007 1
2 200008 1
2 200009 1
;
run;


data av/view=av;
        set a; by cid;
        b_lag=lag(balance);
        if first&#46;cid then b_lag=&#46;;               
run;

proc expand data=av   out=b  method=none;
       by cid;          
       convert b_lag=mean6 / transformout=(movave 6  );
       convert b_lag=mean12/transformout=(movave 12 );
run;

data _null_;
        set b;
                put @1 cid= @10 month=  @25 balance=  @40 mean6= 7&#46;5  @60 mean12= 7&#46;5;;
run;
[/code:bwp97i7i]
作者: shiyiming    时间: 2010-12-19 19:16
标题: Re: 练手系列2:移动平均计算
我通常只敢说自己的SAS Programming 3-4分,只能应付工作需求。很多人都自信满满的回答我:“我在我们组里就是最强的,可以打9分吧!”。估计是谦虚了一下,没说满10分。结果问了2个问题,再让他给自己打分,“嗯~~,不好意思,我想只能算5分吧”。其实还高了。 <!-- s:D --><img src="{SMILIES_PATH}/icon_biggrin.gif" alt=":D" title="Very Happy" /><!-- s:D -->
作者: shiyiming    时间: 2010-12-20 04:15
标题: Re: 练手系列2:移动平均计算
你老人家都只有3--4,一般的岂不是只能给自己打一分?还要不要人家活了
作者: shiyiming    时间: 2010-12-25 13:12
标题: Re: 练手系列2:移动平均计算
若是sas的话:一个proc expand就能解决的问题.当然前面的data步也是相当的perfect.
若是oracle比较高的版本的话:可以用avg+开窗函数就可解决.
作者: shiyiming    时间: 2011-4-25 19:23
标题: Re: 练手系列2:移动平均计算
华山论剑?
小弟学习了,正好找到一篇有点相关性的文章:《Easy Rolling Statistics with PROC EXPAND》
<!-- m --><a class="postlink" href="http://www2.sas.com/proceedings/forum2008/093-2008.pdf">http://www2.sas.com/proceedings/forum2008/093-2008.pdf</a><!-- m -->
作者: shiyiming    时间: 2011-4-25 21:54
标题: Re: 练手系列2:移动平均计算
刚时成功,最笨的方法来了,呵呵!!

[code:3s74yr8m]data a;
infile datalines;
input cid month $ balance;
datalines;
1 200001 1
1 200002 1
1 200003 2
1 200004 &#46;
1 200005 1
1 200006 1
1 200007 1
1 200008 1
1 200009 1
1 200010 1
1 200011 1
1 200012 1
1 200101 1
1 200102 1
1 200103 1
1 200104 1
1 200105 1
1 200106 1
1 200107 1
1 200108 1
1 200109 1
2 200001 1
2 200002 1
2 200003 2
2 200004 &#46;
2 200005 1
2 200006 1
2 200007 1
2 200008 1
2 200009 1
;
run;

proc sort data=a;
by cid month;
run;
data b;
  set a;
  by cid;
  retain total;
  if first&#46;cid=1 then do;
     total=0;
        end;
  if balance ne &#46; then total=total+balance;
run;
data c;
  set b;
  by cid;
  if first&#46;cid=1 then do;
    mv6=0;
        mv12=0;
        retain i k 0;
        i=1;
   end;
   if i&lt;12 and balance=&#46; then k=k+1;
   if first&#46;cid ne 1 then do;
     if i&lt;6 then do;
           mv6=lag(total)/(i-k);
          end;
          else do;
          mv6=mean(lag1(balance),lag2(balance),lag3(balance),lag4(balance),lag5(balance),lag6(balance));
          end;
          if i&lt;12 then do;
            mv12=lag(total)/(i-k);
                end;
          else do;
          mv12=mean(lag1(balance),lag2(balance),lag3(balance),lag4(balance),lag5(balance),
lag6(balance),lag7(balance),lag8(balance),lag9(balance),lag10(balance),lag11(balance),lag12(balance));
      end;
i=i+1;
        end;
        if last&#46;cid=1 then k=0;
run;
   




[/code:3s74yr8m]
作者: shiyiming    时间: 2011-4-25 22:57
标题: Re: 练手系列2:移动平均计算
[quote=&quot;天性爱好者&quot;:1henghux]刚时成功,最笨的方法来了,呵呵!!
[/quote:1henghux]
看了大家的后,才知道,好像自己弄错了呀,弄得太急了!!




欢迎光临 SAS中文论坛 (http://mysas.net/forum/) Powered by Discuz! X3.2