如何计算条件累积和

时间:2014-01-24 22:32:04

标签: macros sas do-loops datastep

我有一个类似下面的数据集,我正在尝试运行总计的事件2和3,稍微扭曲。我只想在Event_1_dt小于当前记录中的日期时计算这些事件。我目前正在使用宏%do循环遍历该项类型的每个记录。虽然这产生了期望的结果,但性能比期望的慢。每个Item_Type最多可包含1250条记录,并且有几千种类型。是否可以在循环所有1250次迭代之前退出循环?我对尝试加入犹豫不决,因为有大约30多个事件需要计算,但我愿意接受建议。另一个复杂因素是即使Event_1_dt总是大于Date,也没有任何其他限制。

Item_Type   Date        Event_1_dt  Event_2_flg Event_3Flg  Desired_Event_2_Cnt Desired_Event_3_Cnt
   A        1/1/2014    1/2/2014    1           1           0                   0
   A        1/2/2014    1/2/2014    0           1           0                   0
   A        1/3/2014    1/8/2014    1           0           1                   2
   B        1/1/2014    1/2/2014    1           0           0                   0
   B        1/2/2014    1/5/2014    1           0           0                   0
   B        1/3/2014    1/4/2014    1           1           1                   0
   B        1/4/2014    1/5/2014    0           1           1                   0
   B        1/5/2014    .           1           1           2                   1
   B        1/6/2014    1/7/2014    1           1           3                   2

对应代码:

%macro History;
data y;
set x;
Event_1_Cnt = 0;
Event_2_Cnt = 0;
%do i = 1 %to 1250;
    lag_Item_Type = lag&i(Item_Type);
    lag_Event_2_flg = lag&i(Event_2_flg);
    lag_Event_3_flg = lag&i(Event_3_flg);
    lag_Event_1_dt = lag&i(Event_1_dt);

    if Item_Type = lag_Item_Type and lag_Event_1_dt > . and lag_Event_1_dt < Date then do;

        if lag_Event_2_flg  = 1 then do;
            Event_2_Cnt = Event_2_cnt + 1;
        end;

        if lag_Event_3_flg = 1 then do;
            Event_3_Cnt = Event_3_cnt + 1;
        end;

    end;
%end;

运行;

%好转;

%历史;

2 个答案:

答案 0 :(得分:2)

嗯,这对SAS来说不是一项微不足道的任务,但它仍然可以在一个DATA步骤中解决,而不需要合并。您可以使用哈希对象。这个想法如下。 在每个项目类型中,按记录记录,我们将事件标记“收集”到哈希对象中的“箱”中,其中每个箱是特定日期。所有垃圾箱按日期按升序排序。同时,我们将当前记录的日期插入到相同的散列中(按日期分配到相应的位置),然后从这个地方迭代“向上”,总结由此时刻收集的所有时间段(将具有日期&lt;然后日期当前记录,因为我们上升了。)

以下是代码:

data have;
    informat Item_Type $1. Date Event_1_dt mmddyy9. Event_2_flg Event_3_flg 8.; 
    infile datalines dsd dlm=',';
    format Date Event_1_dt date9.;
    input Item_Type Date Event_1_dt Event_2_flg Event_3_flg;
datalines;
A,1/1/2014,1/2/2014,1,1
A,1/2/2014,1/2/2014,0,1
A,1/3/2014,1/8/2014,1,0
B,1/1/2014,1/2/2014,1,0
B,1/2/2014,1/5/2014,1,0
B,1/3/2014,1/4/2014,1,1
B,1/4/2014,1/5/2014,0,1
B,1/5/2014,,1,1
B,1/6/2014,1/7/2014,1,1
;
run;
proc sort data=have; by Item_Type; run;

data want;
    set have;
    by Item_Type;
    if _N_=1 then do;
        declare hash h(ordered:'a');
        h.defineKey('Event_date','type');
        h.defineData('event2_cnt','event3_cnt');
        h.defineDone();
        declare hiter hi('h');
    end;

    /*for each new Item_type we clear the hash completely*/

    if FIRST.Item_Type then h.clear();

    /*now if date of Event 1 exists we put it into corresponding   */
    /* (by date) place of our ordered hash. If such date is already*/
    /*in the hash, we increase number of events for this date      */
    /*adding values of Event2 and Event3 flags. If no - just assign*/
    /*current values of these flags.*/

    if not missing(Event_1_dt) then do;
        Event_date=Event_1_dt;type=1;
        rc=h.find();
        event2_cnt=coalesce(event2_cnt,0)+Event_2_flg;
        event3_cnt=coalesce(event3_cnt,0)+Event_3_flg;
        h.replace();
    end;

    /*now we insert Date of the record into the same oredered hash,*/
    /*making type=0 to differ this item from items where date means*/
    /*date of Event1 (not date of record)*/

    Event_date=Date;
    event2_cnt=0; event3_cnt=0; type=0;
    h.replace();
    Desired_Event_2_Cnt=0;
    Desired_Event_3_Cnt=0;

    /*now we iterate 'up' from just inserted item, i.e. looping     */
    /*through all items that have date < the date of the record.    */
    /*Items with date = the date of the record will be 'below' since*/
    /*they have type=1 and our hash is ordered by dates first, and  */
    /*types afterwards (1's will be below 0's)*/

    hi.setcur(key:Date,key:0);
    rc=hi.prev();
    do while(rc=0);
        Desired_Event_2_Cnt+event2_cnt;
        Desired_Event_3_Cnt+event3_cnt;
        rc=hi.prev();
    end;
    drop Event_date type rc  event2_cnt event3_cnt;
run;

我无法用你的实际行数来测试它,但我相信它应该非常快,因为我们只通过一个小的哈希对象循环,它完全在内存中,我们只为每个哈希对象做了很多循环必要时记录(仅限早期事件)并且不进行任何IF检查。

答案 1 :(得分:0)

我不认为哈希是必要的 - 似乎一个简单的数据步骤就可以解决问题。这可能会阻止您(或遇到代码的下一位程序员)需要“重新阅读并进行研究”才能理解它。

我认为以下内容可行:

data have;
    informat Item_Type $1. Date Event_1_dt mmddyy9. Event_2_flg Event_3_flg 8.; 
    infile datalines dsd dlm=',';
    format Date Event_1_dt date9.;
    input Item_Type Date Event_1_dt Event_2_flg Event_3_flg;
datalines;
A,1/1/2014,1/2/2014,1,1
A,1/2/2014,1/2/2014,0,1
A,1/3/2014,1/8/2014,1,0
B,1/1/2014,1/2/2014,1,0
B,1/2/2014,1/5/2014,1,0
B,1/3/2014,1/4/2014,1,1
B,1/4/2014,1/5/2014,0,1
B,1/5/2014,,1,1
B,1/6/2014,1/7/2014,1,1
;



data want2 (drop=_: );
    set have; 
    by ITEM_Type;

    length _Alldts_event2 _Alldts_event3 $20000;
    retain _Alldts_event2 _Alldts_event3;

    *Clear _ALLDTS for each ITEM_TYPE;
    if first.ITEM_type then Do;
        _Alldts_event2 = "";
        _Alldts_event3 = "";
    END;

    *If event is flagged, concatenate the Event_1_dt to the ALLDTS variable;
    if event_2_flg = 1 Then _Alldts_event2 = catx(" ", _Alldts_event2,Event_1_dt);
    if event_3_flg = 1 Then _Alldts_event3 = catx(" ", _Alldts_event3,Event_1_dt);
    _numWords2 = COUNTW(_Alldts_event2); 
    _numWords3 = COUNTW(_Alldts_event3);

    *Loop through alldates, count the number that are < the current records date;
    cnt2=0;
    do _i = 1 to _NumWords2;
        _tempDate =  input(scan(_Alldts_event2,_i),Best12.);
        if _tempDate < date Then cnt2=cnt2+1;
    end;

    cnt3=0;
    do _i = 1 to _NumWords3;
        _tempDate =  input(scan(_Alldts_event3,_i),Best12.);
        if _tempDate < date Then cnt3=cnt3+1;
    end;
run;

我相信Hash可能会更快,但您必须决定可理解性/性能的权衡取舍。