2015-04-03 201 views
1

是否有可能多次重複一個數據步驟(例如您可能在%do-%while循環中),其中重複次數取決於數據步驟?SAS - 重複一個數據步驟來解決一個值

我有一個數據集與數值變量答:我計算一個新的變量結果= min(1,A)。我希望結果的平均值等於一個目標,我可以通過將常量k縮放變量A來達到目標​​。這是解決k其中目標=平均(最小(1,A * K)) - 其中K和目標是常數和A是一個列表。

這是我到目前爲止有:

filename f0 'C:\Data\numbers.csv'; 
filename f1 'C:\Data\target.csv'; 

data myDataSet; 
    infile f0 dsd dlm=',' missover firstobs=2; 
    input A; 
    init_A = A; /* store the initial value of A */ 
run; 

/* read in the target value (1 observation) */ 
data targets; 
    infile f1 dsd dlm=',' missover firstobs=2; 
    input target; 
    K = 1; * initialise the constant K; 
run; 

%macro iteration; /* I need to repeat this macro a number of times */ 
    data myDataSet; 
     retain key 1; 
     set myDataSet; 
     set targets point=key; 

     A = INIT_A * K; /* update the value of A /* 
     result = min(1, A); 
    run; 

    /* calculate average result */ 
    proc sql; 
     create table estimate as 
     select avg(result) as estimate0 
     from myDataSet; 
    quit; 

    /* compare estimate0 to target and update K */ 
    data targets; 
     set targets; 
     set estimate; 

     K = K * (target/estimate0); 
    run; 
%mend iteration; 

我可以通過運行%iteration幾次想要的答案,但我非常希望運行迭代,直到(target - estimate0 < 0.01)。這樣的事情可能嗎?

謝謝!

+0

如果您將目標,估計值0或目標估計值0總計爲一個或多個宏變量。然後你可以在你的宏中使用%do%until循環,直到(%eval)總計<0.01 – 2015-04-03 13:45:07

回答

0

兩部分答案。

首先,當然可以做你說的話。如果你需要一個有效的迭代宏代碼示例,那麼可以在線獲得一些代碼示例。例如,David Izrael的精明的Rakinge macro,它通過遍歷一個相對簡單的過程(基本上是proc freqs)來執行rimweighting過程。這與你正在做的事很相似。在此過程中,它按照各種終止標準查看datastep,並輸出一個宏變量,即滿足標準總數(因爲每個分層變量分別需要滿足終止標準)。然後它檢查%if符合條件,如果符合則終止。

這個的核心是兩件事。首先,你應該有一個固定的最大迭代次數,除非你喜歡無限循環。這個數字應該大於您應該需要的最大合理數量,通常大約爲兩倍。其次,您需要收斂條件,以便您可以在滿足時終止循環。

例如:

data have; 
    x=5; 
run; 

%macro reduce(data=, var=, amount=, target=, iter=20); 
    data want; 
    set have; 
    run; 
    %let calc=.; 
    %let _i=0; 
    %do %until (&calc.=&target. or &_i.=&iter.); 
    %let _i = %eval(&_i.+1); 
     data want; 
     set want; 
     &var. = &var. - &amount.; 
     call symputx('calc',&var.); 
     run; 
    %end; 
    %if &calc.=&target. %then %do; 
    %put &var. reduced to &target. in &_i. iterations.; 
    %end; 
    %else %do; 
    %put &var. not reduced to &target. in &iter. iterations. Try a larger number.; 
    %end; 
%mend reduce; 

%reduce(data=have,var=x,amount=1,target=0); 

這是一個很簡單的例子,但它具有所有的相同元素。我更喜歡使用do-until和自己增量,但你也可以做相反的事情(如%rakinge)。令人遺憾的是,宏語言不允許直到像數據步語言那樣做。好吧。其次,你可以在單個數據步驟中做這樣的事情。即使在較舊的版本(9.2等)中,您也可以在單個數據步驟中完成上面所要求的所有操作,儘管它看起來有點笨重。在9.3+中,特別是在9.4中,可以使用RUN_MACRO或DOSUBL和/或FCMP語言,在數據步驟中運行該proc sql,並在不等待另一個數據步驟的情況下返回結果。即使這樣簡單:

data have; 
    initial_a=0.3; 
    a=0.3; 
    target=0.5; 
    output; 
    initial_a=0.6; 
    a=0.6; 
    output; 
    initial_a=0.8; 
    a=0.8; 
    output; 
run; 

data want; 
    k=1; 
    do iter=1 to 20 until (abs(target-estimate0) < 0.001); 
    do _n_ = 1 to nobs; 
     if _n_=1 then result_tot=0; 
     set have nobs=nobs point=_n_; 
     a=initial_a*k; 
     result=min(1,a); 
     result_tot+result; 
    end; 
    estimate0 = result_tot/nobs; 
    k = k * (target/estimate0); 
    end; 
    output; 
    stop; 
run; 

這樣做在一個數據步驟中完成。我有點欺騙,因爲我正在編寫自己的數據迭代器,但在這類事情中這是相當普遍的,而且速度非常快。反覆執行多個數據步驟和執行sql步驟的宏通常要慢得多,因爲每個步驟都有一些開銷。

+0

看來我的代碼中缺少的部分是'call symputx'行。我是SAS新手,並且認爲宏像C/C++預處理器命令,但是如果代碼正常工作,那麼一定會有一些魔術發生。謝謝! – VersBersch 2015-04-04 02:43:07

1

我剛纔有一個類似的問題,就在這一天。下面的方法是我使用的,您需要將環路結構從for環路更改爲do while環路(或任何適合您的用途):

首先執行表的初始掃描以確定您的環路終止條件並獲得行的表上的號碼:

data read_once; 
    set sashelp.class end=eof; 

    if eof then do; 
    call symput('number_of_obs', cats(_n_));  
    call symput('number_of_times_to_loop', cats(3)); 
    end; 
run; 

確保結果如預期:

%put &=number_of_obs; 
%put &=number_of_times_to_loop; 

遍歷源表再次多次:

data final; 

    do loop=1 to &number_of_times_to_loop; 
    do row=1 to &number_of_obs; 
     set sashelp.class point=row; 
     output; 
    end; 
    end; 

    stop; * REQUIRED BECAUSE WE ARE USING POINT=; 

run; 
相關問題