很好的一篇文章,特适合入门者。
; {: M$ v: ]6 o+ l: u/ u, b从本质上讲,SAS是一种完善的第四代计算机语言。因此要真正掌握它,我们仍然要抛开其华丽的外表,从学习它的核心――SAS程序开始。 现在,让我们将SAS看成一个计算能力极强的统计学白痴(之所以这样说,是因为它计算能力虽然极强,却只能帮你计算而不能提出自己的实验设计方案或研究方向来),而你有一个非常小的关于数据分析的问题要请它帮忙。自然你要开口提出请求,无论措辞是委婉动听还是直截了当,你的大实话无非是“喂,老兄,我有这样一些数据,我想做这样一种统计分析,您能帮我吗?”。他迅速检查您的要求,在认为合情、合理并且合法后,就开始进行计算,并且在很短的时间内给出计算结果。 好的,计算机语言就是我们和计算机对话时所用的语言。和以上人类对话的例子相对照,“我有这样一些数据”对应SAS程序中的数据步;而“我想做这样一些统计处理”对应SAS程序中的程序步;最后的那一句哀求“您能帮我吗”则对应了Submit命令。在默认情况下,运行结果或者出错信息将自动给出(当然你也可以强制不让它输出)。此外,还有一些系统环境控制语句,如Libname、title等。SAS程序就是由一个或多个数据步和/或程序步加上一些乱七八糟的环境控制语句组成。在本章中,我们将首先学习SAS对数据的管理方式,然后会了解到什么是数据步和程序步,最后则介绍一下SAS语言中结构化语句的语法。 §3.1 SAS系统对数据的管理 在SAS系统中只有SAS数据集才能被SAS过程直接调用,SAS数据集的结构和DBF数据库完全相同,因此无须多讲。而SAS数据集存储在被称为SAS数据库的文件集中,在PC系统中,SAS数据库与某一个文件夹相对应,我们要为每一个数据库指定一个库标记(库名)来识别该库,使用Libname命令可以指定库标记。它的一般格式如下: Libname 库标记 ’文件夹位置’ 选项; 例如要指定目录“C:\USER”为库标记A,可以在视窗中提交如下语句: libname a ’c:\user’; 数据库可分为永久库和临时库两种。临时库只有1个,名为WORK,它在每次启动SAS系统后自动生成,关闭SAS时库中的数据集被自动删除;永久库可有多个,用户可以使用Libname语句指定永久库的库标记,永久库中的所有文件都将被保留。但库标记仍是临时的,每次启动SAS系统后都要重新指定。不过为了方便用户,SAS在每次启动时都会自动指定两个库标记: SASUSER:指明为永久库,即库中的数据集被保存起来,以便下次启动系统时使用。4 ^0 }. _" b2 o9 m4 D
WORK:指明为临时库,对于开发和检查新程序非常有用,但每次SAS运行结束后WORK库中的所有文件将被删除。在程序中引用该库中的数据集可以省略库标记,即它被认为是缺省的数据库。( U% t2 X& u) V: }5 y o, c8 g
SAS数据库对应文件夹,如SASUSER对应c:\sas\sasuser,WORK对应c:\sas\saswork, SAS数据集则对应文件,每个数据集实际上是在硬盘的相应文件夹内产生一个文件名为数据集名,而扩展名为.sd2的文件,因此如果你成功的建立了一个数据集,理论上你就可以反复使用它直到硬盘报废。 每一个数据集都有一个两级名,第一级是库标记,第二级是数据集名,中间用“.”隔开,在程序中通过指定两级名来识别文件。文件两级名的一般形式如下: 库标记.数据集名 如在a库中的数据集abc可以这样来引用:a.abc 。 §3.2 SAS程序的数据步 3.2.1 预备知识 SAS程序的语句从上一个语句结束处开始,以一个分号结束,可占多行。 3.2.2 数据步 以DATA语句开始,用于创建和处理数据集。其中最常用的语句有: DATA语句 它的主要功能是:4 z, C2 u4 Q1 ]# E8 v
标志数据步的开始。* S7 `0 p2 f* u+ Q" k
命名将要创建的SAS数据集。
. Y- X7 K# P/ RDATA语句的一般形式如下: DATA 数据集名; INFILE语句 用于从外部文件读入数据,必须出现在INPUT语句之前。它的主要功能是:
# t3 N# g. W3 m3 i; I确定一个包含原始数据的外部文本文件。
! F5 @1 O- j2 p7 P8 YINFILE语句的格式如下: INFILE ’外部文件的所在位置及名称’ 选项; CARDS语句 用于直接输入数据,标志着数据块的开始。格式如下:
4 [$ y) b, u4 {- m' p* T+ j. LCARDS;
p- Q) L5 e) Y# _) S2 e: o数据块
: c( y2 |* B3 e;
I6 n" i) O8 @INPUT语句 用于向系统表明如何读入每一条记录。它的主要功能有:
1 q- y) L- U3 |( O" x1 s读入由语句指定的数据列。; N0 O3 e1 L' g# H- K
为相应的数据域定义变量名。
7 B6 D, G w1 ?7 T" k确定变量的读入模式。
& u, O% {/ x% v" ?7 d, ^INPUT语句的格式如下: INPUT 变量名 [变量类型 起止列数]...; 方括号表示其中的内容为可选,如果不输入,系统会以默认值代替。 3.2.3 数据的两种输入方式 上面我们学到了INFILE语句和CARDS语句,它们分别对应了两种数据输入的方式: 【直接输入方式】 实际上我们在第一章已经用到了直接输入方式,仍以那个程序为例,其中的数据步如下: data temp;
! G9 G' U7 l/ F& n0 P6 r+ C& W 命名将要建立的数据集为work.temp
6 @, r) f' ^. J" l2 d" iinput x y@@;, ] ^' q8 ^6 V3 |& U
要输入的变量为x、y,并且连续输入 cards;6 x, d9 ~. S1 F2 a3 X
直接输入数据,数据块开始 34 56 78 90 35 67 89 10 23 65 77 45" `- w! D8 H z, ?
数据块* E# M; b& K6 D! `3 K
;) n; H& e6 b) ?2 ^7 z. B7 Z
数据块结束 变量y后面的@@表示数据可以在一行里连续读入,SAS默认按列来分隔变量,可是这里只有两个变量,输成两列数据太长,因此加上两个@@,SAS见到这个符号,在按变量名依次读取完数据后,不是跳到下一行,而是继续在该行读数据,直至本行结束或到达分号为止。 如果你对这段叙述无法理解,请去掉两个@@,重新运行一遍程序,看看LOG视窗中的提示都有些什么变化。! g; E: F. n K1 {9 s
【外部文件读入方式】 如果刚才的数据已经事先输好,在硬盘上的“C:\USER”文件夹内存为temp.dat文件,该文件内容如下(纯文本): 34 56 78 90 35 67 89 10 23 65 77 45 则我们可以输入程序如下: data temp; 命名将要建立的数据集为work.temp" o+ b* u B0 N( f. \
Infile ’c:\user\temp.dat’; 指定外部数据文本文件名
! q2 I( e# {4 R* | minput x y@@; 要输入的变量为x、y,并且连续输入 Run; 数据步结束,以上语句可以执行了 数据步以DATA语句开始,那么在哪里结束?在遇到run语句或另一个数据步/程序步时就结束了。 为什么程序的最后都要加一句run?这个问题还不太好解释,每一个程序应至少在最后有一个run语句,表明前面的所有语句可以提交运行了,如果没有这一句,SAS会以为你后面还有语句要输入,从而将一些非立即执行的语句保留在编译缓冲区中,等待后续命令发出后一起执行,这有时会把事情弄得很糟。出于减少麻烦的需要,建议大家养成在每一个数据步或程序步后都写上一句run的习惯。 Submit命令和run语句有什么区别?这是初学者最爱问的一个问题,也同样是一个难以回答的问题。简单地讲,run是程序语句,可在程序中多次出现,它表示前面的程序段已经全部写完,可以作为一个或几个整体提交运行了,而Submit则是SAS命令,只在最后程序运行的时候发出,表示将程序正式提交运行。如果你还不明白,那么我还是来给你打个比方,run语句是你对SAS说“我想让您做的就是这些了”,而Submit命令就是对SAS说“现在开始为我做这些事,好吗”,明白了吗? 【读入其他格式的数据文件】 除了以上的两种通过数据步创建数据集的方法,SAS还提供了一些其他的方式可以用来读入其他格式的数据文件。6.11版本以上的SAS可以利用FILE菜单上的import命令将其他格式的数据文件导入SAS系统,创建SAS自己的数据集。可以导入的数据文件格式有:dBase数据库,EXCEL工作表,LOTUS的数据库,纯文本的数据文件等。 导入的操作完全是对话式的,界面友好,简便实用。以下简单叙述导入的步骤,假如例1.1的数据输成一个dBASEIII数据库文件temp.dbf, 已经存放在"c:\user"下,要导入成数据集work.li1_1。选择FILE菜单上的import,弹出一个对话框,按照向导的提示进行下去。 1. 选择导入的数据格式,从下拉式菜单上选择DBF格式,单击NEXT按钮。 2. 给出数据文件的位置和文件名,在对话框中键入C:\USER\temp.DBF,或点BROWSE直接从上面选择文件,选好后单击NEXT按钮。 3. 选择导入的目的地,即指定要创建的数据集的名字和存放的数据库名,先在左面的对话框选择数据库名WORK(临时库),在右面的对话框键入数据集的名字li1_1,此名可任意起,少于8个字符,选择完后,单击FINISH按钮,就完成了此次操作。 这时已经建好了一个数据集,名为WORK.li1_1,与我们前面建立的数据集完全一致。 §3.3 SAS程序的程序步 通俗的讲,SAS的程序步(有的书中也称过程或过程步)就是已经编好了的用于数据整理和统计的计算机程序,你只需要调用它们就是了。程序步总是用一个PROC语句开始,后面紧跟着程序步名,用以区分不同的程序步。以下是一些常用的程序步的名称及功能。 程序步名
+ S: ]& W. z c# \功 能 SORT 将指定的数据集按指定变量排序
8 m. Z! M: }* V: U4 q9 o* fPRINT 将数据集中的数据列表输出
8 q) _/ r. |# _1 J' Y. K' R5 [MEANS 对指定的数值变量进行简单的统计描述
- K% T7 s/ a& i' u* oFREQ 对指定的分类变量进行简单的统计描述
# x0 |* ^( ~+ I- T ^4 y8 N( @TTEST 对指定的变量做t检验
$ [4 O0 X% e& D; ]3 RANOVA 对指定的变量做方差分析
' w G. N, C0 v2 WNPAR1WAY 对指定的变量做非参数检验1 ]# n2 D" Y2 v2 P/ q7 a
REG 对指定的变量做回归分析
7 @& Z/ D- }- B1 D. r7 ^# jCORR 对指定的变量做相关分析
( D& W% r$ \5 ~1 v% qCHART 绘出低分辨率的统计图 PROC就是程序(procedure)的缩写,而程序步的名字大都是其功能相对应的单词或词组的缩写。 可见SAS的许多功能就是通过程序步来体现的,可如此多的种类也使我们难以总结出一个程序步的通式来。不过仍可大致给出如下结构: PROC 过程名 [DATA=数据集名] [选项];
0 K3 H M; S6 O3 y+ r 该过程的专用语句描述;& z2 H8 U5 m! T+ ^" v( S4 f5 W
[VAR 变量序列;]# H0 P- `0 z( w* H" f) N
[WHERE 条件表达式...;]: L2 H) b0 R+ o
[BY 变量序列;]
: u7 ^1 m) V- E) }, c0 ^Run; 方括号里的语句均可以省略,在这时该过程按最通常的情况来处理,即: 处理最新建立的SAS数据集。
K' V& K, W! _处理所有的变量(或对一个计算过程来说处理全部数值变量)。
& R' e* a. Z9 m% V2 e一次处理整个数据集而不是某个子集。+ H Y7 y9 G8 U$ @
我们在第一章中用到的PRINT程序步就是采用的这种默认方式。 但有时我们的要求超出了默认方式所提供的范围,这时就要动用方括号里的秘密武器了。 DATA参数 指明所需处理的数据集名,请注意在这里它是一个参数而不是语句。
2 ^3 {7 \9 h9 Y6 E2 m5 x2 [VAR语句 如果只想分析某一个或几个特定的变量,则可用VAR语句指定它们。例如只想显示变量x的列表,则PRINT过程如下:6 \- [' j, {& L. z
Proc print ;
6 I, u& I5 f1 ^; b" b5 c Var x;
2 J7 t6 u; s# T, u# ARun; 如果想显示x和y两个变量,则将VAR语句改为如下形式即可: var x y; 当然,也可以将VAR语句删除,结果相同。 Where语句 如果你想处理的不是整个数据集而只是其中符合某种条件的子集,那么WHERE语句将会非常有用,如上例中我们只想显示大于50的x变量的值,则在PRINT过程中加入where语句如下: v& Z) y. x& `9 S
Where x>50; 如果条件变为x、y两个变量的值都要大于50,则where语句改为: where x>50 and y>50; SAS语言中常用的逻辑表达符号有and(和)、or(或)、xor(异或)、ne(不等)等。 By语句 如果你需要分组处理数据,例如要按性别分组输出统计结果,你有两种选择,一是用不同的where语句将同一个程序步反复写几遍;另一种显然更酷的方法就是采用by语句。如上例我们想按不同的y值输出x值,则加入by语句如下:
: K1 W% x I, k6 s- ~2 A5 {5 z4 \By y; 使用BY语句要求数据集已经按BY语句中指定的变量排序。如果没有排序,则程序无法正确运行。可以用SORT过程来排序,语法结构如下: PROC SORT DATA=数据集名;' ~6 |. w' H1 W, K; l1 L
BY 变量名列;
# p$ u7 u' `- D0 @RUN; §3.4 结构化语句简介 每一种结构化语言编写的程序都由顺序、分支、循环三种结构构成, SAS语言也不例外。在这里简要介绍一下分支和循环语句的语法。这些语句均可直接在数据步和程序步中使用,适当地使用它们可以大大简化我们的工作。 3.4.1 分支(条件)语句 【语法格式】 语法格式如下: IF 条件 THEN$ @* b2 O0 h6 o
程序块;
$ E4 h9 c7 v& }( q8 N+ e- DELSE 程序块; 可见其语法和FOXBASE语言十分相似,只是前面多了THEN,结尾没有ENDIF,可以将两者相比较来理解。其中程序块如果只有一句,则可直接写出,否则应以DO开头,以END结束。 【应用实例】 例3.1 在产生数据集temp的同时为其增加变量class,当x>50时class=1,否则class=2。 解:程序如下: data temp;
3 {7 K" J* V, G 数据步开始,定义要建立的数据集为WORK库的TEMP
" q% O( i; D, A3 b) a$ p: d- a input x y@@;* G% C6 g: z/ v* _% S5 f3 ]
要输入的变量为X和Y,并且采用数据连续读入方式
; i7 T% L- a% d1 M1 c( j if x>50 then class=1;9 m' K" W r* R2 S3 K( k% Q0 G6 \
建立新变量CLASS,如果X>50,则CLASS=1 M' p3 H) [! j, o9 x
else class=2;
4 G: G3 c; m% e/ x' c/ U 否则,CLASS=2% P& e/ F6 A3 K& {5 c; \
cards;
4 d7 \- p) q* M, A 数据块开始
! p. s9 Z! {5 Z; g7 c0 h34 56 78 90 35 67 89 10 23 65 77 45
2 @" ~; p1 i2 A! J2 ^! d+ ? 数据块1 O' a7 n8 Q6 }4 z% Z6 W8 e- y
;# u: Q- d7 `3 M/ A) V. @
数据块结束8 ?4 [ a% Q8 a( r# p$ R0 h8 |
proc print; 列表输出数据集中的数据,检查有无错误7 e7 w2 [- ]# p+ K q
run; 程序结束,开始运行以上程序 以上程序在书写时采用了缩进格式,使程序的结构更清楚。当然大家可以左对齐写完所有的语句,但这样书写的程序在较长时难以阅读。 为什么可以这样写?在这里有必要解释一下数据步的执行过程,input语句按变量顺序将数据读入内存缓冲区,直至每一个变量都有值相对应。然后数据步继续向下执行,从而其它语句可以修改内存缓冲区,如修改值、增加变量等,在执行到数据步结束后,程序又回到input语句处继续执行,直到数据读完为止。现在再看看这个程序,大家可以理解它了吧! 3.4.2 循环语句 【语法格式】 语法格式如下: DO 起始条件 TO 终止条件;# H; L6 T7 q0 Q$ s3 Q. a
程序块;; k9 t- J2 h# `$ i, W8 k% a
END; 该语句主要用于建立数据集。 【应用实例】 例3.2 在产生数据集temp的同时为其增加变量class,取值依次为1、2。 解:程序如下: Data temp; 数据步开始,定义要建立的数据集为WORK库的TEMP
, d7 E* M8 T* y, i, g7 N do class = 1 to 2; 循环开始,循环控制变量为CLASS,取值从1到24 d, u. _; @' ?6 {
input x y@@; 要输入的变量为X和Y,并且采用数据连续读入方式% [! u' `5 g _/ z
output; 用OUTPUT语句将循环控制变量写入数据集中& R8 E# m2 M0 I9 D! Y
end; 循环结束
, }5 V3 |) e6 \0 j a) \0 xcards; 数据块开始
H2 Q9 q) s1 c* a- Y34 56 78 90 35 67 89 10 23 65 77 45
+ g8 F2 J G5 `0 f% V& }; S 数据块
: ?/ n9 w& h% V* w5 A. q F; 数据块结束
7 e& s. c; j( Jrun; 程序结束,开始运行以上程序 注意在数据步中,我们不需要用语句改变循环变量的大小,系统会自动改变。 现在,SAS语言的基本知识算是介绍完了。在理论上,你现在可以坐在计算机前,独立编写程序以做出你的统计作业或者是向SAS公司发射核导弹(狗头)。
1 P' r8 H$ a% c8 @# r* l2 Z4 L6 b% O" P2 g8 n6 J/ R6 ]
|