2015-01-02 116 views
1

我不明白我的SAS代碼中發生了什麼。該代碼的行爲與預期相同,但僅在第一次編譯期間,即名爲'SummaryTable'的表顯示代碼觸發%mylogreg和%ModelsSummary的3次迭代中每一次的正確'LogLik'值。但是,如果我重新運行代碼,則它的行爲不同,3次迭代的'LogLik'值是相同的,它等於最後一次迭代的值。因此,在第一次編譯代碼時,變量「MyLogLik」以某種方式保存了第三次迭代的值。SAS宏編碼

第一資訊生成步驟:

data ingots; 
input heat soak r n @@; 
datalines; 
7 1.0 0 10 7 1.7 0 17 7 2.2 0 7 7 2.8 0 12 7 4.0 0 9 
14 1.0 0 31 14 1.7 0 43 14 2.2 2 33 14 2.8 0 31 14 4.0 0 19 
27 1.0 1 56 27 1.7 4 44 27 2.2 0 21 27 2.8 1 22 27 4.0 1 16 
51 1.0 3 13 51 1.7 0 1 51 2.2 0 1 51 2.8 0 1 
; 
run; 

然後宏:

%macro mylogreg(Num,param); 

    title "variables are &param"; 
    proc logistic data=ingots des outest = parameters&Num noprint; 
model r/n = &param; 
run; 

%mend; 

%macro ModelsSummary(Count,Var,Param); 
    proc sql; 

    select _LNLIKE_ into:MyLogLik from parameters&Count; 

    insert into SummaryTable (ModelNumber,ModelVariables,NumParameters, LogLik) values (&Count,"&Var",&Param, &MyLogLik); 

    drop table parameters&Count; 


    quit; 
%mend; 

然後我的代碼:

proc sql; 
    create table SummaryTable(
         ModelNumber num, 
         ModelVariables varchar (500), 
         NumParameters num, 
         LogLik num 
        ) 
                  ; 
quit; 

data _NULL_; 

array a[2] $ (' heat' ' soak'); 
length temp $500; 

    /*Number of Variables*/ 
    n = dim(a); 

    /*k tell us the number of variables in aech group of all possible conbination*/ 
    do k=1 to n; 

/*total tells the number of different convinations given that we form groups of k variables*/ 
total = comb(n,k); 

do j=1 to total; 

    /*allcomb is the SAS function which forms all different combinations*/ 
    call allcomb(j,k,of a[*]); 
    /*This counter show the total number of convinations for all ks*/ 
    Counter + 1; 

    do i = 1 to k; 
     if i = 1 then temp = ''; 
     /*this temp string contains the explanatory variables to be passed to the logistic reg*/ 
     temp=catt(temp,a[i]); 
    end; 

    /* NumParam shows the number of parameters in the logistic model, including the intercept*/ 
    NumParam = k + 1; 

    /* First we call the logistic regression macro, and the we fill out the table summarizing the important results*/ 
    call execute('%mylogreg('||Counter||','||temp||')'); 
    call execute('%ModelsSummary('||Counter||','||temp||','||NumParam||')'); 


    end; 

end; 

run; 

那麼問題是,如果我重新運行該代碼,那麼'SummaryTable'中'LogLik'的值將全部相同,並且該值對應於第三個模型。正如我之前所說的,第一次運行代碼時,循環按預期工作,每個模型在「SummaryTable」中都有相應的「LogLik」值。我已經檢查過%ModelSummary獲得變量'Counter'的正確值(這也可以在SQL輸出中確認),並且看起來是正確的。所以,我認爲我需要的是在主代碼完成之後清理'MyLogLik'的值或類似的東西。

有人可以指出我缺少什麼嗎?我沒有看到我的代碼有什麼問題!

回答

4

簡短的答案是嘗試在%NRSTR()內包裹您的宏調用。這將是一個長時間的解釋。但宏觀語言很難。用於調用宏時的調用執行時機使其變得更加棘手。以下大部分內容是我從Ian Whitlock的許多SAS-L帖子/論文/討論中學到的。如果你谷歌「伊恩惠特洛克呼叫執行」你會發現比我能提供更好的解釋,但這裏是一個鏡頭。

下面是一個簡單的宏的例子。它使用數據步驟PUT語句向日志寫入名稱列表。 PROC SQL用於生成名稱列表並將其存儲在宏變量&列表中。請注意,宏變量被聲明爲宏的本地。

%macro ListNames(sex=F); 
    %local list; 
    proc sql noprint; 
    select name into :List separated by " " 
    from sashelp.class 
    where sex="&sex" 
    ; 
    quit; 

    data list; 
    list="&list"; 
    put "Inside Macro " list=; 
    run; 

%mend ListNames; 

下面是調用宏兩次日誌(不調用execute):

20   %listNames(sex=F) 
Inside Macro list=Alice Barbara Carol Jane Janet Joyce Judy Louise Mary 
NOTE: The data set WORK.LIST has 1 observations and 1 variables. 
21   %listNames(sex=M) 
Inside Macro list=Alfred Henry James Jeffrey John Philip Robert Ronald Thomas William 
NOTE: The data set WORK.LIST has 1 observations and 1 variables. 

這裏是調用使用調用兩次宏觀調控的例子執行一個不起作用。類似於你的代碼不工作的方式。首先是代碼,那麼日誌,然後我的解釋:

data _null_; 
    input sex $1; 
    call execute('%ListNames(sex='||sex||')'); 
    cards; 
F 
M 
; 
run; 

%put OUTSIDE MACRO list=&list; 

%*cleanup; 
%symdel List; 

LOG:

30   data _null_; 
31   input sex $1; 
32   call execute('%ListNames(sex='||sex||')'); 
33   cards; 
NOTE: CALL EXECUTE generated line. 
36   ; 
1   + proc sql noprint; 
1   +      select name into :List separated by " "  from 
sashelp.class  where sex="F" ; 
1   + 
    quit; 
1   + 
data list;  list="";  put "Inside Macro " list=; run; 

Inside Macro list= 
NOTE: The data set WORK.LIST has 1 observations and 1 variables. 

2   + proc sql noprint; 
2   +      select name into :List separated by " "  from 
sashelp.class  where sex="M" ; 
2   + 
    quit; 

2   + 
data list;  list="";  put "Inside Macro " list=; run; 

Inside Macro list= 
NOTE: The data set WORK.LIST has 1 observations and 1 variables. 

37   run; 
38   %put OUTSIDE MACRO list=&list; 
OUTSIDE MACRO list=Alfred Henry James Jeffrey John Philip Robert Ronald Thomas William 
39   

說明:

注意,沒有警告或錯誤日誌中,但代碼不工作」。宏內的列表值&始終爲空。有趣的是,儘管宏聲明&列表是本地的,但&列表最終被創建爲全局宏變量。奇怪?

調用執行和宏語言的工作是生成代碼。當你使用call execute來調用宏時(如上所述),宏執行並生成任何SAS代碼,並將其轉儲到輸入堆棧中。 所有代碼都是在執行任何代碼之前生成的。

在上面的示例中,將生成PROC SQL步驟,並生成DATA LIST步驟。但是,在執行PROC SQL步驟之前生成DATA LIST步驟!通常,會認爲PROC SQL步驟將被執行,並且將填充&列表,然後DATA LIST步驟將被編譯並執行。但請記住,此宏是由CALL EXECUTE調用的,它仍在運行的DATA STEP中。在執行主要數據步驟的同時,SAS不能執行PROC SQL(忽略較新的DOSUBL功能和類似)。因此,在生成DATA LIST代碼時,宏變量&列表尚未填充。它是空的。如果宏沒有%local語句,我會得到一個關於宏變量不能解析的警告(就像你做的那樣)。

那麼爲什麼宏變量在宏(返回的男性列表)之外解析?請注意,由宏生成的代碼實際上是在宏外執行的。也就是說,調用execute調用了宏,但生成的代碼只是放在輸入棧上。當它執行時,它是在開放代碼中。所以它會生成一個全局宏變量。請注意,您可以看到由CALL EXECUTE生成的所有代碼,因爲它在日誌中以+開頭。

解決方法是在%NRSTR()中包裝宏觸發器。當你這樣做時,調用execute將不會實際調用宏。但它會生成宏調用,並將宏調用放在輸入堆棧上。然後,當宏執行時,PROC SQL步驟將在DATA LIST步驟之前執行。

這裏是代碼:

data _null_; 
    input sex $1; 
    call execute('%nrstr(%%)ListNames(sex='||sex||')'); 
    cards; 
F 
M 
; 
run; 

和日誌:

49   data _null_; 
50   input sex $1; 
51   call execute('%nrstr(%%)ListNames(sex='||sex||')'); 
52   cards; 

NOTE: CALL EXECUTE generated line. 
55   ; 
1   + %ListNames(sex=F) 

Inside Macro list=Alice Barbara Carol Jane Janet Joyce Judy Louise Mary 
NOTE: The data set WORK.LIST has 1 observations and 1 variables. 

2   + %ListNames(sex=M) 

Inside Macro list=Alfred Henry James Jeffrey John Philip Robert Ronald Thomas William 
NOTE: The data set WORK.LIST has 1 observations and 1 variables. 

%NRSTR()用來隱藏調用宏觸發執行。請注意,調用執行只會生成兩個宏調用(顯示在帶有+前綴的日誌中)。它並不實際執行宏。這些宏在使用CALL EXECUTE的數據步之後執行。因此,由宏生成的代碼按照人們的希望執行(PROC SQL步驟在編譯和執行DATA LIST步驟之前執行)。

關鍵是當您使用CALL EXECUTE調用宏時,如果該宏使用DATA STEP或PROC SQL代碼生成宏變量,則最好使用%NRSTR()來延遲宏的執行。

HTH

+0

感謝您的好解釋!我注意到這個宏是在數據步之後執行的,但我不確定它確實發生了什麼(我一般對於SAS編碼是新的,但對我而言並不直觀),我將在星期一檢查我的代碼,因爲我的SAS連接此刻不起作用。我會讓你知道的。謝謝:) – user1571823

+0

它仍然是代碼不工作。我已經在宏模型總結中聲明瞭MyLogLik本地,並且在%NRSTR中包含宏觸發器作爲調用execute('%nrstr(%%)%ModelsSummary('|| Counter ||','|| temp ||',' || || NumParam ')'); 並在日誌中獲取此信息:注意:由CALL EXECUTE例程生成的行。 2 +%proc sql; _ 警告:宏PROC的表觀調用未解決。 錯誤180-322:語句無效或使用的順序不正確。 – user1571823

+0

在ModelsSummary之前立即顯示沒有%符號 – Quentin

0

它不跳出什麼,確切是錯誤的。你跑步時是否收到錯誤信息?它第一次運行而不是第二次運行的事實告訴我,除了宏變量的範圍外,可能還有其他事情。

您可以使用%local MyLogLik;%ModelsSummary中聲明MyLogLik

0

我覺得你遇到了問題,如何調用執行工作以及何時解決宏變量。我的推理是,如果您通過%ModelsSummary(..)單獨調用宏,則不會發生該錯誤。 我不知道它爲什麼會發生,希望有人對你有更好的迴應。解決方法是使用唯一的宏變量,即在MyLogLik宏變量的末尾添加&計數。

%macro ModelsSummary(Count,Var,Param); 
    proc sql; 

    select _LNLIKE_ into :MyLogLik&count from parameters&Count; 

    insert into SummaryTable (ModelNumber,ModelVariables,NumParameters, LogLik) values (&Count,"&Var",&Param, &&MyLogLik&count); 

    drop table parameters&Count; 


    quit; 
%mend; 
+0

感謝您的工作!還有一個我不明白的功能...當您第一次運行代碼時,您會收到一條警告消息(警告:明顯的符號引用MYLOGLIK1未解決。),並且如果您想要打印出「MYLOGLIK1」的值使用%put記錄日誌,在第一次編譯時不會有任何數值。 – user1571823

+0

但沒有錯誤信息?聽起來好像沒有PROC LOGISTIC的輸出導致OUTEST數據集不存在。這會導致你的'SELECT ... into:'失敗並且不會創建宏變量。 – DomPazz

+0

我認爲這實際上是因爲代碼的格式化,其中:myloglik沒有任何空格。我將在代碼中修復它。你原來的代碼有同樣的問題。 – Reeza