公告

[公告]
2014/01/17
由於已經是faculty的關係,不太有足夠時間寫部落格。因此更新的速度會相當緩慢。再加上近幾年來SAS GLOBAL FORUM沒有出現讓我覺得驚艷的技術文件,所以能分享的文章相對也減少許多。若有人推薦值得分享的SAS技術文件,請利用『問題討論區』告知。

2013/07/19
臉書留言板的功能因為有不明原因故障,因此特此移除。而intensedebate的留言板因管理不易,也一併移除。目前已經開啟內建的 G+ 留言系統,所以請有需要留言的朋友,可直接至『問題討論區』裡面留言。


2007年7月17日 星期二

Methods for Repeated Macro Calls

原文連結:http://www.lexjansen.com/pharmasug/2007/tt/tt13.pdf

前一陣子我自己寫了一個 macro 打算呼叫不同的資料進來跑同一批程式。寫完後才發現,這種方法一點也不聰明,因為屆時還是要一行一行的呼叫 macro,並把替換資料的參數改成要執行的資料名稱。於是我一直在苦思如何更有效率的來處理這種「重複執行同一個 macro」的情況,但終究無解。直到最近在 PharmaSUG 2007 裡面才發現一個技術文件談到如何解決這類的問題。該作者 Nicholas Nerren 提出三個方法來克服繁雜的重複呼叫 macro 的動作。

先假設要執行的 macro 是下面這串程式:

%macro process(dataset);
data &dataset;
set mylib.&dataset;
patient = inv||pat;
factor = 0.85*base;
run;
proc print data = &dataset;
run;
%mend process;


方法一:利用 CALL EXECUTE 函式

首先,先用 PROC CONTENTS 把 library 裡面所有的資料檔都列出來,並另存成 content 這個新的資料。在 content 中,我們址保留 libname 和 memname 這兩個變數。

proc contents data mylib._all_ out=content(keep=libname memname) noprint;
run;


接著,隨便開一個新的資料(此處是開一個虛無的資料集 _null_,該資料在 SAS 關閉後會自動消失),然後吧 content 給呼叫進來。最後,下面紅色的程式碼就可以把每一個資料都自動丟進 process 這個 macro 來執行。

data _null_;
set content;
by libname memname;
if first.memname = 1 then call execute('%process('||memname||');');
run;


方法二:使用 RUNIT macro

作者提供一個名為 RUNIT 的 macro:

%macro runit(dataset,var);
%let dsid = %sysfunc(open(&dataset));
%do i = 1 %to %sysfunc(attrn(&dsid,nobs));
%let thisobs = %sysfunc(fetchobs(&dsid,i));
%let thisvar = %sysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,&var))));
%process(&thisvar);
%end;
%let rc = %sysfunc(close(&dsid));
%mend runit;


和方法一雷同,先跑 PROC CONTENTS 程序:

proc contents data mylib._all_ out=content(keep=libname memname) noprint;
run;


接著把存出來的 content 給排序一下:

proc sort data=content nodup;
by libname memname;
run;


最後再執行這個 RUNIT macro 即可:

%runit(content,memname);

方法三:改良版 RUNIT macro


此法是將前一個 RUNIT macro 作一點改良:

%macro runit() / parmbuff;
%let count = 1;
%let dataset = %scan(&syspbuff,&count,%str((),));
%do %while (&dataset ^= );
%process(&dataset);
%let count = %eval(&count+1);
%let dataset = %scan(&syspbuff,&count,%str((),));
%end;
%mend runit;


一樣先跑 PROC CONTENTS 程序:

proc contents data mylib._all_ out=content(keep=libname memname) noprint;
run;


接著跑一段 PROC SQL:

proc sql noprint;
select distinct(memname) into :list separated by ','
from content;
quit;


然後執行 RUNIT macro:

%runit(&list);

這兩個 RUNIT macro 最大的差別只是在讀取資料的速度上有快慢,但只要使用者不是一次導入上千上萬個資料集,這一點小差別是感覺不出來的。

CONTACT INFORMATION
Your comments and questions are valued and encouraged. Contact the author at:
Nicholas Nerren
PPD, Inc.
929 North Front Street
Wilmington, NC 28401-3331
Work Phone: 910-558-2974
E-mail: nicholas.nerren@wilm.ppdi.com
CODE { display: block; /* fixes a strange ie margin bug */ font-family: Courier New; font-size: 8pt; overflow:auto; background: #f0f0f0 url(http://klcintw.images.googlepages.com/Code_BG.gif) left top repeat-y; border: 1px solid #ccc; padding: 10px 10px 10px 21px; max-height:200px; height:200px; // for IE6 line-height: 1.2em; }