2017-03-20 7 views
1

I過去1日、7日、1ヶ月、3ヶ月、6ヶ月間の特徴の合計を実行している生成に使用されている次のコードを持っています。私はA1データセットの作成に(= OBS)を使用して、%ヘルパーマクロの観測の数を制限する場合SASマクロエラーが実行

LIBNAME A "C:\Users\James\Desktop\data\Base Data"; 
LIBNAME DATA "C:\Users\James\Desktop\data\Data1"; 

%MACRO HELPER(P); 

data a1; 
set data.final_master_&P. ; 
QUERY = '%TEST('||STRIP(DATETIME)||','||STRIP(PARTICIPANT)||');'; 
CALL EXECUTE(QUERY); 
run; 

%MEND; 

%MACRO TEST(TIME,PAR); 
proc sql; 
select SUM(APP_1), SUM(APP_2), sum(APP_3), SUM(APP_4), SUM(APP_5) INTO :APP_1_24, :APP_2_24, :APP_3_24, :APP_4_24, :APP_5_24 
FROM A1 
WHERE DATETIME BETWEEN INTNX('SECONDS',&TIME.,-60*60*24) AND &TIME.; 

/* 7 Days */ 
select SUM(APP_1), SUM(APP_2), sum(APP_3), SUM(APP_4), SUM(APP_5) INTO :APP_1_7DAY, :APP_2_7DAY, :APP_3_7DAY, :APP_4_7DAY, :APP_5_7DAY 
FROM A1 
WHERE DATETIME BETWEEN INTNX('SECONDS',&TIME.,-60*60*24*7) AND &TIME.; 

/* One Month */ 

select SUM(APP_1), SUM(APP_2), sum(APP_3), SUM(APP_4), SUM(APP_5) INTO :APP_1_1MONTH, :APP_2_1MONTH, :APP_3_1MONTH, :APP_4_1MONTH, :APP_5_1MONTH 
FROM A1 
WHERE DATETIME BETWEEN INTNX('SECONDS',&TIME.,-60*60*24*7*4) AND &TIME.; 

/* Three Months */ 

select SUM(APP_1), SUM(APP_2), sum(APP_3), SUM(APP_4), SUM(APP_5) INTO :APP_1_3MONTH, :APP_2_3MONTH, :APP_3_3MONTH, :APP_4_3MONTH, :APP_5_3MONTH 
FROM A1 
WHERE DATETIME BETWEEN INTNX('SECONDS',&TIME.,-60*60*24*7*4*3) AND &TIME.; 

/* Six Months */ 

select SUM(APP_1), SUM(APP_2), sum(APP_3), SUM(APP_4), SUM(APP_5) INTO :APP_1_6MONTH, :APP_2_6MONTH, :APP_3_6MONTH, :APP_4_6MONTH, :APP_5_6MONTH 
FROM A1 
WHERE DATETIME BETWEEN INTNX('SECONDS',&TIME.,-60*60*24*7*4*6) AND &TIME.; 

quit; 

DATA T; 
PARTICIPANT = &PAR.; 
DATETIME = &TIME; 
APP_1_24 = &APP_1_24.; 
APP_2_24 = &APP_2_24.; 
APP_3_24 = &APP_3_24.; 
APP_4_24 = &APP_4_24.; 
APP_5_24 = &APP_5_24.; 
APP_1_7DAY = &APP_1_7DAY.; 
APP_2_7DAY = &APP_2_7DAY.; 
APP_3_7DAY = &APP_3_7DAY.; 
APP_4_7DAY = &APP_4_7DAY.; 
APP_5_7DAY = &APP_5_7DAY.; 
APP_1_1MONTH = &APP_1_1MONTH.; 
APP_2_1MONTH = &APP_2_1MONTH.; 
APP_3_1MONTH = &APP_3_1MONTH.; 
APP_4_1MONTH = &APP_4_1MONTH.; 
APP_5_1MONTH = &APP_5_1MONTH.; 
APP_1_3MONTH = &APP_1_3MONTH.; 
APP_2_3MONTH = &APP_2_3MONTH.; 
APP_3_3MONTH = &APP_3_3MONTH.; 
APP_4_3MONTH = &APP_4_3MONTH.; 
APP_5_3MONTH = &APP_5_3MONTH.; 
APP_1_6MONTH = &APP_1_6MONTH.; 
APP_2_6MONTH = &APP_2_6MONTH.; 
APP_3_6MONTH = &APP_3_6MONTH.; 
APP_4_6MONTH = &APP_4_6MONTH.; 
APP_5_6MONTH = &APP_5_6MONTH.; 
FORMAT DATETIME DATETIME.; 
RUN; 

PROC APPEND BASE=DATA.FLAGS_&par. DATA=T; 
RUN; 

%MEND; 

%helper(1); 

このコードは、完全に実行されます。しかし、obs番号に制限を設けないと、つまり、データセットa1のすべての行に対して%testマクロを実行すると、エラーが発生します。 SAS EGでは、ステータスバーが "running data step"でハングアップした後、 "server disconnected"ポップアップが表示され、Base SAS 9.4ではproc sqlに作成されたマクロ変数が解決されていないというエラーが表示されます。コードは観測の限られた量のために正常に動作として

は私が困惑しているが、全体のデータセットにしようとしたとき、それがハングまたはエラーが発生します。私がこれを行っているデータセットには約130,000件の観測値があります。

+0

呼び出し実行時にマクロ呼び出しに%nrstr()を追加する必要があります。 http://stackoverflow.com/questions/27749472/sas-macro-coding/27757820の私の長い答えを見てください。つまり、コードが大量に生成されているため、コールの実行に問題が残る可能性があります。 – Quentin

+0

ちょうどこれを試して、生成されるコードの量は私が思う問題です。これを回避する方法はありますか? – user2662468

+0

ジョーの答えの代替案を参照してください:http://stackoverflow.com/questions/25545892/dynamically-call-macro-from-sas-data-step。私は%インクルードアプローチを試みます。 – Quentin

答えて

1

あなたの実際の質問への答えは、あなたがあまりにも多くのマクロコードを生成し、おそらくあまりにも多くの時間を取っているということです。あなたがこれをやっているやり方は、O = n^2レベルで動作することです。基本的にすべてのレコードをすべてのレコードにデカルト結合させ、次にいくつかのレコードにデカルト結合します。 130,000 * 130,000はかなりまともなサイズです。その上に、実際には130,000行ごとにSQL環境を何度か開いています。ああ。

ソリューションは遅すぎない方法で、またはそれは、少なくとも、あまりにも多くのオーバーヘッドを持っていないような方法で、ある場合のいずれかにそれを行うことです。

速い解答はではなく、はデカルト結合を行うか、または結合する必要があるかを制限することです。 1つの良い解決策は、問題を再構成することです。すべてのレコードを比較する必要はありませんが、代わりに各暦日、たとえば24時間を超える期間を考慮してください。他の4人)。 1ヶ月、3ヶ月など、あなたは本当に時間を把握する必要がありますか?恐らく大きな違いはありません。それを取り除くことができれば、組み込みのPROCを使用して、可能なすべての1か月、すべての可能な3か月の期間などをプリコンパイルしてから、適切なものに結合することができます。しかし、それは130,000で動作しません。あなたが1日に1つに制限することができれば、それはうまくいくでしょう。

あなたが第2レベル(またはより悪い)でそれを行う必要がある場合は、何がやりたいだろうことはデカルトが参加避け、代わりにあなたはすでに見てきた様々な記録を追跡している

、および合計。アルゴリズムの簡単な説明は次のとおりです。行ごとに

:キューの現在の項目がある場合

  • (キューの末尾)ローリング合計にチェックをこの行の値を追加します。その期間外。それがある場合には、(期間外ではないまで繰り返す)ローリング合計からそれを引くと、次の項目をチェックし、現在のキュー位置を更新
  • 戻る合計この時点で

これは、それぞれのチェックが必要です通常は2回です(曜日が異なる月があるため、いくつかの繰り返しで行がポップされない奇数境界を除く)。これは、O = nの時間に、デカルト結合よりもはるかに高速で、その上に必要なメモリー/スペースがはるかに少なくなります(デカルト結合はディスク・スペースを必要とするかもしれません)。

このソリューションのハッシュバージョンは以下のとおりです。これはすべての行を比較すると私が考える最も速い解決策になります。私は意図的にテストデータに毎日1行ずつ、同じ数の行数を持たせることに注意してください。これは、それが行方法でどのように動作するかを非常に簡単に見ることができます。 (たとえば、1日あたり480行を作成したため、24時間ごとに481行が作成され、481には同じ時刻が含まれています。ltleに変更すると、昨日同じ時刻を含めたくない場合は480になります)。 '01FEB20xx'〜 '01MAY20xx'期間は '01JUL20xx'〜 '01OCT20xx'期間よりもはるかに少ない日数(したがって行数)があるため、 '月'ベースの期間は月が変わる境界でわずかに奇妙な結果になることがわかります、 例えば; 30/90/180日の期間がよいでしょう。

data test_data; 
    array app[5] app_1-app_5; 
    do _i = 1 to 130000; 
    dt_var = datetime() - _i*180; 
    do _j = 1 to dim(app); 
     *app[_j] = floor(rand('Uniform')*6); *generate 0 to 5 integer; 
     app[_j]=1; 
    end; 
    output; 
    end; 
    format dt_var datetime17.; 
run; 

proc sort data=test_data; 
    by dt_var; 
run; 



%macro add(array=); 
     do _i = 1 to dim(app); 
     &array.[_i] + app[_i]; 
     end; 
%mend add; 



%macro subtract(array=); 
     do _i = 1 to dim(app); 
     &array.[_i] + (-1*app[_i]); 
     end; 
%mend subtract; 

%macro process_array_add(array=); 

    array app_&array. app_&array._1-app_&array._5; 


    %add(array=app_&array.); 

%mend process_array_add; 

%macro process_array_subtract(array=, period=, number=); 

    if _n_ eq 1 then do; 
    declare hiter hi_&array.('td'); 
    rc_&array. = hi_&array..first(); 
    end; 
    else do; 
    rc_&array. = hi_&array..setcur(key:firstval_&array.); 
    end; 

    do while (intnx("&period.",dt_var,&number.,'s') lt curr_dt_var and rc_&array.=0); 
    %subtract(array=app_&array.); 
    rc_&array. = hi_&array..next(); 
    end; 

    retain firstval_&array.; 
    firstval_&array. = dt_var; 

%mend process_array_subtract; 


data want; 
    set test_data; 

* if _n_ > 10000 then stop; 
    curr_dt_var = dt_var; 
    array app[5] app_1-app_5; 


    if _n_ eq 1 then do; 
    declare hash td(ordered:'a'); 
    td.defineKey('dt_var'); 
    td.defineData('dt_var','app_1','app_2','app_3','app_4','app_5'); 
    td.defineDone(); 
    end; 


    rc_a = td.add(); 

    *start macro territory; 

    %process_array_add(array=24h); 
    %process_array_add(array=1wk); 
    %process_array_add(array=1mo); 
    %process_array_add(array=3mo); 
    %process_array_add(array=6mo); 



    %process_array_subtract(array=24h,period=DTDay, number=1); 
    %process_array_subtract(array=1wk,period=DTDay, number=7); 
    %process_array_subtract(array=1mo,period=DTMonth, number=1); 
    %process_array_subtract(array=3mo,period=DTMonth, number=3); 
    %process_array_subtract(array=6mo,period=DTMonth, number=6); 

    *end macro territory; 

    rename curr_dt_var=dt_var; 
    format curr_dt_var datetime21.3; 
    drop dt_var rc: _:; 

    output; 


run; 
+0

私はこれを編集しました。これは、加算/減算を分割する必要があることを実感しました(すべての行が等しいので問題として表示されませんでしたが、おそらくそうではないため追加をすべて実行する必要があります) )。 – Joe

+0

私はPROC MEANSソリューションを試しましたが、明らかに、マルチラベルフォーマット(255レベル)には(比較的小さい)制限があり、残念ながらそれは許されません。 – Joe

1

純粋なデータステップ非ハッシュバージョンです。私のマシンでは実際にはハッシュ・ソリューションより高速です。 HDDを搭載したマシンでは実際には高速ではないと思われます(私はSSDを持っているので、ポイントアクセスはハッシュアクセスよりも大幅に遅くないので、ハッシュをロードする必要はありません)。私はハッシュがよく分からない場合やトラブルシューティングが容易になるので、それを使用することをお勧めします。ほとんどの行では、11行、現在の行、5行が2回(1行、減算、別の行)アクセスされ、合計130万行の合計読み取り回数は約1億回です。 (デカルトで約170億回の読み込みと比較してください)

マクロに「_2」をつけて、ハッシュ解答のマクロと区別します。

data test_data; 
    array app[5] app_1-app_5; 
    do _i = 1 to 130000; 
    dt_var = datetime() - _i*180; 
    do _j = 1 to dim(app); 
     *app[_j] = floor(rand('Uniform')*6); *generate 0 to 5 integer; 
     app[_j]=1; 
    end; 
    output; 
    end; 
    format dt_var datetime17.; 
run; 

proc sort data=test_data; 
    by dt_var; 
run; 

%macro add_2(array=); 
     do _i = 1 to dim(app); 
     &array.[_i] + app[_i]; 
     end; 
%mend add; 



%macro subtract_2(array=); 
     do _i = 1 to dim(app); 
     &array.[_i] + (-1*app[_i]); 
     end; 
%mend subtract; 

%macro process_array_add_2(array=); 

    array app_&array. app_&array._1-app_&array._5; *define array; 

    %add_2(array=app_&array.);      *add current row to array; 
%mend process_array_add_2; 

%macro process_array_sub_2(array=, period=, number=); 
    if _n_ eq 1 then do;        *initialize point variable; 
    point_&array. = 1; 
    end; 
    else do;           *do not have to do this _n_=1 as we only have that row; 
    set test_data point=point_&array.;    *set the row that we may be subtracting; 
    end; 

    do while (intnx("&period.",dt_var,&number.,'s') lt curr_dt_var and point_&array. < _N_); *until we hit a row that is within the period...; 
    %subtract_2(array=app_&array.);    *subtract the rows values; 
    point_&array. + 1;        *increment the point to look at; 
    set test_data point=point_&array.;    *set the new row; 
    end; 

%mend process_array_sub_2; 


data want; 
    set test_data; 

    *if _n_ > 10000 then stop;      *useful for testing if you want to check time to execute; 
    curr_dt_var = dt_var;       *save dt_var value from originally set record; 
    array app[5] app_1-app_5;      *base array; 

    *start macro territory; 
    %process_array_add_2(array=24h);     *have to do all of these adds before we start subtracting; 
    %process_array_add_2(array=1wk);     *otherwise we have the wrong record values; 
    %process_array_add_2(array=1mo); 
    %process_array_add_2(array=3mo); 
    %process_array_add_2(array=6mo); 

    %process_array_sub_2(array=24h,period=DTDay, number=1); *now start checking to subtract what we need to; 
    %process_array_sub_2(array=1wk,period=DTDay, number=7); 
    %process_array_sub_2(array=1mo,period=DTMonth, number=1); 
    %process_array_sub_2(array=3mo,period=DTMonth, number=3); 
    %process_array_sub_2(array=6mo,period=DTMonth, number=6); 

    *end macro territory; 

    rename curr_dt_var=dt_var; 
    format curr_dt_var datetime21.3; 
    drop dt_var _:; 

    output;           *unneeded in this version but left for comparison to hash; 


run; 
関連する問題