数组

SAS可以把一组同为数值型或同为字符型的变量合在一起,使用同一个名字称呼,用下标来区分。这与通常的程序设计语言中的数组略有区别,通常的程序设计语言中数组元素没有对应的变量名,而SAS数组每个元素都有自己的变量名。

一、数值型数组

定义数值型数组的格式为:

ARRAY 数组名(维数说明) 数组元素名列表 (初始值表);

例如:

ARRAY tests(3) math chinese english (0, 0, 0);

数组名是一个合法的SAS名字且不能与同一数据步中的变量重名。对一维数组,维数说明只要说明元素个数,这时下标从1开始。数组元素名列表列出这个数组的各个元素实际代表的变量名,各变量名以空格分隔。比如,上例中tests(1)代表数学成绩,tests(2)代表语文成绩,tests(3)代表英语成绩。初始值表给各数组元素赋初值,按顺序对应。

数组说明中初始值表可以省略,这时其初始值为相应数组元素的值(如果其数组元素还没有值则初值为缺失值)。

数组说明中的数组元素名列表可以省略,这时其元素也有对应的变量名,变量名为数组名后附加序号,比如:

ARRAY x(3);

中数组x的各元素名为x1,x2,x3。

也可以在说明维数时用“下标下界:下标上界”来说明一个其它的下标下界,如

ARRAY sales(95:97) yr95-yr97 ;

这时sales(95)为yr95,sales(96)为yr96,sales(97)为yr97。上面的变量名列表是一种特殊的语法,在用到变量名列表时如果连续写几个前面字母相同,后面是连续的序号的变量,只要写出第一个和最后一个,中间用减号连接。

一维数组的维数说明还可以是一个星号,这时数组大小由提供的元素列表中的变量个数决定,如上面的数组tests可以等价地说明为:

ARRAY tests(*) math chinese english (0, 0, 0);

可以用函数DIM(数组名)来获得数组的长度。

可以定义二维数值型数组,只要在维数说明中指定用逗号分开的两个下标界说明,例如:

array table(2,2) x11 x12 x21 x22;

说明table(1,1)为x11,table(1,2)为x12,table(2,1)为x21,table(2,2)为x22。二维数组元素按行排列。

二、字符型数组

定义字符型数组的语法略复杂,它需要加一个$符来说明数组元素类型为字符型,并且要说明每一元素所能存储的字符串的最大长度。说明格式如下:

ARRAY 数组名(维数说明) $ 元素长度说明 数组元素名列表 (初始值表);

例如:

ARRAY names(3) $ 10 child father mother;

字符型数组其它方面用法与数值型相同。

三、临时数组

上面格式说明的数组都是把若干个变量集合在一起使用同一个数组名称呼,每个数组元素是一个独立的变量。SAS也提供了与其它程序设计语言相同的数组,即数组元素只由数组名和序号决定,没有对应的变量名。这种数组叫做临时数组,定义格式为:

ARRAY 数组名(维数说明) _TEMPORARY_ (初始值表);

可见临时数组就是在数组说明中用_TEMPORARY_代替了数组元素列表。例如:

ARRAY x(3) _TEMPORARY_ (0, 0, 0);

说明了一个有三个元素的临时数组x。其元素为x(1),x(2),x(3),即使变量x1,x2,x3 存在也与此数组无关。临时数组的特点是它只用于中间计算,最终不被写入数据集。并且临时数组与其它变量不同的是,它在数据步隐含循环(后面会解释此概念)中能自动保留上一步得到的值。临时数组当然也可以有多维数组,或字符型数组。

四、使用数组

临时数组的使用与其它程序设计语言中的数组作用相同,可以存放性质类似的数据进行处理。SAS以变量为元素的数组可以方便变量的循环处理,比如,读入了comp1-comp10 十个计算机销售额变量,prin1-prin6六个打印机销售额变量,希望计算其总和,可以用如下的数组说明与DO循环配合进行:

data sales;
input comp1-comp10 prin1-prin6;
ARRAY y(*) comp1-comp10 prin1-prin6;
tot=0;
do i=1 to DIM(y);
    tot + y(i);
end;
cards;
………
;
run;

此例中数组说明用了星号说明维数,求总和时用了累加语句。事实上,在数组说明的数组元素列表部分除了列出具体的变量名表外,还可以用特殊名字_NUMERIC_代表所有数值型变量的列表,用_CHARACTER_代表所有字符型变量的列表,用_ALL_代表所有变量的列表(用_ALL_ 时所有变量应该同为数值型或同为字符型,否则出错)。所以上例中的数组y的说明中还可以用_NUMERIC_或_ALL_代替变量名列表。

实际上,SAS为变量累加提供了一个专门的函数SUM(OF …),比如上面的tot变量可以用SUM(OF comp1-comp12 prin1-prin6)计算。这个例子主要为说明如何循环处理多个变量。
函数

SAS提供了比一般程序设计语言多几倍的标准函数可以直接用在数据步的计算中,其中包括所有语言都有的数学函数、字符串函数,还包括特有的统计分布函数、分位数函数、随机数函数、日期时间函数、财政金融函数,等等。

这些函数的调用方法类似其它语言,比如求x1,x2,x3三个自变量的和可以用函数 SUM(x1,x2,x3) 。另外,SAS还提供了函数调用的另一种语法以便于把多个数据集变量作为函数自变量,其格式为“函数名(OF 变量名列表)”,其中变量名列表可以是任何合法的变量名列表,比如x1 ,x2,x3的和等价地可以用SUM(OF x1 x2 x3)或SUM(OF x1-x3)表示。注意两种写法不能混在一起,比如SUM(OF x1,x2,x3)和SUM(x1-x3)都是错的。

本小节对重要的函数加以介绍,其它详见《SAS软件:Base SAS软件使用手册》(高惠璇等编译,中国统计出版社出版)。



一、数学函数

    *
      ABS(x) 求x的绝对值。
    *
      MAX(x1,x2,…,xn) 求所有自变量中的最大一个。
    *
      MIN(x1,x2,…,xn) 求所有自变量中的最小一个。
    *
      MOD(x,y) 求x除以y的余数。
    *
      SQRT(x) 求x的平方根。
    *
      ROUND(x,eps) 求x按照eps指定的精度四舍五入后的结果,比如ROUND(5654.5654,0.01) 结果为5654.57,ROUND(5654.5654,10)结果为5650。
    *
      CEIL(x) 求大于等于x的最小整数。当x为整数时就是x本身,否则为x右边最近的整数。
    *
      FLOOR(x) 求小于等于x的最大整数。当x为整数时就是x本身,否则为x左边最近的整数。
    *
      INT(x) 求x扔掉小数部分后的结果。
    *
      FUZZ(x) 当x与其四舍五入整数值相差小于1E-12时取四舍五入。
    *
      LOG(x) 求x的自然对数。
    *
      LOG10(x) 求x的常用对数。
    *
      EXP(x) 指数函数 。
    *
      SIN(x), COS(x), TAN(x) 求x的正弦、余弦、正切函数。
    *
      ARSIN(y) 计算函数y=sin(x)在 区间的反函数,y取[-1,1]间值。
    *
      ARCOS(y) 计算函数y=cos(x)在 的反函数,y取[-1,1]间值。
    *
      ATAN(y) 计算函数y=tan(x)在 的反函数,y取 间值。
    *
      SINH(x), COSH(x), TANH(x) 双曲正弦、余弦、正切
    *
      ERF(x) 误差函数
    *
      GAMMA(x) 完全 函数

此外还有符号函数SIGN, 函数一阶导数函数DIGAMMA,二阶导数函数TRIGAMMA ,误差函数余函数ERFC, 函数自然对数LGAMMA,ORDINAL函数,AIRY 函数,DAIRY函数,Bessel函数JBESSEL,修正的Bessel函数IBESSEL,等等。

二、数组函数

数组函数计算数组的维数、上下界,有利于写出可移植的程序。数组函数包括:

    *
      DIM(x) 求数组x第一维的元素的个数(注意当下界为1时元素个数与上界相同,否则元素个数不一定与上界相同)。
    *
      DIM k(x) 求数组x第k维的元素的个数。
    *
      LBOUND(x) 求数组x第一维的下界。
    *
      HBOUND(x) 求数组x第一维的上界。
    *
      LBOUND k(x) 求数组x第 k维的下界。
    *
      HBOUND k(x) 求数组x第 k维的上界。

三、字符函数

较重要的字符函数有:

    *
      TRIM(s) 返回去掉字符串s的尾随空格的结果。
    *
      UPCASE(s) 把字符串s中所有小写字母转换为大写字母后的结果。
    *
      LOWCASE(s) 把字符串s中所有大写字母转换为小写字母后的结果。
    *
      INDEX(s,s1) 查找s1在s中出现的位置。找不到时返回0。
    *
      RANK(s) 字符s的ASCII码值。
    *
      BYTE(n) 第n个ASCII码值的对应字符。
    *
      REPEAT(s,n) 字符表达式s重复n次。
    *
      SUBSTR(s,p,n) 从字符串s中的第p个字符开始抽取n个字符长的子串
    *
      TRANWRD(s,s1,s2) 从字符串s中把所有字符串s1替换成字符串s2后的结果。

其它字符函数还有COLLATE,COMPRESS,INDEXC,LEFT,LENGTH,REVERSE,RIGHT,SCAN ,TRANSLATE,VERIFY,COMPBL,DEQUOTE,INDEXW,QUOTE,SOUNDEX,TRIMN,INDEXW。

四、日期和时间函数

常用日期和时间函数有:

    *
      MDY(m,d,yr) 生成yr年m月d日的SAS日期值
    *
      YEAR(date) 由SAS日期值date得到年
    *
      MONTH(date) 由SAS日期值date得到月
    *
      DAY(date) 由SAS日期值date得到日
    *
      WEEKDAY(date) 由SAS日期值date得到星期几
    *
      QTR(date) 由SAS日期值date得到季度值
    *
      HMS(h,m,s) 由小时h、分钟m、秒s生成SAS时间值
    *
      DHMS(d,h,m,s) 由SAS日期值d、小时h、分钟m、秒s生成SAS日期时间值
    *
      DATEPART(dt) 求SAS日期时间值dt的日期部分
    *
      INTNX(interval,from,n) 计算从from开始经过n个in间隔后的SAS日期。其中interval 可以取'YEAR'、'QTR'、'MONTH'、'WEEK'、'DAY'等。比如,INTNX('MONTH', '16Dec1997'd, 3)结果为1998年3月1日。注意它总是返回一个周期的开始值。
    *
      INTCK(interval,from,to) 计算从日期from到日期to中间经过的interval间隔的个数,其中interval取'MONTH'等。比如,INTCK('YEAR', '31Dec1996'd, '1Jan1998'd)计算1996年12 月31日到1998年1月1日经过的年间隔的个数,结果得2,尽管这两个日期之间实际只隔1年。

其它日期和时间函数还有DATE、TODAY、DATETIME、DATEJUL、JULDATE、HOUR、MINUTE、SECOND 、TIME、TIMEPART等。详见《SAS系统-Base SAS软件使用手册》、《SAS系统-SAS/ETS软件使用手册》。

五、分布密度函数、分布函数

作为一个统计计算语言,SAS提供了多种概率分布的有关函数。分布密度、概率、累积分布函数等可以通过几种统一的格式调用,格式为

    * 分布函数值 = CDF(' 分布', x <, 参数表>);
    * 密度值 = PDF(' 分布', x <, 参数表>);
    * 概率值 = PMF(' 分布', x <, 参数表>);
    * 对数密度值 = LOGPDF(' 分布', x <, 参数表>);
    * 对数概率值 = LOGPMF(' 分布', x <, 参数表>);

CDF计算由'分布'指定的分布的分布函数, PDF计算分布密度函数值,PMF计算离散分布的分布概率,LOGPDF为PDF的自然对数,LOGPMF为PMF的自然对数。函数在自变量 x处计算,<, 参数表>表示可选的参数表。

分布类型取值可以为: BERNOULLI, BETA, BINOMIAL, CAUCHY, CHISQUARED, EXPONENTIAL, F, GAMMA, GEOMETRIC, HYPERGEOMETRIC, LAPLACE, LOGISTIC, LOGNORMAL, NEGBINOMIAL, NORMAL 或 GAUSSIAN, PARETO, POISSON, T, UNIFORM, WALD 或 IGAUSS, and WEIBULL。可以只写前四个字母。

例如,PDF('NORMAL', 1.96)计算标准正态分布在1.96处的密度值(0.05844),CDF('NORMAL', 1.96)计算标准正态分布在1.96处的分布函数值(0.975)。PMF对连续型分布即PDF。

除了用上述统一的格式调用外,SAS还单独提供了常用的分布的密度、分布函数。

    * PROBNORM(x) 标准正态分布函数
    * PROBT(x,df<,nc>) 自由度为df的t分布函数。可选参数nc为非中心参数。
    * PROBCHI(x,df<,nc>) 自由度为df的卡方分布函数。可选参数nc为非中心参数。
    * PROBF(x,ndf,ddf<,nc>) F(ndf,ddf)分布的分布函数。可选参数nc为非中心参数。
    * PROBBNML(p,n,m) 设随机变量Y服从二项分布B(n,p),此函数计算P(Y m)。
    * POISSON((lambda,n) 参数为lambda的Poisson分布Y n的概率。
    * PROBNEGB(p,n,m) 参数为(n,p)的负二项分布Y m的概率。
    * PROBHYPR(N,K,n,x<,r>) 超几何分布的分布函数。设N个产品中有K个不合格品,抽取n个样品,其中不合格品数小于等于x的概率为此函数值。可选参数r是不匀率,缺省为1 ,r代表抽到不合格品的概率是抽到合格品概率的多少倍。
    * PROBBETA(x,a,b) 参数为(a,b)的Beta分布的分布函数。
    * PROBGAM(x,a) 参数为a的Gamma分布的分布函数。
    * PROBMC 计算多组均值的多重比较检验的概率值和临界值。
    * PROBBNRM(x,y,r) 标准二元正态分布的分布函数,r为相关系数。

六、分位数函数

分位数函数是概率分布函数的反函数。其自变量在0到1之间取值。分位数函数计算的是分布的左侧分位数。SAS提供了六种常见连续型分布的分位数函数。

    * PROBIT(p) 标准正态分布左侧p分位数。结果在-5到5之间。
    * TINV(p, df <,nc>) 自由度为df的t分布的左侧p分位数。可选参数nc为非中心参数。
    * CINV(p,df<,nc>) 自由度为df的卡方分布的左侧p分位数。可选参数nc为非中心参数。
    * FINV(p,ndf,ddf<,nc>) F(ndf,ddf)分布的左侧p分位数。可选参数nc为非中心参数。
    * GAMINV(p,a) 参数为a的伽马分布的左侧p分位数。
    * BETAINV(p,a,b) 参数为(a,b)的贝塔分布的左侧p分位数。

七、随机数函数

SAS可以用来进行随机模拟。它提供了常见分布的伪随机数生成函数。

1.均匀分布随机数

有两个均匀分布随机数函数:UNIFORM(seed),seed必须是常数,为0,或5位、6位、7位的奇数。RANUNI(seed),seed为小于 2**31-1的任意常数。在同一个数据步中对同一个随机数函数的多次调用将得到不同的结果,但不同数据步中从同一种子出发将得到相同的随机数序列。随机数种子如果取0或者负数则种子采用系统日期时间。

2.正态分布随机数

有两种,NORMAL(seed),seed为0,或5位、6位、7位的奇数。RANNOR(seed),seed为任意数值常数。

3.指数分布随机数

RANEXP(seed),seed为任意数值,产生参数为1的指数分布的随机数。参数为lambda的指数分布可以用RANEXP(seed)/lambda得到。

另外若Y=alpha-beta*LOG(RANEXP(seed)),则Y为位置参数为alpha,尺度参数为beta的极值分布。若Y=FLOOR(-RANEXP(seed)/LOG(p)),那么Y是具有参数p的几何分布变量。

4.伽马分布随机数

RANGAM(seed, alpha),seed为任意数值常数,alpha>0,得到参数为alpha的伽马分布。设X=RANGAM(seed, alpha),则Y=beta*X是形状参数为alpha,尺度参数为beta的GAMMA分布随机数。如果alpha是整数,则Y=2*X是自由度为 2*alpha的卡方分布随机数。

如果alpha是正整数,则Y=beta*X是Erlang分布随机数,为alpha个独立的均值为beta的指数分布变量的和。

如果Y1=RANGAM(seed,alpha),Y2=RANGAM(seed,beta),在Y=Y1/(Y1+Y2)是参数为(alpha,beta )的贝塔分布随机数。

5.三角分布随机数

RANTRI(seed,h),seed为任意数值常数,0<h<1。此分布在0到1取值,密度在0到h 之间为2x/h,在h到1之间为2(1-x)/(1-h)。

6.柯西分布随机数

RANCAU(seed),seed为任意数值常数。产生位置参数为0,尺度参数为1的标准柯西分布随机数。Y=alpha+beta*RANCAU(seed)为位置参数为alpha,尺度参数为beta的一般柯西分布随机数。

7.二项分布随机数

RANBIN(seed,n,p)产生参数为(n,p)的二项分布随机数,seed为任意数值。

8.泊松分布随机数

RANPOI(seed,lambda)产生参数为lambda>0的泊松分布随机数,seed为任意数值。

9.一般离散分布随机数

RANTBL(seed, p1, …, pn)生成取1,2,…,n的概率分别为p1,…,pn的离散分布随机数。

八、样本统计函数

样本统计函数把输入的自变量作为一组样本,计算样本统计量。其调用格式为“函数名(自变量1,自变量2,…,自变量n)”或者“函数名(OF 变量名列表)”。比如SUM是求和函数,如果要求x1,x2,x3的和,可以用SUM(x1,x2,x3),也可以用SUM(OF x1-x3)。这些样本统计函数只对自变量中的非缺失值进行计算,比如求平均时把缺失值不计入内。

各样本统计函数为:

    * MEAN 均值

    *


    *
      MAX 最大值
    *
      MIN 最小值
    *
      N 非缺失数据的个数
    *
      NMISS 缺失数值的个数。
    *
      SUM 求和
    *
      VAR 方差
    *
      STD 标准差
    *
      STDERR 均值估计的标准误差,用STD/SQRT(N)计算。
    *
      CV 变异系数
    *
      RANGE 极差
    *
      CSS 离差平方和
    *
      USS 平方和
    *
      SKEWNESS 偏度
    *
      KURTOSIS 峰度

      注意:数据集的存储一般是每行为一个个体的观测值,每列是个体的一个属性(变量),所以统计一般应该对列进行,而不是象这里对行进行,把各变量作为一个样本的各个观测处理。这里提供的函数主要用于进行一些自编程的计算。

      SAS/IML矩阵功能简介

      SAS/IML 是SAS提供的一个可以进行矩阵运算编程的工具,详细使用请参见有关资料或系统帮助(Help | Extended Help | SAS System Help: Main Menu | Help for SAS Products | SAS/IML)。它可以用来进行交互的矩阵运算,也可以编好一个程序再一起运行。程序可以使用分支、循环、模块化子程序等控制结构。数据步中的函数大都能在SAS/IML中使用,SAS/IML 也提供了一些特有的函数。SAS/IML的一个方便之处是它可以直接读取SAS的数据集并把结果写成SAS数据集,它也有存取外部文件的功能。

      要交互运行SAS/IML,只要在程序窗口输入

      proc iml;
        reset print;

      提交此程序,就可以进入交互的SAS/IML运算状态。退出用QUIT语句。SAS/IML中可以使用标量、行向量、列变量和矩阵,可以使用字符型数据。变量取名规则遵循SAS语言的统一规定,变量可以存储标量、向量和矩阵。

      赋值用等号。例如:

      sc = 15.25;
      vh = {1 2};
      vh1=5:9;
      vv = {3, 4};
      mat1 = {1 2 3,
             4 5 6};
      mat2 = {"Li" "Ming",
             "Zhang" "Chong"};

      我们注意赋值语句是一个SAS语句,它以分号结尾。上面定义了标量sc,行向量vh和vh1,列向量vv,两行两列的矩阵mat1,字符型矩阵mat2。由上可见,写矩阵常量时,行的元素之间以空格分隔,行之间以逗号分割。可以用“开始:结尾”的写法生成一个等差数列行向量。

      矩阵之间可以用<、=等符号进行元素两两间的比较。要得到一个标量的结果,可以用ALL()函数表示自变量的各元素均为真(非零),用ANY()函数表示自变量的元素中至少一个为真。可以用&、|、^连接两个逻辑型矩阵(元素间的与、或、非)。

      用||表示矩阵左右连接,用//表示矩阵上下连接。x`表示x的转置。

      SAS/IML 中可以进行通常的矩阵加减乘(+、-、*)运算,也可以进行对应元素之间的乘除(#、/)运算。矩阵加减必须两个矩阵大小完全相等,或者其中一个是标量。矩阵乘法要求第一矩阵的列数等于第二矩阵的行数,或其中一个是标量。矩阵元素之间的乘除运算是对应元素进行乘除,当两个矩阵大小完全相同时可以进行运算,其中有一个是标量时可以进行运算,另外,如果其中有一个是行(列)向量而其长度与另一个矩阵的列数(行数)相同也可以进行运算。矩阵逆要用INV()函数运算。

      为了读入一个数据集,先打开数据集,用如

      USE sasuser.c9501;

      然后用READ ALL VAR{ 变量列表}的格式读入数据集的各变量值,例如:

      READ ALL VAR {name math chinese};
      print name math chinese;

      这时三个变量都可以作为列向量来使用