1

我是一名 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 子句?感谢您的想法和帮助!

此致

4

4 回答 4

5

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

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

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

于 2013-12-03T15:29:04.707 回答
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 文档。

于 2013-12-02T22:45:08.927 回答
1

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

于 2013-12-02T20:05:14.600 回答
1

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

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;
于 2013-12-03T16:28:41.843 回答