ここにあり
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時間にわたって、すべてのハッシュ検索を行うことにより、あなたはローリング合計を計算する1つの以前のもののモーフですあなたのデータセットにうまくいけば、そのアプローチのシンプルさは、レコードあたり3 * 3600のハッシュ・ルックアップの大きなコストと、データ・ベクトル全体をハッシュにロードする必要があることに気付きました。
表示される時間ログデータは、データの先頭に新しいレコードが挿入されており、データが時間的に単調減少すると推定します。
データステップは、単調なデータの1回のパスで、時間枠にわたってローリング合計を計算できます。この技法では、屈折率の進んだ位置がモジュラスで調整される 'リング'配列が使用されます。 1つの配列は時間のためのものであり、もう1つはメトリック(使用法)のものです。必要な配列サイズは、時間枠内で発生する可能性がある最大項目数です。
は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;
スタート時間の昇順にソートされた前の項目からローリング合計を計算する場合と。(降順の場合が続く):RING_SIZE
16およびTIME_WINDOW
は12秒である。RING_SIZE
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;
降順でソートされたデータの場合は、物事を揺るがす可能性があります。昇順に並べ替え、ローリングとリゾート降順を計算します。
このようなジグルを行うことができない場合や、1回のパスをしたい場合はどうすればよいですか?
ローリング計算の対象となるアイテムは、「リード」行またはSETを介してまだ読み取られていない行からのものでなければなりません。これはどのように可能ですか? 2番目のSETステートメントを使用して、データセットに対する別のチャネルを開き、リード値を取得することができます。
鉛データを処理するための簿記がもう少しあります - データの最後に早すぎる上書きと縮小されたウィンドウを処理する必要があります。私は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'という変数をrefferenしています。 – user2877959