【问题标题】:sas - calculate moving average for grouped data with BY statementsas - 使用 BY 语句计算分组数据的移动平均值
【发布时间】:2013-12-18 15:44:56
【问题描述】:

我是一名 SAS 初学者,我很好奇以下任务是否可以像目前在我脑海中那样简单得多。

我在名为 user_date_money 的表中有以下(简化的)元数据:

用户 - 日期 - 金钱

每个日历日(过去 4 年)都有不同的用户和日期。数据按用户 ASC 和日期 ASC 排序,示例数据如下所示:

User  | Date     | Money
Anna   23.10.2013   5
Anna   24.10.2013   1
Anna   25.10.2013   12
      ....       
Aron   23.10.2013   5
Aron   24.10.2013   12
Aron   25.10.2013   4 
     ....
Zoe    23.10.2013   1
Zoe    24.10.2013   1
Zoe    25.10.2013   0

我现在想计算货币的五天移动平均线。我从非常流行的带有 lag() 函数的方法开始,如下所示:

data cma; 
set user_date_money;
if missing(money) then
do;
OBS = 0;
money = 0.0;
end;
else OBS = 1;
money5 = lag5(money);
OBS5= lag5(obs);
if missing(money5) then money5= 0.0;
if missing(obs5) then obs5= 0;

if _N_ = 1 then
do;
SUM = 0.0;
N = 0;
end;
else;
sum = sum + money-money5;
n = n + obs-obs5;
MEAN = sum / n ;
retain sum n;
run;

如您所见,如果数据步骤遇到新用户,则会出现此方法的问题。 Aron 会从 Anna 那里得到一些滞后值,这当然不应该发生。

现在我的问题:我很确定您可以通过添加一些额外的字段(如 laaggeduser)来处理用户切换,如果您注意到这样的切换,则可以通过重置 N、Sum 和 Mean 变量但是:

这可以更简单的方式完成吗?也许以任何方式使用 BY 子句? 感谢您的想法和帮助!

最好的问候

【问题讨论】:

    标签: sql sas datastep


    【解决方案1】:

    我认为最简单的方法是使用 PROC EXPAND:

    PROC EXPAND data=user_date_money out=cma;
      ID date;
      BY user;
      CONVERT money=MEAN / transformin=(setmiss 0) transformout=(movave 5);
    RUN;
    

    正如 John 的评论中提到的,重要的是要记住缺失值(以及关于开始和结束观察)。我已在代码中添加了 SETMISS 选项,因为您明确表示要“清零”缺失值,而不是忽略它们(默认 MOVAVE 行为)。 如果您想排除每个用户的前 4 个观察值(因为他们没有足够的历史前史来计算移动平均值 5),您可以在 TRANSFORMOUT=() 中使用选项“TRIMLEFT 4”。

    【讨论】:

    • 不错的解决方案,但用户应确保了解 proc expand 对缺失值和移动平均值的默认处理,在每个变量的前几次观察中,以确保移动平均值计算以所需的方式完成。
    • 你好 Dmitry,这个解决方案是我最喜欢的 - 优雅,只有 5 行,它完全符合它应该做的事情。虽然我需要更深入地研究 proc expand 的技术,但这是一个很好的起点!谢谢!
    【解决方案2】:

    如果您的特定需求足够简单,您可以使用 PROC MEANS 和多标签格式进行计算。

    data mydata;
    do id = 1 to 5;
      datevar = '01JAN2010'd-1;
      do month = 0 to 4;
        datevar=intnx('MONTH',datevar,1,'b');
        sales = floor(500*rand('normal',7))+1500;
        output;
      end;
    end;
    run;
    
    proc format;
    value movingavg (multilabel notsorted)
    '01JAN2010'd-'31MAR2010'd = 'JAN-MAR 2010'
    '01FEB2010'd-'30APR2010'd = 'FEB-APR 2010'
    '01MAR2010'd-'31MAY2010'd = 'MAR-MAY 2010'
    /* ... more of these ... */
    ;
    quit;
    
    proc means data=mydata;
    class id datevar/mlf order=data;
    types id*datevar;
    format datevar movingavg.;
    var sales;
    run;
    

    PROC FORMAT 可以通过使用 CNTLIN 数据集以编程方式完成,有关更多信息,请参阅 PROC FORMAT 的 SAS 文档。

    【讨论】:

    • 你将如何使用它来计算 4 年的 5 天移动平均线?在这种情况下,您的方法似乎需要做很多工作。
    • 您可以通过编程轻松地制作 CNTLIN 数据集;它只需要每天一行/分组组合。
    【解决方案3】:

    如果您确保您的数据已排序,您可以使用firstlast 命名变量在您获得新成员时初始化您的运行总计。这些和retain 应该可以满足您的需求;我不认为这里真的需要lag()

    【讨论】:

      【解决方案4】:

      是的,您可以按分组使用。首先,您将按用户和日期排序(正如您已经拥有的那样)。

      proc sort data=user_date_money;
          by user date;
      run;
      

      然后,使用 by 变量和计数器重做数据步骤。

      data cma;
          set user_date_money;
          by user;
      
          length User_Recs 3
                  Average 8;
      
          retain User_Recs;
      
          if First.User=1 then User_Recs=0;
      
          User_Recs=User_Recs+1;
      
          if User_Recs>4 then do;
              Average=(lag4(money)+lag3(money)+lag2(money)+lag1(money)+money)/5;
          end;
      
          drop User_Recs;
      run;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-16
        • 1970-01-01
        • 2020-06-26
        • 2018-06-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多