公告

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

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


2007年2月28日 星期三

SAS-L – A VERY POWERFUL RESOURCE FOR SAS USERS WORLDWIDE

原文載點:http://www2.sas.com/proceedings/sugi28/247-28.pdf

這一篇由 JoAnn Matthews 等人於 2003 年發表在 SUGI 28 的文章,並不是在講關於 SAS 的用法,而是在介紹一個很棒的 SAS 討論群組,叫做:SAS-L

這有點像是一個新聞群組,也可以說是網頁論壇,由 University of Georgia 所提供的平台,讓眾多 SAS 的愛好者在這邊互相交換心得,並且提出自己遇到的問題。雖然 SAS-L 的介面並不太具有親和力,不過這邊的確吸引了很多高手前來。如同一般的論壇一樣,如果沒有註冊,則只能觀看留言。註冊後就可以開始貼文章了。 JoAnn 在文內有詳細介紹如何註冊,如何用新聞群組軟體來觀看,或搭配 Google 搜尋。有興趣的人可以瞧一下她的講解。

2007年2月27日 星期二

Splitting a Large SAS® Data Set

原文載點:http://www2.sas.com/proceedings/sugi28/075-28.pdf

當處理龐大檔案,若沒有很強的硬體設備,通常我們會建議將檔案稍做一些切割以便分別處理。但 SAS 中沒有任何指令是可以任意切割資料,即便最簡單的「均分」都沒有指令提供。Selvaratnam Sridharma 於 2003 年的 SUGI 28 發表了一篇關於切割資料的文章,並寫了兩個小 macro 分享給大家。


The %split1 Macro

第一個 macro 程式可供使用者將資料切割成數塊,每一塊包含 n 個觀測值,SAS 會自動命名新的小資料集,而切剩的資料會放進最後一個小資料集中(此資料的觀測值總數就會小於 n)。

%macro split1(num);
data _null_;
if 0 then set orig nobs=count;
call symput('numobs',put(count,8.));
run;
%let m=%sysevalf(&numobs/&num,ceil);
data %do J=1 %to &m ; orig_&J %end; ;
set orig;
%do I=1 %to &m;
if %eval(&num*(&i-1)) <_n_ style="color: rgb(51, 51, 255);">%mend split1;


範例:

data orig;
do i =1 to 84;
output;
end;
run;
%split1(18);


資料集 orig 會被切割成五塊(orig_1~orig_5)。前四塊各含 18 個觀測值,最後一個則只有 15 個觀測值。

The %split2 Macro

第二個 macro 會將檔案切成 n 塊,每塊的觀測值會相等。同樣地,切剩的資料仍舊會被丟到最後一個資料集中。

%macro split2(num);
data _null_;
if 0 then set orig nobs=count;
call symput('numobs',put(count,8.));
run;
%let n=%sysevalf(&numobs/&num,ceil);
data %do J=1 %to &num ; orig_&J %end; ;
set orig;
%do I=1 %to #
if %eval(&n*(&i-1)) <_n_ style="color: rgb(51, 51, 255);">%mend split2;


範例:

data orig;
do i =1 to 85;
output;
end;
run;
%split2(6);


資料集 orig 會被切成六塊(orig_1~orig_6)。前五塊各含 15 個觀測值,最後一個資料集則只有 10 個觀測值。

Contact Information
Selvaratnam Sridharma
U.S. Census Bureau
4700 Silver Hill Road, Stop 8400
Washington, D.C. 20233
301-763-5398

2007年2月26日 星期一

Implementing a Multiple Comparison Test for Proportions in a 2xc Crosstabulation in SAS

原文載點:http://www2.sas.com/proceedings/sugi31/204-31.pdf

一般在分析一個 binary 變數在兩個不同群體下的比例是否一致,我們會先繪製一個 2 x 2 表格,然後用卡方分析來檢定兩個比例。如果有 c 個群體,則卡方分析也可以檢定 2 x c 表格。然而,此時的虛無假設變成:

H0: P1=P2=...=Pc

如果拒絕了這個虛無假設,我們只能得到這個結論:至少有兩個比例互不相等。可是有哪些是不相等的,卡方分析並無法提供進一步的報表。於是, Alan C. Elliott 和 Joan S. Reisch 兩人在 2006 年的 SUGI 31 發表了一篇關於在卡方分析底下的多重比較方法,並提供 macro 程式給人使用。

在使用這個 macro 前,需要做一點小小準備。首先,要用 PROC FREQ 製作 2 x c 表格,並且將每個 cell 裡面的 frequency number 另存出來。如圖所示:



如果群組數目不是很多,可以另外新建一個資料,把這些數值(紅色圈選處)寫進去。當這個資料準備好後,立刻接上 Alan C. Elliott, Joan S. Reisch 所提供的 macro,就等於是大功告成了。以他們的程式為例:

data;
do a = 1 to 2;
do b = 1 to 4;
input wt @@;
output;
end;
datalines;
32 43 16 9
55 65 64 16
run;
%compprop(n=4);


結果如下所示(不甚清楚,可詳見原文):



Macro 程式如下所示,可直接複製使用:

**************************************************************
* SAS® macro to perform multiple comparisons on proportions *
**************************************************************;
%macro compprop(n=);
proc freq;
weight wt;
tables a*b /chisq out=freqout outpct;
run;
*Select variables from data set freqout;
data propmc;set freqout;
if a=1;
* Apply an arcsine transformation to the proportions;
pi180=180/3.141;
n=(count*100)/pct_col;
p=0.5*(arsin(sqrt(count/(n+1)))*pi180 + arsin(sqrt((count+1)/(n+1)))*pi180);
proc sort data=propmc;by p;
proc transpose data=propmc out=propmc1;var n p b;
*Separate out pieces of the data and recombine into the needed data set;
data tmp;set propmc1;
if _N_=1;
%do i=1 %to &ncols;
n&i=col&amp;amp;amp;i;
%end;
data tmp2;set propmc1;
if _N_=2;
data tmp3;set propmc1;
if _N_=3;
%do i=1 %to &ncols;
lab&i=col&amp;amp;amp;i;
%end;
drop col1-col&ncols;
data propmc3;merge tmp tmp2 tmp3;
run;
* The array q05() contains the table lookup values for the q statistic with infinity
degrees of freedom for k groups (1 to 10);
data propmc2;set propmc3;
array p(*) col1-col&ncols;
array na(*) n1-n&ncols;
array lab(*) lab1-lab&ncols;
array q05(10);
q05(1)=.;
q05(2)=2.772;
q05(3)=3.314;
q05(4)=3.633;
q05(5)=3.585;
q05(6)=4.030;
Statistics SUGI 31 and Data Analysis
5
q05(7)=4.170;
q05(8)=4.286;
q05(9)=4.387;
q05(10)=4.474;
icompare=&n;
qcrit=q05(icompare);
* print out the multiple comparison results table;
msg=' ';
file print;
put ' Tukey Style Multiple Comparisons of Proportions';
put ' ';
put ' Compare Diff SE q q(.05) Conclude';
put ' ------------------------------------------------------------';
iskiprest=0;
* as the table is constructed, determine the correct Conclude message;
do i=icompare to 1 by -1;
do j=1 to i;
if i ne j then do;
se=round(sqrt((410.35/(na(i)+.5))+(410.35/(na(j)+.5))),.01);
diff=round(p(i)-p(j),.01);
q=round((p(i)-p(j))/se,.01);
iskip=0;
* Do not test if last one in series was Accept;
if msg='Accept' and j ne 1 then do
msg='Do not test';iskip=1;
put @5 lab(i) 'vs' @10 lab(j) @15 msg;
msg='Accept';
end;
* Do not test last one if previous one was Accept;
if iskip=0 and iskiprest=1 then do
msg='Do not test';iskip=1;
put @5 lab(i) 'vs' @10 lab(j) @15 msg;
end;
if iskip=0 then do;
if q gt qcrit then msg='Reject';else msg='Accept';
put @5 lab(i) 'vs' @10 lab(j) @ 15 diff @25 se @35 q @45 qcrit @55 msg ;
end;
if j=1 and (msg='Accept' or msg='Do no test')then iskiprest=1;
retain msg;
end;
end;
end;
put ' ';
put ' Reference: Biostatistical Analysis, Fourth Edition, Jerrold Zar, 1999, p
564.';
run;
%mend compprop;


附帶一提的是,原文內有提到 c 的數目是從 2 到 10,但沒說超過 c 會發生什麼事情。所以在使用時若群組數目超過 10 的話,請特別留意任何錯誤訊息。

CONTACT INFORMATION
Alan C. Elliott
Center for Biostatistics and Clinical Science
UT Southwestern Medical Center Dallas
5323 Harry Hines Blvd
Dallas TX 75390-8822
Phone (214) 648-2712
Fax (214) 648-7673
alan.elliott@utsouthwestern.edu

You Can’t Stop Statistics: SAS/STAT Software Keeps Rolling Along

原文載點:http://www2.sas.com/proceedings/sugi31/185-31.pdf

兩位 SAS 內部人員 Maura Stokes 和 Robert Rodriguez在 2006 年的 SUGI 31 發表了一篇相當重要的文章,內容主要在說明最新版本的 SAS 9.2 新功能。其中比較重要的是增加了一些舊版沒有的「內建」的程序,如 GLIMMIX、QUANTREG、GLMSELECT(其實這些可以從 SAS 官網下載外掛程式讓舊版的 SAS 使用)。此外,許多高解析的圖表也一併內建到 ODS 裡面,讓使用者可以輕鬆的繪製一些以往要花很多程式碼才能完成的圖形。由於這篇文章涵蓋的範圍太廣,有新的程式碼,有新的輸出報表和新的高解析圖表,因此就僅拿一些比較「炫」的部分來分享一下。

GENERALIZED LINEAR MIXED MODELS

PROC GLIMMIX 程序在 2005 年就已經發表了,不過僅供外掛,在最新的 SAS 中已經加入了這個相當具有威力的程序。程式的寫法和 PROC MIXED 很像,如下所示:

proc glimmix data=Neuralgia; class Treatment Sex;
model Pain= Treatment Age Treatment*Age /solution oddsratio;
ods select ParameterEstimates OddsRatios;
run;


連輸出結果都很像:




當然報表解讀又是另一回事情了。想更進一步瞭解 PROC GLIMMIX 的使用方法可以參考

Schabenberger, O. (2005). “Introducing the GLIMMIX procedure for Generalized Linear Models,” Proceedings of the Thirtieth Annual SAS Users Group International Conference. Cary, NC: SAS Institute Inc.

MODEL SELECTION

一般在配適線性模式時,如果程序裡面已經有內建一些選擇變數的 option,如 PROC REG 的 selection statement 下的 method 指令,可以用 forward、backward 和 stepwise 來挑。如果程序裡面沒有這類 option,如 PROC GENMOD,就要用手動的方式來嘗試。PROC GLMSELECT 程序則是針對任何標準的廣義線性模式來做最佳變數選擇。特別要注意的是,他只能處理 univariate response 的情況,而無法處理 multivariate responses。此外,其相關的圖形也已經完全和 ODS 整合起來,所以讓我們來看看這個範例:

ods graphics on;
proc glmselect data=baseball plots=all;
class league division; model logSalary = nAtBat nHits nHome nRuns nRBI nBB yrMajor crAtBat crHits crHome crRuns crRbi crBB league division nOuts nAssts nError / details=all selection=stepwise(select=sl) stats=all;
run;
ods graphics off;


此處要特別將 ODS 可以產生的效果提出來一下。此程式啟動了 ODS 功能(紅色標示處),並且在 PROC GLMSELECT 後加上 plots=all 的選項,這樣 SAS 就會自動產生所有在此程序可以生出來的圖形。當然,如果妳只想指定某一張圖形的畫,就要指定特定名稱。這些名稱都有紀錄在 SAS 官網上面,供使用者免費下載。如果指定 plots=criterionpanel ,則 SAS 只會產生 Criteron Panel 這張圖。該圖秀出所有可供判斷最佳模式的圖表。為免仍有人看不懂這張表,SAS 乾脆在圖上標上☆符號,告訴使用者這個就是最佳模式!如下圖所示:



ANNOUNCING NEW SOFTWARE: SAS STAT STUDIO

新版 SAS 還可以呼叫一個新的軟體名為 SAS STAT STUDIO。這整合了包含 SAS 資料、程式、輸出報表和圖形,且可以利用表單點選的方式來完成分析(類似SPSS吧)。但文內並沒有特別敘述如何操作,只秀了一張很 fancy 的圖,不過我們可以拭目以待。



EFFECT PLOT IN PROC LOGISTIC

其實 logistic regression model 並沒有什麼太重要的圖需要特別繪製,因為重點都是在如何解釋那個 Odds Ratio。不過新的 ODS 仍舊聊勝於無地加了幾張圖。只要在 PROC LOGISTIC 後面指定 plots option 就可完成。裡面用了一個範例如下:

ods graphics on;
proc logistic data=one plots=(effect(clband yview=(.5,1)));

class Treatment Diagnosis / param=ref; model Cured/N= Diagnosis Treatment;
ods select effectplot;

run;

ods graphics off;


這個程式可以生出一張 Predicted Probabilities Plot:



BAYESIAN ANALYSIS FOR THE PIECEWISE EXPONENTIAL MODEL IN PROC PHREG

這回連 PROC PHREG 裡面都加入了新的指令可以用 Bayesian analysis 來配適 piecewise exponential model。我在 2005 年修 Bayesian analysis 時有學到這段,可是當時只有 WINBUGS 這個軟體可以比較有效率的估計參數和繪製圖表(但語法還是很難學,而且還要上工作站去跑程式)。現在 SAS 也加入了這個功能,方法相當簡單:

ods graphics on;
proc phreg data=Exposed;
model Days*Status(0)=Treatment Sex;
bayes piecewise=loghazard;
run;
ods graphics off;


令人訝異地,只需要補上紅色那段程式碼,就可以完全搞定。要產生相關圖表甚至不需要在 PROC PHREG 後面加上 plots 選項,只要直接呼叫出 ODS 即可。

在這裡僅列出圖表。



想要進一步地瞭解貝氏理論在倖存分析上的應用,可以參考下面這本教科書:

Ibrahim, J. G., Chen, M., and Sinha, D. (2001). Bayesian Survival Analysis, New York: Springer.

附帶一提的是,本書的作者 Joe Ibrahim 是北卡大生統系的貝氏理論權威,當年教我貝氏理論和高等數理統計學的就是這位大師。Joe 人是不錯,可是他的考試每次沒耗個六七個小時是不可能寫的完的。。。。。

BAYESIAN ANALYSIS FOR THE POISSON REGRESSION MODEL IN PROC GENMOD

同樣地,在 PROC GENMOD 下,貝氏理論也可以輕鬆套用了!^^

ods graphics on;
proc genmod data=liver;
model y = x1-x6 / dist=poisson;
bayes;
run;

ods graphics off;


這回連 bayes statement 後面啥東西都不用加了!Orz

THINGS GO BETTER WITH PROC TTEST

連最簡單的 T-test 都有新的花樣!除了使用 ODS 可以產生長條圖和常態曲線外,一個很炫的「交叉分析」也出現在報表當中。讓我們來先看看程式怎樣寫:

ods graphics on;
proc ttest data=asthma;
var PEF1 PEF2 / crossover= (Drug1 Drug2);

run;
ods graphics off;


紅色那段程式碼就是 SAS 9.2 為 PROC TTEST 程序新增的指令。這個指令新到連目前 SAS 線上手冊的 PROC TTEST 程序指令集都還沒有加入。

他會產生下面這些圖形:



REGRESSION DIAGNOSTICS FOR GEE MODELS IN THE GENMOD PROCEDURE

早期要使用 GEE 在 PROC GENMOD 時,並沒有任何關於模式診斷的指令可供使用。我和一位老師討論過為什麼大家在做 GENMOD 時不重視模式診斷,他自己也說不上來,總之結論就是這一部份的理論很晚才發展出來,因此 SAS 在一開始時沒有加入相關程式。這部分的理論大約是在 90 年代末期才陸續有一些論文發表出來。我們先來看一下程式:

ods graphics on;
proc genmod data = preqaq99 descending plots=(cooksd clustercooksd);
class pract_id ;

model bothered = female age dayacc severe toilet/d=bin itprint ;
repeated sub=pract_id/corr=exch modelse;
run;

ods graphics off;


紅色部分的程式碼可以呼叫大家最經常拿來鑑定 influential data 的 Cooks' D 值。如下所示:



關於這部分的理論,可以參考下面這篇論文:

Preisser JS, Qaqish BF (1999), “Robust Regression for Clustered Data with Application to Binary Responses”, Biometrics, 55, 574–579.

其中 John Preisser 是我的 committee member 之一,而 Qaqish 是他的博士論文指導教授。Qaqish 本人以前是 GEE 的發明者之一梁賡義博士的博士論文指導學生。Preisser 和 Qaqish 目前仍在北卡大生統系執教著。

POWER AND SAMPLE SIZE APPLICATION

SAS 其實已經有可以計算 power analysis 的程序,如 PROC POWER 和 PROC GLMPOWER。其中 PROC GLMPOWER 在 9.2 版前要外掛。文內並沒有特別說 9.2 版會加入這個程序。不過倒是有研發一個視窗介面的程式讓使用者用點選的方式就可以完成 power analysis。這對於一些對程式不熟悉的人來說應該是相當方便。

圖就不另外列了,文章裡面有。

ENHANCEMENTS TO SAS/STAT PROCEDURES

除此之外,還有其他一些林林總總的新增項目,僅列出我覺得比較重要的:

  • PROC GENMOD 新增可以做 model selection criteria 的 AIC 和 QIC 值。
  • PROC MIXED 新增 method=laplace。
  • 在 survery analysis 程序中加入 Jackknife 法。
  • PROC PHREG 新增 class statement。
  • 新增 PROC QUANTREG 程序。
  • PROC NPAR1WAY 新增 conover test 和中位數差的 Hodges-Lehmann 信賴區間。
  • PROC GENMOD 和 PROC PHREG 可算出 cumulative residuals。


2007年2月24日 星期六

An Introduction to SQL in SAS®

原文載點:http://www2.sas.com/proceedings/sugi30/257-30.pdf

歷年來有很多人在 SUGI 寫過 PROC SQL 的教學文件,但我覺得這一篇寫的比較好,所以在此推薦給需要的人。這是 Pete Lund 在2005 年 SUGI 30 所發表的一篇頗長的文章。PROC SQL 提供很好的語法讓使用者可以在某個很大的資料庫裡面找到想要使用的資料。其基本的語法有:

• SELECT(選擇變數或欄位名稱)
• FROM (指定資料庫名稱)
• ON (給定搜尋資料所需的條件)
• WHERE (給定搜尋資料所需的「觀測值」條件)
• GROUP BY (分群整合)
• HAVING (整合所需條件)
• ORDER BY (按欄位順序)

最簡單的 PROC SQL 程序可以寫成這樣:

proc sql;
select BookingDate,
ReleaseDate,
ReleaseCode
from SASclass.Bookings;
quit;


簡單的說,就是從 SASclass library 裡面的 Bookings 資料集中挑出 Booking Date, ReleaseDate 和 ReleaseCode 三個變數。

如果想要把上述抓出的變數另存成新的資料集,可用「create table ... as」語法:

proc sql;
create table ReleaseInfo as
select BookingDate,
ReleaseDate,
ReleaseCode
from SASclass.Bookings; quit;


三個變數就會被存入 ReleaseInfo 這個新的資料集裡面。

如果想要對變數做 rename, label, length 和 format 的動作,只要改一下上面那個程式成:

proc sql;
create table ReleaseCodes as
select BookingDate as BD,
ReleaseDate as RD format=monyy7.,
ReleaseCode label='Rel Code'
from SASclass.Bookings; quit;


此時,BookingDate 會被更名為 BD,ReleaseDate 會被更名為 RD,且格式被改為 monyy7.
的格式,ReleaseCode 則為被貼上 Rel Code 的標籤。

如果想要選所有的變數,不需要全部輸入,只要一個星星就可解決:

proc sql;
create table BookingCopy as
select *
from SASclass.Bookings;

quit;


另外,在選擇變數時,可以做一些變數間的簡單數學運算,並將結果存成新的變數,如:

select
BookingDate,

ReleaseDate,
ReleaseDate – BookingDate as LOS
from SASclass.Bookings;


如此程式不但會選出 BookingDate 和 ReleaseDate 兩個變數,還會將兩個相減另存成新變數 LOS。

如果選擇變數時要順便加上一些條件,由於 PROC SQL 程序中並無法使用「If ... then ... else
...」語法,所以要用別的方法取代,其基本型態如下:

CASE
WHEN [condition] THEN [value]
WHEN [condition] THEN [value]
ELSE [value]
END AS [column name]


簡單說就是把「If ... then ... else」改成「When ... then ... else」,最後用「END AS」存出來。有個簡單範例:

select *,
case
when InfractionType eq 'IS' then 'X'

when Severity eq 'S' then 'X'
else ' '
end as CheckThese

from SASclass.Infractions;


這個程式不止選出所有的變數,還另為設了一個新變數「CheckThese」,條件是,當字串變數 InfractionType 等於 IS 還有字串變數 Severity 等於 S 時,就在新變數上填上一個 X,如果沒有符合條件的話就留空白。

下面是一個比較複雜的例子:

case
when InfractionType eq 'IS' and Severity eq 'S' then 1
when InfractionType eq 'IS' then 2
when Severity eq 'S' then 3
else 0
end as CheckThese


如果不同的 when 接的是同樣的變數,那麼程式可以稍微縮寫一下。比方說:

case
when Age lt 13 then 'PreTeen'
when Age lt 20 then 'Teenager'
else 'Old Person'
end
as AgeGroup


兩個 when 後面的變數都是 Age,就可以把他挪到 case 後面,如下所示:

case Age
when lt 13 then 'PreTeen'
when lt 20 then 'Teenager'

else 'Old Person'
end as AgeGroup

如果只是單純地要限制選擇出來的變數符合某些條件,可直接用 where 語法來處理:

select *
from SASclass.Infractions
where Severity eq 'S';


上述程式雖然選擇所有的變數,但只有符合 Severity 變數是 S 的條件才會納入。

select *
from SASclass.Infractions
where Severity eq 'S' and InfractionType eq 'II';


上述程式一次用兩個條件,只要用 and 把兩個條件黏起來即可。

如果只想挑出含有 missing data 的變數,可用 is null 或 is missing 來指定:

select *
from SASClass.Charges
where SentenceDate is null;


如果要選擇某個範圍內的變數,可用 between 語法來指定:

select *
from SASClass.Bookings
where BookingDate
between '1jul2001'd and '30jun2002'd;


上述程式就是只限定 BookingDate 介於 1jul2001 到 30jun2002 這段時間才會輸出。這邊很有彈性的設定是,起點和終點不用限定位置,誰放前誰放後並不會影響結果。

以下要介紹 PROC SQL 針對字串變數所能處理的強大功能。

如果要找一個字串變數裡面含有某些文字的觀測值,可用「contain」這個語法來完成。如:

select *
from SASClass.Charges
where ChargeDesc contains 'THEFT';


這個程式執行後,只要 ChargeDesc 裡面含有 THEFT 這個字的觀測值都會被抓出。因此,諸如“AUTO THEFT”, “THEFT 2”, “PROPERTY THEFT”這些字串全部都會被抓出。如果連大小寫也不管,可以把 upcase() 這個函數套在變數上,如下所示:

where upcase(ChargeDesc) contains 'THEFT'

這樣會連“Car Theft”和“Theft of Property”這些字串也一併挑出來。特別注意的是,contain 語法並不會考慮空格,因次既使 THEFT 這個字跟其他字連在一起(如:“THEFT2” 和“AUTOTHEFT”),也會挑出。

PROC SQL 還可以做模糊比對,使用者不用很明確瞭解要尋找的條件,主要加入「Like」運算子還有一些可供模糊比對的代碼即可。這些代碼有:
  • _ (underscore) 可以模糊比對任何單一字母或數字
  • % (percent sign) 可以模糊比對任意多個字母或數字
以下提供四個範例:

where ChargeDesc like '%THEFT%';

這種用兩個 % 把一個字串包起來的模糊比對方法,跟 contain 的效果一模一樣。

where ChargeDesc like 'THEFT%';

如果只用一個 % 在 THEFT 後面,這只會找出以 THEFT 開頭的字串,如 “THEFT”, “THEFT 2” and “THEFT-AUTO”。

where ChargeDesc like '%THEFT';

如果只用一個 % 在 THEFT 前面,這只會找出以 THEFT 結尾的字串,如“AUTO THEFT” 和 “3RD DEGREE THEFT”。

where ChargeDesc like '%_THEFT';

如果使用 _ 在 % 和 THEFT 之間,則任何數字或字母填進那個空格的 THEFT 字串都會被挑出,即使是空格也包含,因此像“AUTO THEFT” and “AUTO-THEFT” 都會被挑出。如果不加底線,而只留一個空白在 % 和 THEFT 中間的話,那就只會傳回“AUTO THEFT”了。

接著,如果你聽到每個字串,但不知道正確拼法,只要輸入相近的拼音,PROC SQL 也會幫你找出所有可能的結果。這背後的道理涉及到 1918 年 Margaret K. Odell 和 Robert C. Russel 所發明的 Soundex algorithm。PROC SQL 很人性化的用「=*」來代表「sounds like」。

select LastName, FirstName
from SASClass.Inmates
where LastName =* 'Smith';


上述程式是要從 LastName 這個變數中找出聽起來像Smith 的字串,還順便會做個次數統計,輸出如下報表:



This is pretty cool!!!!!

接著,PROC SQL 也提供排序功能。

select *
from SASClass.Charges
where ChargeType eq 'A'
order by FIM;


則上述的結果會以 FIM 遞升的次序來排列。如果要用依序用多個變數排列,就在 order by 後面依序加上,記得用逗號分開。如下所示:

select OrgAgency format=$Agency., BookNum, FIM
from SASclass.Charges
where ChargeType eq 'A'
order by FIM,OrgAgency;


如果要以「遞減」的方式排序,則可在變數後面加上 desc 這個字。

select OrgAgency format=$Agency., BookNum, FIM
from SASclass.Charges
where ChargeType eq 'A'
order by FIM desc,OrgAgency desc;


order by 語法還可以用變數的第幾個字母或字串來排列。假設上述例子要特別指定用 FIM 的第一個字母來排列,則語法如下:

select OrgAgency format=$Agency., BookNum, FIM
from SASclass.Charges
where ChargeType eq 'A'
order by substr(FIM,1,1), OrgAgency;


substr 是 SAS DATA 程序中的一個函數,可以指定從第幾個字母開始割出幾個字母出來,因此 substr(FIM, 1, 1)即表示從 FIM 的第一個字母開始割一個字母出來,亦即選定 FIM 變數的第一個字母。

此外,order by 針對字串變數的預設雖然是依照字母排列,但是會先排完大寫字母,然後才排小寫字母。如果要叫他不要管大小寫,那可以用upper( ) 函數來更改,如:

select ChargeDesc
from MixedCase
order by upper(ChargeDesc);


要特別注意的一點是,upper( ) 和lower( ) 函數是 PROC SQL 專用,若在 SAS 其他的地方使用類似的函數,要改成 upcase( ) 和 lowcase( )。

之前有提到,我們可以在 PROC SQL 裡面做一些小小的運算,然後將運算結果輸出成新的變數。下面列出一些常用的運算函數:

  • Avg:平均數
  • NMiss: missing values 的個數
  • Count:non-missing values 的個數
  • Prt :計算P(T>|t|),T 代表 T 分配。
  • CSS:校正後的sum of squares
  • Range:全距
  • CV:變異係數coefficient of variation
  • Std:標準差standard deviation
  • Freq :同 Count (same as Count)
  • StdErr:標準誤standard error of the mean
  • Max :最大值maximum value
  • Sum:總和sum of values
  • Mean:同 Avg
  • T:T-test 的 T 值
  • Min :最小值
  • USS :無校正的 sum of squares
  • N:同 Count
  • Var:變異數
下面有一些小範例:

select *, sum(GoodTime,CreditDays) as OffDays
from SASClass.Charges;


上述程式除了選擇所有變數外,另外將 GoodTime 和 CreditDays 兩個變數的加總並另存為新變數 OffDays。如圖所示:



當然,PROC SQL 也可以對單一變數內的觀測值進行運算:

select sum(Bail) as TotalBail, mean(Bail) as MeanBail, max(Bail) as MaxBail, nmiss(Bail) as NoBailSet
from SASclass.Charges;


上述程式針對 Bail 這個變數作了四個統計運算,並且分別存成四個新的變數。如圖所示:



PROC SQL 也可以針對類別變數做個別的操作。比方說:

select BookNum, sum(Bail) as TotalBail
from SASClass.Charges
group by BookNum;


這個程式會針對 BookNum 內不同的類別各自做 sum(Bail) 的動作。如圖所示:



PROC SQL 另外提供一個簡單的 group by 方法。如果要 group by 多個變數,可以只填上這些變數在 select 後面的「位置」,這樣 SAS 仍可以準確的抓出你要 group by 的變數。如:

select FIM, ChargeType, mean(Bail) as TotalBail
from SASClass.Charges
group by FIM, ChargeType;


這個程式要以 FIM 和 ChargeType 做 group by 的動作。由於他們在 select 後面的位置是第一個和第二個,因此可以用下面這個方法來取代:

group by 1,2;

接著來介紹一個特殊的運算子「distinct」。他的功能類似整理變數,把一些重複的觀測值剃掉,讓使用者可以清楚的看裡面有多少個分類。舉例來說:

select distinct Facility
from SASClass.Bookings;


如果 Facility 裡面的觀測值是「5, 5, 6, 6」這四個數字的話,則程式僅會列出「5, 6」這兩個數字。(註:我知道這個例子舉的很爛,可是突然天外飛來一筆地想要這樣解釋,這樣讀者應該會比較瞭解 :D 原文中並不是用這個例子,特此說明!)

若結合 count( ) 函數就可以知道這個變數裡面有多少個類別:

select count(distinct Facility)
from SASClass.Bookings;


以「5, 5, 6, 6」這個例子來看的話,輸出結果就是「2」。

特別注意一點,count( ) 函數只能一次針對一個變數,所以放兩個以上的變數會出現 error。如:

select count(distinct Race, Sex)
from SASclass.Inmates;


上面這個程式是不行的。解決的方法是用連結(concatenate)符號「||」把變數連起來,這樣就可以傳回兩個變數組合數目。如果要分別計算次數,那就要用數個 count( ) 才行。

select count(distinct Race||Sex)
from SASclass.Inmates;


說明完運算函數後,要特別聲明一點。經過運算函數所產生出來的新變數,不能直接用在 where 或 when 條件式內。舉例來說:

create table OffDays as
select *, sum(GoodTime,CreditDays) as OffDays
from SASClass.Charges
where OffDays gt 0;


上述程式會發生 SAS 不知道有 OffDays 這個變數存在的問題。解決的方法是在新變數前面加上「calculated」這個字,這樣 SAS 就知道這是經由某個運算函數所產生的新變數:

create table OffDays as
select *, sum(GoodTime,CreditDays) as OffDays
from SASClass.Charges
where calculated OffDays gt 0;


下面範例是 OffDays 出現在 when 條件式後的改法:

select *, sum(GoodTime,CreditDays) as OffDays,
case
when calculated OffDays gt 0 then '*'
else ' '
end as OffDayFlag
from SASClass.Charges;


最後要介紹一個和 where 很像的語法:having。 Where 的功能是可以限制僅列出挑選出來變數的條件。Having 的功能也是一樣,但特別是用在已經做了 group by 這個動作之後。如果沒有使用 group by 在你的 PROC SQL 內,則 where 和 having 的輸出結果會完全一樣。如下面這兩個程式所示:

select Race
from SASclass.Inmates
where Sex eq 'M';

select Race
from SASclass.Inmates
having Sex eq 'M';


那麼來比較一下在使用 group by 情況下,where 和 having 的不同。



左邊的結果顯示 PROC SQL 是先挑出 Sex 是 M 的觀測值,然後再根據不同的 Race 計算 Total。右邊的結果則是先計算不同 Race 的 Total,然後再把 Sex 是 M 的挑出來。因此右邊的 A 個數就是左邊所顯示的 91 個。

基本上我這篇文章省略了一些部分,有興趣的可以去看 Pete 的原文。

CONTACT INFORMATION

Pete Lund
Looking Glass Analytics
215 Legion Way SW
Olympia, WA 98501
(360) 528-8970
(360) 570-7533
pete.lund@lgan.com
www.lgan.com

A Format to Make the _TYPE_ Field of PROC MEANS Easier to Interpret

原文載點:http://www2.sas.com/proceedings/sugi29/045-29.pdf

在 PROC MEANS 程序所產生的結果中,如果用 output 指令匯出成一個新的資料集,裡面會自動多出一個叫做「_Type_」的變數。這個變數主要是傳遞變數型態。如果只拿一個變數去跑,這個 _Type_ 就變成多餘了。如果使用很多變數,理論上 _Type_ 應該要顯示到底每一個項結果是從哪些變數算出來的,可是 SAS 只給了一些毫無意義的數字來做區別。Matt Pettis 在 2005 年的 SUGI 29 分享了一個 macro 程式,可以賦予 _Type_ 更明確的訊息給使用者。

舉例來說,在一般的情況下使用 PROC MEANS 時,程式碼如下:

PROC MEANS data=work missing noprint;
class Location Directory ResponseCode;
output out=temp;
run;
PROC PRINT data=temp;
Title '_TYPE_ WITHOUT formatting'; run;


這是在計算 Location、Directory 和 ResponseCode 三個變數的含有多少 missing data。列印出來的結果如下:



由上表可知 _Type_ 變數底下都是一些無意義的數字,從 0 到 7。如果改用 Matt 提供的 macro 程式,就可以看出一些和原本不一樣的地方。如下所示:

%TypeFormat(formatname=testtyp,var=Location Directory ResponseCode);
PROC PRINT data=temp;
var _type_ Location Directory ResponseCode _freq_;
format _type_ testtyp.;
title '_TYPE_ WITH formatting'; run;


參數解釋:
formatname=給定 _type_ 變數格式的一個代號,任取
var=所有要用在 PROC MEANS 裡面的變數

輸出報表如下:



可以看到 _Type_ 底下已經出現明確的變數名稱,這樣就比較容易去抓出我們真正要使用到的訊息,看錯行的機會也會減少。

Macro 程式檔可以到原文載點的 Appendix 裡面找到。

CONTACT INFORMATION
Matt Pettis
Thomson West Group
610 Opperman Dr.
Eagan, MN 55417
+1 (651) 848-3976
Matt.pettis@thomson.com

Slicing and Dicing the SAS Data Set


這是 William C. Murphy 於2005年的 SUGI 30 發表的一篇關於如何在 SAS 底下進行資料切割和合併的文章。由於 SAS 讀取資料是一欄一欄(column)地讀取,所以的操作也是以欄位為優先,因此當要切割或合併對象是行位(row)時,有時在沒有特定條件下來設定的話就很難用直覺的方式去處理。Howard M. Proskin 提出了幾個方法讓這類的資料處理更方便。

一般來說 SAS 是用這種方式讀取已經存在的資料:

data Out;
set Source;
run;


如果想要從某一個觀測值開始讀取的話,可用「firstobs=n」:

data Out;
set Source (firstobs=3);
run;


上述程式表示從資料 Source 的 第三筆開始讀取。

當然,想要叫他讀到某個觀測值停止的話,就用「obs=n」來界定停止點:

data Out;
set Source(firstobs=3 obs=10);
run;


THE POINT= OPTION

如果只想讀「一筆」資料,可以將 firstobs 和 obs 的參數設定成同樣的數值,不過有另外一個比較彈性的寫法,那就是使用「point」語法:

data Out;
Slice=17;
set Source point=Slice;
output;
stop;
run;


這個程式先設定一個暫時的變數叫做「Slice」,並且給定一個數值「17」給他,然後再把 Slice 給定到 point 語法。output 和 stop 這兩個語法一定要加上,因為 output 可以輸出最後的結果,而 stop 是強制結束這個程式繼續去讀下面的資料。

如果想一次讀進不連續的多筆資料,可以配合 do loop 來讀取:

data Out;
do Slice=17, 1, 23, 17, 5, 6;
set Source point=Slice;
output;
end;
stop;
run;


SAMPLING DATA SETS

想要隨機從一個很大的資料裡面抽出一些樣本的話,可以用下面這個程式:

data Sample;
do i=1 to 1000;
Slice=int(nObs*ranuni(123456789));
set BigDataSet point=Slice nobs=nObs;
output;
end;
stop;
run;


ranuni 語法可以從後面的 seed(123456789)中以均勻分配的方式生出亂數出來。

COMBINING DATA SETS


如果有兩個資料想要合併,通常會用 merge 語法:

data Medical;
merge Physical Drugs;
by Patient;
run;


但如果你某筆資料要以「一對多」的方式合併,打個比方說,假設某個資料有病人的資料,但每個病人可能會服用多種藥物,藥物資料卻是放在另一個資料裡面,此時要合併的話,光用 merge 的話 SAS 只會給你一個大大的 error。不過只要使用下面這個程式就可以輕鬆完成這種特殊情況的資料合併。

data Medical;
set Physical (rename=(Patient=tmpPatient));
do i=1 to nObs;
set Drugs nobs=nObs point=i;
if Patient=tmpPatient then output;
end;
run;


最後 William 有提供一個可以很奇怪的 macro,有興趣的人可以自己去看看。

Your comments and questions are values and encouraged. Contact the author at
William C. Murphy
Howard M. Proskin & Associates, Inc.
300 Red Creek Dr., Suite 330
Rochester, NY 14623
Phone 585-359-2420
FAX 585-359-0465
Email wmurphy@hmproskin.com or wcmurphy@usa.net
Web www.hmproskin.com

2007年2月23日 星期五

A SAS Macro to Count Consecutive Days

原文載點:http://www2.sas.com/proceedings/sugi29/069-29.pdf

本篇是由 Pingping Zhang 於2004年的 SUGI 29 發表出來。篇幅很短,只有三頁。目標也很簡單,就是提供一個 macro 程式來計算某些具有連續日期的資料,如:



那個 macro 程式是這樣寫的:

%macro CONSECUTIVEDAYS ( indata=, /* name of inputting data set */
subject=, /* id variable, such as patid, patno, patient */
chkdate=, /* date variable that we will count the number of consecutive days, need to be numerical SAS date */
numconday=, /* at least how many consecutive days required to output */
outdata= /* name of outputting data set */
);

%*****sorting data by subject and date *****;
proc sort data=&indata nodupkeys;
by &subject &chkdate;
run;
%*****flagging the same number when date is consecutive to the previous date *****;
data one;
set &indata;
by &subject &chkdate;
tempdate=lag(&chkdate);
if first.&subject then tempdate=.;
retain cflag;
if first.&subject then cflag=1;
if &chkdate ne tempdate+1 then cflag=cflag+1;
run;
%*****counting the number of same flags *****;
data two(keep= &subject cflag consecutive);
set one;
by &subject cflag;
retain consecutive;
if first.cflag then consecutive=0;
consecutive=consecutive+1;
if last.cflag and consecutive>=&numconday;
run;
%***** merging two (counts) with one (original) *****;
data three;
merge one two(in=a);
by &subject cflag;
if a;
run;
SUGI 29 Coders' Corner
4
%***** getting one observation per subject when it has at least required consecutive day ***;
proc sort data=three;
by &subject cflag &chkdate;
run;
data four;
set three;
by &subject cflag &chkdate;
retain firstdate;
if first.cflag then firstdate=&chkdate;
lastdate=&chkdate;
format lastdate firstdate date9.;
if last.cflag;
run;
proc sort data=four(drop=cflag &chkdate tempdate) out=&outdata;
by &subject firstdate;
run;
%mend CONSECUTIVEDAYS;


參數說明:

indata = 原始資料檔名
subject = ID變數名稱
chkdate = 日期變數名稱
numconday = 連續天數
outdata = 輸出結果的資料檔名

範例一:
%CONSECUTIVEDAYS ( indata=Diary,
subject=SUBID,
chkdate=PATDYDT,
numconday=4,
outdata=out1);


輸出結果:


說明:從Diary檔裡面找出有連續四天以上的觀測值,並且列出第一天和最後一天的日期。

範例二:
%CONSECUTIVEDAYS ( indata=Diary,
subject=SUBID,
chkdate=PATDYDT,
numconday=3,
outdata=out1);


輸出結果:


說明:從Diary檔裡面找出有連續三天以上的觀測值,並且列出第一天和最後一天的日期。

因此,如果要計算所有觀測值的連續觀測天數的話,只要把 numconday 的參數設定成 1 即可。

CONTACT INFORMATION
Your comments and questions are valued and encouraged. Contact the author at:
Pingping Zhang
Kendle International Inc.
1200 Carew Tower 441 Vine Street
Cincinnati, OH 45202
Work Phone: 513-345-1526

2007年2月22日 星期四

Using the DATASETS Procedure

原文載點:http://www2.sas.com/proceedings/sugi31/032-31.pdf

這篇是 David Fickbohm 在06年的 SUGI 31發表的報告。主要是在講一個很簡單的 SAS 程序「PROC DATASETS」。一般的資料都是在 SAS 的 Data statment 中處理,但這個程序可以加強一些效果。

GETTING TO KNOW PROC DATASETS

首先要瞭解最基本的寫法:
PROC DATASETS options;
COMMANDS options;
QUIT; RUN;


裡面可以放下面這些語法:
Detail / Nodetails – 控制觀測值、變數和標籤的 output。
List / Nolist – 控制檔案路徑的輸出。預設值:LIST。
Library – 指定 SAS Library。如果沒指定,則預設值是最後出現過的 library。如果之前都沒有使用 library,SAS會自動指向 work library。
FORCE – 無論有無 error 都強制執行此程序。
MEMTYPE – specifies the member type. (<=還沒搞清楚這要幹啥!) NOWARN - 抑制 warning messages 在 log 出現。

DETERMINING THE CONTENTS OF A LIBRARY


其實 PROC DATASETS 可以跑出跟 PROC CONTENTS 一樣的結果:

PROC CONTENTS DATA = XX06._ALL_ NODS;

David 特別強調 PROC DATASET 用的 keyword 比較少,但用順的人應該覺得沒差。

DETERMINING THE CONTENTS OF A LIBRARY AND THE ATTRIBUTES OF THE VARIABLES IN THE DATASETS

這段主要在講,在 PROC DATASETS 後的 option 寫上 all 的話可以列出所有資料訊息。

MANIPULATING FILES

PROC DATASETS 可以快速拷貝和移動資料到不同的 library,程式如下所示:

LIBNAME SS06 "C:\TEMP1";
LIBNAME KEPR "T:\DAVEKEEP";
PROC DATASETS;
COPY IN = SS06 OUT = KEPR;
QUIT; RUN;


簡單說,就是把 SS06 裡面所有的資料複製到 KEPR 的路徑下。

LIBNAME SS06 "C:\TEMP1";
LIBNAME KEPR "T:\DAVEKEEP";
PROC DATASETS;
COPY IN = SS06 OUT = KEPR;
SELECT AAA BBB;
QUIT; RUN;


上面這個比較進階,只有複製 SS06 底下的 AAA 和 BBB 這兩個資料到 KEPR 路徑下而已。

LIBNAME SS06 "C:\TEMP1";
LIBNAME KEPR "T:\DAVEKEEP";
PROC DATASETS;
COPY IN = SS06 OUT = KEPR;
EXCLUDE CCC;
QUIT; RUN;


想反地,如果想要拷貝除了 CCC 以外的資料,就用 EXCLUDE 來限定。

除此之外,還有一些施展「大絕招」的功能。

LIBNAME SS06 "C:\TEMP1";
PROC DATASETS LIBRARY = SS06;
SAVE AAA BBB;
QUIT; RUN;


用 SAVE 指令可以「只」儲存 AAA 和 BBB 兩個資料,其餘的會「全部刪除」。

LIBNAME SS06 "C:\TEMP1";
PROC DATASETS LIBRARY = SS06;
DELETE AAA BBB;
QUIT; RUN;


反過來,用 DELETE 指令只會刪除 AAA 和 BBB,其他檔案的小命都會保住。

LIBNAME SS06 "C:\TEMP1";
PROC DATASETS LIBRARY = SS06 KILL;
QUIT; RUN;


最強的大絕招「KILL」指令一施展,SS06 底下所有的資料都會被刪除。為此,David特別下了一個註解:BE VERY CAREFUL with KILL.

THE USE OF APPEND VS SET

有時想要合併許多資料,就可以用 APPEND 和 SET 指令。其實就跟 SAS/DATA 程序一樣。

PROC DATASETS LIBRARY = WORK FORCE;
APPEND OUT = MAY05_CLAIMS DATA = JUNE05_CLAIMS;
QUIT; RUN;


上面的程式是要把
JUNE05_CLAIMS 資料 append 到 MAY05_CLAIMS 這個資料。FORCE option在這邊可以強制合併不同的變數、資料型態和資料長度。David 強烈推薦這個 option。

THE USE OF CHANGE

這是拿來改資料檔名的。

PROC DATASETS LIBRARY = YY;
CHANGE MAY05_CLAIMS = MAY_JUNE05_CLAIMS;
QUIT; RUN;


THE USE OF AGE

有個滿奇怪的語法叫做 AGE。這個語法可以做批次檔名修改。

proc datasets library=daily nolist;
age today day1-day7;
quit; run;


上面是 David 給的範例,他的說明是「add day1 to the library and delete day7」,並給了下面這串 log:

LOG LISTING NOTE:
Deleting DAILY.DAY7 (memtype=DATA).
NOTE: Ageing the name DAILY.DAY6 to DAILY.DAY7 (memtype=DATA).
NOTE: Ageing the name DAILY.DAY5 to DAILY.DAY6 (memtype=DATA).
NOTE: Ageing the name DAILY.DAY4 to DAILY.DAY5 (memtype=DATA).
NOTE: Ageing the name DAILY.DAY3 to DAILY.DAY4 (memtype=DATA).
NOTE: Ageing the name DAILY.DAY2 to DAILY.DAY3 (memtype=DATA).
NOTE: Ageing the name DAILY.DAY1 to DAILY.DAY2 (memtype=DATA).
NOTE: Ageing the name DAILY.TODAY to DAILY.DAY1 (memtype=DATA).

我還沒完全搞懂用意。="=|||

THE MODIFY COMMAND

MODIFY 指令主要是給人修改資料標籤的。

PROC DATASETS LIBRARY = WORK;
MODIFY JUN05_PAYMENTS (LABEL = "NEW_MEMBER_LABEL");
RENAME OLD_VARIABLE_NAME = NEW_VARIABLE_NAME;
LABEL NEW_VARIABLE_NAME = LABEL_FOR_RENAMED_VARIABLE;
FORMAT EXISTING_VARIABLE_NAME COMMA11.2;
QUIT; RUN;


這個程式一共進行四項動作:
(一)加了個標籤到 JUNE05_PAYMENTS 資料裡面。
(二)改了變數名稱。
(三)給這個新的變數名稱一個新的標籤。
(四)給一個舊的變數新的格式。

注意:RENAME 指令只能用在 MODIFY 指令後面。

AUDIT STATEMENT

這個功能是可以用來記錄你對資料幹了啥麼好事。

PROC DATASETS LIB = MYLIB;
AUDIT MAY_SALES;
INITIATE;
QUIT; RUN;


使用 INITIATE 時,系統會建立一個和 MAY_SALES 同樣檔名但是 type 卻是 audit 的檔案,舉凡任何新增、更新或刪除的動作都會被記錄下來。

PROC DATASETS LIB = MYLIB;
AUDIT MAY_SALES;
SUSPEND;
QUIT; RUN;


SUSPEND 指令可以保證 audit 檔暫時不會被刪除。

PROC DATASETS LIB = MYLIB;
AUDIT MAY_SALES;
RESUME;
QUIT; RUN;


如果檔案已經被 suspend,則可以用 RESUME 指令來還原所有紀錄。

PROC DATASETS LIB = MYLIB;
AUDIT MAY_SALES;
TERMINATE;
QUIT; RUN;


TERMINATE 指令可以終止記錄並且刪除 audit 資料。

PROC DATASETS LIB = MYLIB;
EXCHANGE SALES = JUN_SALES;
QUIT; RUN;


EXCHANGE 可以交換兩個資料的檔名。

CONTACT INFORMATION
Please feel free to contact me with questions and comments about this paper:
David Fickbohm
HomeGain+
1250 45th St. Suite 200 Emeryville, CA 94608
510 594 4151 day phone
510 655 0848 fax
davidf@homegain.com

(PS)每個程式都有一個 QUIT 指令,這可用可不用,看個人習慣。

SAS Blogging - Finding Resources & Sharing information Outside of SAS Tech Support

原文載點:http://www2.sas.com/proceedings/sugi31/120-31.pdf

本篇文章由 Angela Hall 於06年的 SUGI 31 所發表。這並不是一個程式教學文章,而是專門介紹怎樣建立部落格來分享 SAS 使用心得。 Angela 並設立一個評分標準:

===========================
Ranking
---------------------------------------
★★★ Great
★★ Good
★ Useful
===========================


以下幾個是她在文內所推薦的部落格:

Rank: ★★★
http://savian.blogspot.com/
http://auspiciouscoincidence.org/geek/
http://sas-bi.blogspot.com/

Rank: ★★
http://www.omor.com/coruscation/
http://datasteps.blogspot.com/

Rank:
http://sas.weblogsinc.com/

有興趣的人可以前往一探。

Purpose and prospective

這三十年來經由無數人無私的奉獻,使得 SAS 的功能遠遠超出一般人在校園裡面的所學。我從開始接觸 SAS 到現在已經超過十年,但仍舊每天在學習新的 SAS 語法。很多人認為我的 SAS 很強,但自知所瞭解的程度大概只有 SAS 的百分之一吧!因此, SUGI 不啻是給我最新的程式寫作技術,讓我無時無刻都能增進 SAS 編寫能力。在台灣,使用 SAS 的人也許很多,但除了從校園內的少數統套軟體課程中學習以外,並無太多管道瞭解更進一步的 SAS 奧義。本著 SUGI 無私的奉獻精神,我雖無能力能夠創造出新的 SAS 技術來教導大家,但願意藉由這個部落格來分享一些不錯的 SUGI 文獻給同樣喜愛 SAS 的同胞。

一年一刊的 SUGI 文獻集差不多會有兩百多篇文章。我沒有辦法每一篇文章都拿出來分享,更無法逐字逐句翻譯成中文。僅能列出自己曾經讀過的文章,並將重點標示出來,輔以中文稍做說明。每篇文章都會標明檔案下載處,有興趣的人可以自行下載,並對照我的心得來輔助學習。

希望這個網站可以帶來一些回響,並召集更多有志者一同來加入 SUGI 的分享行列。

What is SUGI?

SUGI 是 SAS User Group International 的縮寫。這個團體乃由一群熱愛統套軟體 SAS 的人以及位於 North Carolina 的 SAS Institute 代表所組成,並從 1976 年開始每年召開一次年會,除了有各方 SAS 高手齊聚一堂交換心得以外, SUGI 也會將一年來許多優良的 SAS 教學文章集結成冊,完全免費地放在網路上面供有心人士下載。與會人數從 1976 年的 206 人擴增到如今每年至少會有兩千人參加。其官方網站為:

http://support.sas.com/events/sasglobalforum/
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; }