我有一个类似下面的数据集,我正在尝试运行总计的事件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;
运行;
%好转;
%历史;
答案 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可能会更快,但您必须决定可理解性/性能的权衡取舍。