SAS函数&CALL例程介绍与实例精选

SAS函数&CALL例程介绍与实例精选

AN INTRODUCTION OF SAS FUNCTION & CALL ROUTINE AND SELECTED SAMPLE

Bruce Shao

摘要:
SAS提供了大量的内置函数,并且在数量和内容上不断的发展改进(注:本文所述SAS函数指SAS/BASE模块内的函数)。函数数量的变化非常显著,数量从SAS6.12的二百多个增加到SAS9.2的近五百个。这篇文章前半部分将简单介绍SAS函数的形式和种类,以及SAS系统版本更新过程中数量的变化,对于从SAS9.13升级到SAS9.2后内置函数的数量功能和类型上的变化做详细的介绍,特别会介绍到SAS9.2系统中用户可以自定义函数的情况;后半部分将精选典型类型的常用函数来做实例,你将会在这些例子中得到启示。一旦你学会使用合适的函数,将会使你的工作效率加倍。希望本篇文章能引起你对SAS系统提供的有用工具——SAS函数的关注。
正文:
SAS/BASE提供的函数有两种形式:一种是“FUNCTION”(后面用“函数”),可以进行计算或者系统操作并且会返回值;另一种是“CALL routine”(后面用“CALL例程”),用来改变变量的值或者执行其他的系统函数,但不返回值。SAS函数可以非常方便的用于DATA步中,WHERE子句和SQL查询语句中;CALL例程一般用于DATA步中。SAS提供了种类繁多的函数和CALL例程,并且一直在变化发展。
表1中列出了从SAS6.12版本到最新的SAS9.2版本语言参考:字典中函数和CALL例程的个数,图1中描述了函数和CALL例程总数量的变化趋势图。

表1 各SAS版本号中函数和例程的个数

图1 SAS函数和Call例程总数量的变化趋势

SAS/BASE有几类常用的函数:字符函数、字符串匹配函数、数学函数、日期和时间函数、统计函数、随机函数、金融函数、分位数函数等。最新的SAS9.2/BASE模块的语言参考:字典中所列的内置函数发生很多的变化。

在种类方面,SAS9.2语言参考:字典中内置函数,与有26类内置函数的SAS9.13比较,SAS9.2减少了程序响应度量函数(ARM)、货币折算函数(Currency Conversion)、双字节函数(DBCS)等函数种类,新增了算术函数(Arithmetic)、组合函数(Combinatorial)、距离函数(Distance)、财务函数(Finance)、数字函数(Numeric)、搜索函数(Search)、排序函数(Sort),共有30类函数。

在数量及功能方面,SAS9.2语言参考:字典中内置函数比SAS9.13增加了44个函数(包括Call例程),并且有些类型的函数如外部文件函数(External Files)中已有的函数如DOPEN、FOPEN、FEXIST,FILENAME,FILEREF,MOPEN,PATHNAME等函数参数说明被改进;有些函数是从其他模块转过来的,如SAS/ETS的INTCINDEX,INTCYCLE等等; SAS High-Performance Forecasting的HOLIDAY和NWKDOM函数等等; 有些已存在的函数和CALL例程得到增强,如函数CALL POKE, CALL PLKELONG, CALL SCAN, DATDIF, FSEP, INDEX, LAG, SCAN,ZIPSTATE等;PRX函数和例程取代了RX函数和例程,函数RXMATCH, RXPARSE,例程RXCHANGE CALL,RXFREE CALL,REXSUBSTR CALL将不再存在于SAS9.2语言参考:字典中。另外功能强大的SCAN函数和CALL SCAN例程取代了SCANQ函数和CALL SCANQ例程。

SAS9.2系统中函数使用发生的巨大变革是用户可以自定义函数。这样对于用户来说用自定义函数可以取代部分宏语言,模块化编程更容易。与函数比较起来,SAS宏有着不足的地方: 如1,写宏要非常小心谨慎,特别是符号”&”;2,宏可读性差,3自定义函数可以对某些变量其保护作用,不会乱窜现象 4自定义函数是程序更容易模块化,自定义函数可以保存起来,同部门直接可以共享。
上面简单的介绍了SAS函数的发展变化情况,和新版本SAS9.2中函数的比较详细的变化,下面将通过一些实例讨论一下SAS9版本中的一些常用的函数的使用比较和SAS9.2自定义函数的方法。
例一:字符处理函数
首字母大写的代码实现
这是字符处理经常会遇到的情况,下面给出三种通过函数的实现方法:
方法一:使用函数CAT,LOWCASE,UPCASE,SUBSTR

DATA ex;
INFORMAT first last $30.;
input first last;
first=CAT(UPCASE(SUBSTR(first,1,1)), LOWCASE(SUBSTR(first,2)));
last =CAT(UPCASE(SUBSTR(last,1,1)), LOWCASE(SUBSTR(last,2)));
cards;
ronald cODy
THomaS eDISON
albert einstein
;
run;

方法二:使用函数LOWCASE, UPCASE, SUBSTR

DATA ex;
INFORMAT first last $30.;
INPUT first last;
first = LOWCASE(first);
LAST = LOWCASE(last);
SUBSTR(first,1,1) = UPCASE(SUBSTR(first,1,1));
SUBSTR(last,1,1) = UPCASE(SUBSTR(last,1,1));
DATALINES;
ronald cODy
THomaS eDISON
albert einstein
;

方法三:使用函数PROPCASE

DATA ex;
INFORMAT first last $30.;
input first last;
first=PROPCASE(first);
last =PROPCASE(last);
cards;
ronald cODy
THomaS eDISON
albert einstein
;
run;

前面两个例子使用几个函数的组合的代码可以实现这个要求。第三个例子使用函数PROPCASE可以实现首字母大写转变的功能,另外有 ”delimiter” 可选项,可以满足字符串中出现其他特殊符号后要求字母大写的转变功能。如name=PROPCASE(“jeans*west”,’*’),可得到name的值为“Jeans*West”。
例二 数值处理函数
比赛计分计算,计算去掉一个最高分和一个最低分后的总分。
方法一:使用函数MIN,MAX,SUM

DATA ex;
INPUT Name:$ q1-q10 ;
ARRAY QQ q1-q10;
TOTAL=SUM (of q1-q10)-MIN(of q1-q10)-MAX(of q1-q10);
CARDS;
Tom 90 80 81 79 78 76 84 82 84 50
Lucy 89 78 79 77 76 75 81 79 83 74
Jack 47 78 80 81 82 82 78 85 77 85
;
RUN;

方法二:使用函数ORDINAL,SUM

DATA ex;
INPUT Name:$ q1-q10 ;
ARRAY QQ q1-q10;
TOTAL=SUM (of q1-q10)-ORDINAL(1,of q1-q10)-ORDINAL(10,of q1-q10);
CARDS;
Tom 90 80 81 79 78 76 84 82 84 50
Lucy 89 78 79 77 76 75 81 79 83 74
Jack 47 78 80 81 82 82 78 85 77 85
;
RUN;

方法三:使用函数SMALLEST, LARGEST,SUM

DATA ex;
INPUT Name:$ q1-q10 ;
ARRAY QQ q1-q10;
TOTAL=SUM (of q1-q10)-SMALLEST (1, of q1-q10)-LARGEST (1,of q1-q10);
CARDS;
Tom 90 80 81 79 78 76 84 82 84 50
Lucy 89 78 79 77 76 75 81 79 83 74
Jack 47 78 80 81 82 82 78 85 77 85
;
RUN;

方法四:使用例程CALL SORTN,SUM

DATA ex;
INPUT Name:$ q1-q10 ;
ARRAY QQ q1-q10;
CALL SORTN (of qq [*]) ;
TOTAL=SUM (of q2-q9);
CARDS;
Tom 90 80 81 79 78 76 84 82 84 50
Lucy 89 78 79 77 76 75 81 79 83 74
Jack 47 78 80 81 82 82 78 85 77 85
;
RUN;

上面给出了四种不同的函数方法来实现这个计算,但是这几种函数用法和功能并不是完全一样的,如对缺失值的处理不一样,当观察值中出现缺失值时,MIN,MAX将忽略缺失值后计算、CALL SORTN则会把缺失值排为最小值的位置后计算,而SMALLEST, LARGEST和ORDINAL则直接生成缺失值。当计算要求改变如去掉最大两个值和最小两个值后的总分,函数MIN,MAX就难于实现,而SMALLEST, LARGEST和ORDINAL可以实现,但是不如例程CALL SORTN的代码简洁。
例三:日期处理函数
SAS提供了很多日期处理的函数如SECOND,MINUTE,HOUR,DAY,MONTH,YEAR;DATEPART,TIMEPART;INTNX,INTCK,YRDIF等等,但是实际应用中还是不够用。SAS9.2/BASE模块的语言参考:字典中新增了2个内置函数HOLIDAY和NWKDOM,分别用来返回某一年指定假期的具体日期和返回指定年月某一周第几天的具体日期。
例3:2009年的母亲节是哪一天?

data _null_;
MOTHERS = holiday('MOTHERS', 2009);
format MOTHERS date9.;
put MOTHERS=;
run;

SAS9.2的新函数HOLIDAY目前暂时提供22个美国和加拿大的节假日选项。
例四:自定义函数
在SAS9.2系统中,我们可以用C,C++或者SAS语言来自定义函数,分别提供了两种编译语句:用PROC PROTO语句来编译C或C++语言定义的函数;用PROC FCMP语句来编译SAS语言定义的函数,这样就可以建立自己的函数库。SAS本身已经提供了一些专业的函数库如统计、金融函数库,以后来自不同行业的用户可以根据自己的需要建立各自专业的函数库共享使用。
例4:在临床医学中,计算一个study­_ day是个常规任务。

/*自定义函数*/
proc fcmp outlib=sasuser.funcs.trial;
function study_day(start, event);
n = event - start;
if n >= 0 then
return(n + 1);
else
return(n);
endsub;
/*调用函数*/
options cmplib=sasuser.funcs;
data _null_;
start = '15Feb2006'd;
today = '27Mar2006'd;
sd = study_day(start, today);
put sd=;
run;

参考资料6陈述了实现相同目的的宏在使用中将会出现意外问题的讨论,具体见6。
小结:函数是SAS系统提供的重要工具之一,数量、内容和功能上不断的得到完善,并且在新版本9.2提供了一个重要的自定义函数功能。如果你是对函数不熟悉,建议你开始学习使用SAS函数,一旦你学会使用合适的函数,将会使你的工作效率加倍;如果你是一个经验丰富的用户,建议多查看新版本中的函数,也许可以找到更简便的方法来解决以前同样的工作难题。
参考:

  1. SAS Institute. 1996. SAS system for windows. V6.12 TS020. Carey, NC: SAS Institute, Inc.
  2. SAS Institute. 2000. SAS Language Reference: Dictionary, Version8. Carey, NC: SAS Institute, Inc.
  3. Deb Cassidy. 2004. ”An Introduction to SAS Functionality” SUGI29 Montréal, Canada Paper 255-29
  4. SAS Institute Inc. 2008. SAS®9.2 Language Reference dictionary Cary, NC: SAS Institute Inc.
  5. SAS Institute Inc. 2008. What’s New in SAS® 9.2. Carey, NC: SAS Institute, Inc.
  6. Jason Secosky. 2007. A Sampler of What’s New in Base SAS® 9.2 RTSUG Cary, NC: SAS Institute Inc.
  7. Web Sites: SAS爱好者论坛 http://www.sasor.com

联系方式CONTACT INFORMATION
资料整理,难免出错,希望不吝赐教;并欢迎建议和交流,联系方式如下:
Your comments and questions are valued and encouraged. I can be contacted at:
电子信箱:sxlion8 (at) gmail.com