2014-08-28 41 views
5

調用宏時爲SAS程序運行這段代碼執行罰款:動態地從SAS數據步

%MyMacro(foo_val, bar_val, bat_val);

我創建使用表格:我想執行MyMacro一次

DATA analyses; 
    input title : $32. weight : $32. response : $32.; 
    datalines; 
foo1 bar1 bat1 
foo2 bar2 bat2 
; 

對於analyses表中的每一行。

下面的代碼似乎只傳遞字符串值titleweightresponse(而不是數據值foo1等)到我的宏(通過調用該%put命令測試):

DATA _NULL_ ; 
    set analyses; 
    %MyMacro(title, weight, response); 

RUN; 

如何我可以在每個analyses表的記錄中調用宏一次,同時將數據值作爲參數傳遞給宏?其目的是爲了進行非常多的分析而實際運行該解決方案,因此解決方案必須適當縮放到analyses表中的更多記錄。

回答

9

這部分取決於您的宏在做什麼。如果我們假設你的宏正在做一些旨在在數據步之外運行的東西(即它不只是分配一個數據步變量),那麼你有幾個選擇。

調用execute已經解釋過,是某些情況下,一個很好的選擇。但是,它有一些缺點,特別是對於宏觀時序,在某些情況下需要特別注意保護 - 尤其是當您在宏中創建宏變量時。 Quentin在他的評論中展示了一種解決這個問題的方法(將%NRSTR添加到調用中),但是我發現當使用其他方法有優勢時,我更喜歡只使用CALL EXECUTE - 特別是,如果我想使用SAS數據步驟技術(例如FIRST或LAST,或者某種形式的循環)來創建我的宏調用,或者我必須在數據步驟中執行某些操作,並且可以避免再次讀取文件的開銷。如果我只是像上面一樣寫數據步驟 - 數據,設置,調用執行,運行 - 我不會使用它。


PROC SQL SELECT INTO通常是我用於列表處理(這主要是這是什麼)。在處理不太複雜的事情時,我喜歡SQL的簡單性,例如,只需使用DISTINCT即可輕鬆獲得每個宏調用的一個版本,而無需明確編寫proc sort nodupkey或使用first/last處理。它還具有調試的優點,即您可以將所有宏調用寫入結果窗口(如果不添加noprint),如果我試圖瞭解爲什麼這會比日誌更容易閱讀我的電話沒有得到正確生成(並且沒有任何額外的PUT語句)。

proc sql; 
    select catx(',','%macro(',arg1,arg2,arg3)||')' 
    into :mvarlist separated by ' ' 
    from dataset; 
quit; 

&mvarlist. 

它運行得很簡單,沒有時間問題(因爲你只是寫了一堆宏調用)。

這種方法的主要缺點是在宏變量中最多有64k個字符,所以如果你寫了大量的這些,你會遇到這種情況。在這種情況下,使用CALL EXECUTE%INCLUDE文件。


%INCLUDE文件在很大程度上是有用的或者作爲替代,當通話結束字符限制,或者如果你發現有一個文本文件,看您的通話(如果你正在運行這個有用SELECT INTO例如,在批處理模式下,這可能比日誌或列表輸出更容易進行和/或解析)。您只需將呼叫寫入文件,然後%INCLUDE即可。

filename myfile temp; *or a real file if you want to look at it.; 
data _null_; 
set dataset; 
file myfile; 
put @1 catx(',','%macro(',arg1,arg2,arg3)||')'; 
run; 

%include myfile; 

我真的不使用這個多了,但它是由舊的SAS程序員那麼好知道特別常用的技術。


DOSUBL是一個相對較新的方法,並在一定程度上可以用來代替CALL EXECUTE爲它的默認行爲是典型的接近你期望直觀地比CALL EXECUTE的東西。 doc頁面對於這種工作原理是不同的最好的例子。基本上,它通過讓每個單獨的調用看起來導入並將宏變量從/調用到調用環境來修復時序問題,這意味着每個迭代DOSUBL都在不同的時間運行,而在CALL EXECUTE處運行的是一切,宏環境是'固定的'(即,任何對宏變量的引用在運行時都是固定的,除非你用%NRSTR亂搞)。


還有一兩件事值得一提的是RUN_MACRO,該FCMP語言的一部分。這使您可以完全運行宏並將其內容導回到數據步,在某些情況下這是一個有趣的選項(例如,您可以圍繞選擇某項內容的PROC SQL打包調用,然後將其作爲變量導入數據集,全部在一個datastep中)。如果你這樣做是爲了調用一個宏來分配一個數據步驟變量,而不是運行一個不需要導入數據步驟的進程,但是這是值得考慮的事情,如果你確實希望這些數據全部返回到調用該進程的數據集中。

+1

提到的那樣非常好的總結。我仍然希望更多的文檔將來自SAS re DOSUBL和RUN_MACRO。在我看來,這兩種方式都以某種方式在單獨的迷你SAS會話中執行代碼。當幾年前我玩過它的時候,範圍規則並不是很清楚。例如,使用run_macro調用的宏中設置的系統選項可能會或可能不會更改主會話的選項。但他們肯定是強大的工具,值得學習。 – Quentin 2014-08-28 16:37:04

+0

很好的答案。另外值得考慮的是,宏可以改寫爲FCMP函數嗎? – 2014-08-28 16:49:10

+0

我期待着FCMP函數超過宏的那一天...儘管在這種情況下,它聽起來像是一個完整的分析,現在可能最好不要這樣寫(儘管RUN_MACRO允許它以這種方式寫入,如果沒有其他重大利益,我不相信這真的是個好主意)。 – Joe 2014-08-28 17:10:49

0

你可以把變量的值到macrovariables,然後打電話給你的%MyMacro多次(OBS在你的數據集數)與macrovariables作爲參數:

數據:

DATA analyses; 
    input title : $32. weight : $32. response : $32.; 
    datalines; 
foo1 bar1 bat1 
foo2 bar2 bat2 
; 
run; 

運行宏的代碼:

data _NULL_; 
    set analyses end=fine; 
    call symput("ARGUMENT"||compress(_N_),catx(",",title,weight,response)); 
    if fine then call symput("NLOOPS",compress(_N_)); 
run; 
%*PUT &ARGUMENT1; 
%*PUT &ARGUMENT2; 

%MACRO MAIN; 
%DO L=1 %TO &NLOOPS; 
    %MyMacro(&&ARGUMENT&L); 
%END; 
%MEND; 
%MAIN; 
+0

似乎很hacky,我不明白爲什麼我不能在「DATA」步驟中調用宏 - 你能解釋爲什麼這種方法是必須的嗎? – JustinJDavies 2014-08-28 13:00:27

+0

將第二個'%DO'更改爲'%TO' – JustinJDavies 2014-08-28 13:19:30

+1

由於計時問題,您的方法失敗。宏語句在執行數據步驟代碼之前執行。這是因爲宏語言的工作是生成SAS代碼。在您的示例中,宏將標題看作文本字符串參數。它不知道有一個名爲title的數據集變量具有值。呼叫執行方式更像您所期望的。它是一個數據步驟語句,它在執行時可以調用一個宏,並且可以從數據集值傳遞參數。那裏也有一些有趣的計時問題。 (請參閱我的評論)。 – Quentin 2014-08-28 13:53:48

4

您可以使用CALL EXECUTE:

data _null_; 
    set analyses; 
    call execute('%nrstr(%MyMacro('||title||','||weight||','||response||'))'); 
run; 
+1

我喜歡呼叫執行方式。注意,在執行數據_null_步驟時,宏將被執行(解析所有宏符號)。這意味着如果%MyMacro從數據步代碼生成宏變量,它將無法工作。通常添加%nrstr()會很有幫助,它會調用execute生成宏調用,但是直到數據_null_步驟完成後纔會執行宏,例如:call execute('%nrstr(%MyMacro(' || ||標題 ' '|| ||重量', '|| ||響應'))');請參閱http://support.sas.com/kb/23/134.html – Quentin 2014-08-28 13:58:23

+2

另請參閱DOSUBL作爲CALL EXECUTE的替代方法,在同一支持頁面中引用,顯然可以管理時序問題,而無需在%NRSTR中包裝宏調用() – Quentin 2014-08-28 14:04:02

+0

我更新了我的答案以使用%nrstr,正如@Quentin – DavB 2014-08-28 15:38:20