2017-11-29 3 views
0

私には1つの問題があり、正しく動作するように修正することはあまりありません。 私は(所望の出力列「sum_usage」で)テーブルを持っている:SASの時間間隔で1つの列のローリング合計を計算する

id opt t_purchase   t_spent  bonus usage sum_usage 
a 1 10NOV2017:12:02:00 10NOV2017:14:05:00 100  9  15 
a 1 10NOV2017:12:02:00 10NOV2017:15:07:33 100  0  15 
a 1 10NOV2017:12:02:00 10NOV2017:13:24:50 100  6  6 
b 1 10NOV2017:13:54:00 10NOV2017:14:02:58 100  3  10 
a 1 10NOV2017:12:02:00 10NOV2017:20:22:07 100 12  27 
b 1 10NOV2017:13:54:00 10NOV2017:13:57:12 100  7 .  7 

だから、私はtime_purchaseからのすべての使用値を合計する必要がありますが(1つのID、オプト組み合わせ(ID、OPTによってグループ)のためだけのものがあります一意のtime_purchase)をt_spentに設定します。 また、私はmilionの行を持っているので、ハッシュテーブルが最適なソリューションになります。私が試してみた:

data want; 
if _n_=1 then do; 
    if 0 then set have(rename=(usage=_usage)); 
    declare hash h(dataset:'have(rename=(usage=_usage))',hashexp:20); 
    h.definekey('id','opt', 't_purchase', 't_spent'); 
    h.definedata('_usage'); 
    h.definedone(); 
end; 
set have; 
sum_usage=0; 
do i=intck('second', t_purchase, t_spent) to t_spent ; 
if h.find(key:user,key:id_option,key:i)=0 then sum_usage+_usage; 
end; 
drop _usage i; 
run; 

下から5行目は(do i=intck('second', t_purchase, t_spent)確かに正しいではありませんが、これにアプローチする方法は考えています。だから、主な問題は、これを計算するために時間間隔を設定する方法です。私はすでに同じキーでこのハッシュテーブルfuncの1つの関数を持っていますが、時間間隔はありませんので、これも書き込むのはかなり良いでしょうが、それは必要ではありません。

+0

私は含まれるロジックを理解するのに苦労しています。なぜ合計使用量21の4番目の 'id = a'値ですか?もう少し詳しく説明してください。 – DomPazz

+0

'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

+0

また、あなたの 'find'ハッシュテーブル呼び出しはあなたのコードの残りの部分に対応していないようです。このコンテキストでは存在しないような、 'user'と' id_option'という変数をrefferenしています。 – user2877959

答えて

1

個人的には、私はハッシュを捨ててSQLを使用します。

例データ:

data have; 

input id $ opt  
    t_purchase datetime20. 
    t_spent  datetime20. 
    bonus usage sum_usage; 

format 
    t_purchase datetime20. 
    t_spent  datetime20.; 

datalines; 
a 1 10NOV2017:12:02:00 10NOV2017:14:05:00 100  9  15 
a 1 10NOV2017:12:02:00 10NOV2017:15:07:33 100  0  15 
a 1 10NOV2017:12:02:00 10NOV2017:13:24:50 100  6  6 
b 1 10NOV2017:13:54:00 10NOV2017:14:02:58 100  3  10 
a 1 10NOV2017:12:02:00 10NOV2017:20:22:07 100 12  27 
b 1 10NOV2017:13:54:00 10NOV2017:13:57:12 100  7  7 
; 

私は比較のためにここにあなたのsum_usage列を残しています。

ここで、合計のテーブルを作成します。新しい値はsum_usage2です。

proc sql noprint; 
create table sums as 
select a.id, 
     a.opt, 
     a.t_purchase, 
     a.t_spent, 
     sum(b.usage) as sum_usage2 
    from have as a, 
     have as b 
    where a.id = b.id 
     and a.opt = b.opt 
     and b.t_spent <= a.t_spent 
     and b.t_spent >= a.t_purchase 
    group by a.id, 
     a.opt, 
     a.t_purchase, 
     a.t_spent; 
quit; 

は今、あなたは合計を持っていることを、元のテーブルにそれらを結合:

proc sql noprint; 
create table want as 
select a.*, 
     b.sum_usage2 
    from have as a 
     left join 
     sums as b 
     on a.id = b.id 
     and a.opt = b.opt 
     and a.t_spent = b.t_spent 
     and a.t_purchase = b.t_purchase; 
quit; 

これは、あなたがしたいテーブルを生成します。あるいは、ハッシュを使用して値をルックアップし、データ・ステップ(これはサイズを指定すると速くなる場合があります)に合計を追加することができます。私はこの質問を信じるhttps://github.com/FinancialRiskGroup/SASPerformanceAnalytics

1

ここにあり

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演算を使用したローリング値のデッド・レコニングでは、整数以外の値を扱うときに丸め誤差が発生する可能性があります。

関連する問題