여기에 해당
data want2;
set have;
format sum_usage2 best.;
if _n_=1 then do;
%create_hash(lk,id opt t_purchase t_spent, sum_usage2,"sums");
end;
rc = lk.find();
drop rc;
run;
%create_hash()
매크로 초 각 레코드에 대해 3 시간 동안 모든 해시 조회를 수행하여 롤링 합계를 계산 한 당신의 이전 사람의 모프입니다 귀하의 데이터 세트에. 바라건대 당신은 그 접근 방식의 단순함이 레코드 당 3 * 3600 해시 룩업과 커다란 데이터 벡터를 해쉬에로드하는 데 드는 비용이 크다는 것을 알았기를 바랍니다.
제시된 시간 로그 데이터에는 데이터의 맨 위에 삽입 된 새로운 레코드가 있으며 시간이지나면서 단조로운 데이터로 추정됩니다.
데이터 스텝은 단조로운 데이터의 단일 패스에서 시간 창에 대한 롤링 합계를 계산할 수 있습니다. 이 기법은 '반지'배열을 사용하며, 인덱스 진입은 모듈로 조정됩니다. 하나의 배열은 시간에 대한 것이고 다른 하나는 메트릭 (용도)에 대한 것입니다. 필요한 배열 크기는 시간 창 내에서 발생할 수있는 최대 항목 수입니다.
1, 2 시간 간격으로 일부 발생하는 샘플 데이터가 있다면, 200 초 하나 개 점프 :
data have;
time = '12oct2017:11:22:32'dt;
usage = 0;
do _n_ = 1 to &have_count;
time + 2; *ceil(25*ranuni(123));
if _n_ > 30 then time + -1;
if _n_ = 145 then time + 200;
usage = floor(180*ranuni(123));
delta = time-lag(time);
output;
end;
run;
시작 시간 오름차순 정렬 전 항목에서 롤링 합을 계산하는 경우에.(내림차순의 경우) : 16 및 TIME_WINDOW
은 12 초입니다.
%let RING_SIZE = 16;
%let TIME_WINDOW = '00:00:12't;
data want;
array ring_usage [0:%eval(&RING_SIZE-1)] _temporary_ (&RING_SIZE*0);
array ring_time [0:%eval(&RING_SIZE-1)] _temporary_ (&RING_SIZE*0);
retain ring_tail 0 ring_head -1 span 0 span_usage 0;
set have;
by time ; * cause error if data not sorted per algorithm requirement;
* unload from accumulated usage the tail items that fell out the window;
do while (span and time - ring_time(ring_tail) > &TIME_WINDOW);
span + -1;
span_usage + -ring_usage(ring_tail);
ring_tail = mod (ring_tail + 1, &RING_SIZE) ;
end;
ring_head = mod (ring_head + 1, &RING_SIZE);
span + 1;
if span > 1 and (ring_head = ring_tail) then do;
_n_ = dim(ring_time);
put 'ERROR: Ring array too small, size=' _n_;
abort cancel;
end;
* update the ring array;
ring_time(ring_head) = time;
ring_usage(ring_head) = usage;
span_usage + usage;
drop ring_tail ring_head span;
run;
데이터가 내림차순으로 정렬 된 경우 물건을 흔들 수 있습니다. 오름차순 정렬, 롤링 및 리조트 내림차순 계산
그런 지글 거리는 짓을 할 수 없거나 한 번만 할까?
롤링 계산에 포함될 항목은 '리드'행 또는 SET을 통해 아직 읽지 않은 행이어야합니다. 이것이 어떻게 가능한지 ? 두 번째 SET 문을 사용하여 데이터 세트에 대한 별도의 채널을 열고 따라서 리드 값을 얻을 수 있습니다.
리드 데이터를 처리하는 데 더 많은 부기가 있습니다. 데이터 끝 부분의 조기 덮어 쓰기 및 감소 된 창을 처리해야합니다. I는 SQL 대 링 어레이 벤치마킹하지
proc sort data=want2; by time;
run;
proc compare noprint data=want compare=want2 out=diff outnoequal;
id time;
var span_usage;
run;
---------- LOG ----------
NOTE: There were 150 observations read from the data set WORK.WANT.
NOTE: There were 150 observations read from the data set WORK.WANT2.
NOTE: The data set WORK.DIFF has 0 observations and 4 variables.
해시 대 EXPAND PROC 대 :
data want2;
array ring_usage [-1:%eval(&RING_SIZE-1)] _temporary_;
array ring_time [-1:%eval(&RING_SIZE-1)] _temporary_;
retain lead_index 0 ring_tail -1 ring_head -1 span 1 span_usage . guard_index .;
set have;
&debug put/_N_ ':' time= ring_head=;
* unload ring_head slotted item from sum;
span + -1;
span_usage + -ring_usage(ring_head);
* advance ring_head slot by 1, the vacated slot will be overwritten by lead;
ring_head = mod (ring_head + 1, &RING_SIZE);
&debug put +2 ring_time(ring_head)= span= 'head';
* load ring with lead values via a second SET of the same data;
if not end2 then do;
do until (_n_ > 1 or lead_index = 0 or end2);
set have(keep=time usage rename=(time=t usage=u)) end=end2; * <--- the second SET ;
if end2 then guard_index = lead_index;
&debug if end2 then put guard_index=;
ring_time(lead_index) = t;
ring_usage(lead_index) = u;
&debug put +2 ring_time(lead_index)= 'lead';
lead_index = mod (lead_index + 1, &RING_SIZE);
end;
end;
* advance ring_tail to cover the time window;
if ring_tail ne guard_index then do;
ring_tail_was = ring_tail;
ring_tail = mod (ring_tail + 1, &RING_SIZE) ;
do while (time - ring_time(ring_tail) <= &TIME_WINDOW);
span + 1;
span_usage + ring_usage(ring_tail);
&debug put +2 ring_time(ring_tail)= span= 'seek';
ring_tail_was = ring_tail;
ring_tail = mod (ring_tail + 1, &RING_SIZE) ;
if ring_tail_was = guard_index then leave;
if span > 1 and (ring_head = ring_tail) then do;
_n_ = dim(ring_time);
put 'ERROR: Ring array too small, size=' _n_;
abort cancel;
end;
end;
* seek went beyond window, back tail off to prior index;
ring_tail = ring_tail_was;
end;
&debug put +2 ring_time(ring_tail)= span= 'mark';
drop lead_index t u ring_: guard_index span;
format ring: span: usage 6.;
run;
options source;
확인 두 방법 모두 동일한 연산이있다.
주의 : + in 및 -out 연산을 사용하여 롤링 값을 추측 할 때 정수가 아닌 값을 처리 할 때 반올림 오류가 발생할 수 있습니다.
나는 포함 할 논리를 이해하는 데 어려움을 겪고 있습니다. 왜 총 사용량 21의 4 번째'id = a' 값입니까? 조금만 더 설명해주세요. – DomPazz
'intck' 함수가 작동하면't_purchase'와't_spent' 사이의 초 수를 반환합니다. datetime 값은 01-01-1960 이후 초 단위로 저장되기 때문에't_spent-t_purchase'를 쓰는 것과 같습니다. 't_purchase = '01JAN2017 : 00 : 00 : 00'dt'이고't_spent ='01JAN2017 : 00 : 00 : 01'dt'라고 가정 해 봅시다. 본질적으로 1에서 1798848001로 반복하고 있습니다. 확실하지 않습니다. 이것이 당신이하고자하는 일입니다. – user2877959
또한'find' 해시 테이블 호출이 나머지 코드와 일치하지 않는 것 같습니다. 이 문맥에 존재하지 않는 것처럼 보이는'user'와'id_option' 변수를 refferening하고 있습니다. – user2877959