SAS系统和数据分析用在DATA步的控制语句.docx
- 文档编号:9729584
- 上传时间:2023-02-06
- 格式:DOCX
- 页数:26
- 大小:160.01KB
SAS系统和数据分析用在DATA步的控制语句.docx
《SAS系统和数据分析用在DATA步的控制语句.docx》由会员分享,可在线阅读,更多相关《SAS系统和数据分析用在DATA步的控制语句.docx(26页珍藏版)》请在冰豆网上搜索。
SAS系统和数据分析用在DATA步的控制语句
第一十五课用在DATA步的控制语句
DATA步的基本概念、流程和有关文件的操作语句我们前面已介绍。
但我们所介绍的DATA步中的SAS语句都是按语句出现的次序对每一个观测进行处理。
有时需要对一些确定的观测跳过一些SAS处理语句,或者改变SAS语句的处理次序,就需要用到DATA步中的控制语句,实现SAS程序的分支、转移和循环等改变处理次序的功能。
SAS系统提供的控制语句从实现功能的角度看主要有以下五大类:
●实现循环(DO语句)
●实现选择(SELECT语句)
●实现分支(IF语句)
●实现转移(GOTO语句)
●实现连接(LINK语句)
一、实现循环(DO语句)
循环程序中使用DO语句的主要形式有四种,如下所示:
●DO语句的程序格式之一:
IF条件表达式THENDO;
一些SAS语句;
END;
●DO语句的程序格式之二:
DO变量=开始值TO终值BY步长值;
一些SAS语句;
END;
●DO语句的程序格式之三:
DOWHILE(条件表达式);
一些SAS语句;
END;
●DO语句的程序格式之四:
DOUNTIL(条件表达式);
一些SAS语句;
END;
DOWHILE和DOUNTIL语句中的表达式是用括号括起来的。
两种循环程序格式的区别是,对条件表达式的判断位置。
DOWHILE是在循环体的开头,而DOUNTIL是在循环体的结束,也就是说DOUNTIL至少执行循环体中一些SAS语句一次。
下面我们举例来说明DO语句的使用。
1.使用循环DO组产生随机数数据集
例如,我们需要产生一组均匀分布的随机数流的数据集,程序如下:
DataDoRanuni;
seed=20000101;
DoI=1to10by2;
X1=ranuni(seed);
X2=ranuni(seed);
Output;
End;
Procprintdata=DoRanuni;
Run;
程序中的X1和X2都采用相同种子变量值SEED=20000101来产生的均匀分布的随机数流。
在数据步DATA中使用DO循环语句时常常与OUTPUT语句配合来产生数据集。
OUTPUT语句作用是把当前的观测输出到正在被创建的数据集DoRanuni中。
第一次顺序执行产生Seed、I、X1、X2四个变量,OUTPUT输出后,遇到END语句回到DO语句,产生I、X1、X2变量的第二次值,Seed变量因为没有遇到DATA语句,继续保持原来值,DO-END循环结束后,DATA步也就结束了。
均匀分布随机数是最基本也是最重要的随机数,其他分布的随机数都可以用均匀随机数经过变换得到。
最常用的均匀分布随机函数是RANUNI(seed),这个函数是一个模为231-1,乘子为397204094的素数模发生器。
Seed必须是小于模231-1任何数值的常数。
相同的Seed值会产生相同的随机数序列数,但不同次调用随机函数所产生的值通常是不同的,因此计算机所产生的随机数是一种伪随机数。
这个程序中的X1和X2都采用相同种子变量值SEED=20000101所产生的均匀分布的随机数流。
SAS系统提供产生了11种常见分布随机数的函数,如表15.1所示,随机数是我们实验和研究问题的重要的输入数据。
因此要能编写程序,产生符合要求分布的随机数数据集。
程序运行结果如图15.1所示。
图15.1用循环DO组产生随机数数据集
表15.1SAS系统的各种随机函数
随机数函数名
作用
UNIFORM(seed)
产生(0,1)区域均匀分布随机数,乘同余发生器
RANUNI(seed)
产生(0,1)区域均匀分布随机数,素数模发生器
NORMAL(seed)
产生标准正态分布随机数,利用中心极限定理近似公式
RANNOR(seed)
产生标准正态分布随机数,利用变换抽样法
RANEXP(seed)
产生λ=1的指数分布随机数
RANGAM(seed,alpha)
产生伽马分布随机数,alpha>0,seed为任意数值
RANTRI(seed,h)
产生三角分布随机数,0 RANCAU(seed) 产生标准柯西分布随机数 RANBIN(seed,n,p) 产生二项分布随机数,n>0的整数,0 RANPOI(seed,lambda) 产生泊松分布随机数,lambda>0,seed为任意数值 RANTBL(seed,p1,…,p2,…pn) 产生离散分布随机数,0≤pi≤1,seed为任意数值 注: 种子seed一般取0,或5位,6位,7位的奇整数。 对于均值为M,标准差为S的正态分布随机数,可由标准正态分布随机数的线性函数得到: X=M+S*NORMAL(seed) 2.在循环DO组中使用下标数组产生数据集 当我们需要用同一种方法来处理很多变量时,可以用数组语句定义这组变量为数组的一些元素,这个数组中的一些元素就可以在DATA步中较后面的SAS语句里以数组下标的形式被引用。 数组ARRAY语句的基本格式为: Array数组名{下标}<$><长度><<数组元素><(初始值)>>; 例如,以下的几种数组定义方式都是合法的: ●Arrayx{3}T1T2T3; ●Arrayx{5,3}T1-T15; ●Arrayx{2: 6,2: 4}T1-T15; ●Arrayx{3}T1T2T3(100,99,98); ●Arrayx{*}T1T2T3; 第一种方式表示定义了一个一维名为X的数组,它有三个元素,对应的变量为T1,T2和T3。 第二种方式表示定义了一个二维名为X的数组,它共有5×3=15个元素,对应的变量为T1到T15。 第三种方式与第二种方式的区别是还规定每一维下标的下界和上界,通常不特别指明下标的下界从1开始。 第四种方式给出了数组中相应元素的初始值T1=100,T2=99,T3=98。 第五种方式下标用星号*来代替,表示SAS系统通过数组中的变量个数来确定下标。 下面我们通过一个在循环中使用数组变量来产生一个新的数据集的例子,来说明循环中使用数组变量的用法。 假设要由一个老的数据集产生一个新的数据集,新的数据集要新增n个变量,新增变量Ti的值与原数据集的变量Xj值和新增变量的位置值i有关。 为简便起见,假设新增变量Ti=∑Xj×i。 程序如下: DataDoArray; InputX1-X3; Arraya{4}T1-T4; Doi=1to4; a{i}=(X1+X2+X3)*i; End; Card; 123 456 Procprintdata=DoArray; Run; 程序运行结果如图15.2所示。 图15.2循环DO组中使用数组来产生数据集 此程序循环结构是外循环DATA步执行二次,因此产生二条观测,在每次外循环中,内循环DO重复执行四次,新增四个变量。 使用DO语句的循环变量i作为数组的下标,这种下标的使用方法是DO循环中处理下标数组最常用的方法。 3.在循环DO组中使用OUTPUT语句产生数据集 如果在DO-END内循环结束语句END前插入一条OUTPUT语句,那么每次内循环将输出一条观测,而不是内循环所产生的所有变量值只输出在一条观测中。 对于每条观测的变量取值,要注意在DATA步的同一个外循环中,已经产生的变量值保持不变,内循环OUTPUT语句前未赋值的变量为缺失值,直到DATA步的下一个外循环开始时,所有用INPUT或赋值语句创建的变量在重复DATA步开始时将被设置为缺失值。 我们以上面的程序END语句前插入一条OUTPUT语句为例,程序运行结果如图15.3所示。 图15.3循环DO组中使用了OUTPUT语句 4.用循环DO组和RETAIN语句产生数据集 由已有数据集产生具有新增变量的新数据集时,通常新增变量值是本条观测原变量值和循环变量值的函数值。 但如果新增变量值是历史观测中原变量值和循环变量值的函数值时,首先考虑是将历史观测中原变量值取到中间变量,例如数组变量中。 但是因为在每次重复开始DATA步时,所有用INPUT或赋值语句创建的变量将被设置为缺失值,那该如何处理解决呢? 在SAS系统中有一条RETAIN语句专门用于解决这一问题。 在数据步DATA中使用了RETAIN语句来为变量设置初值后,RETAIN语句使得用INPUT语句或赋值语句所指定的变量值从DATA步的这次执行到下一次重复时被保留。 而如果没有使用RETAIN语句,DATA步每次重复执行之前这些变量就会被设置为缺失值。 我们这里给出了RETAIN语句几种常用的使用格式: ●Retain; ●RetainT1T2T3; ●RetainT1T2T3100; ●RetainT1T2T3(100); ●RetainT1T2T3(1009998); 第一种使用格式表示用INPUT语句或赋值语句创建的所有变量从DATA步的这次执行到下一次重复时被保留。 第二种使用格式规定了变量名字,变量列表或数组名,它们的值是用户想保留的。 第三种使用格式表示一个变量列表T1、T2、T3接受同一个初始值100。 第四种使用格式是将初始值100用小括号括起来,SAS系统将分配括号中的这个值给变量列表中的第一个变量,即T1=100,T2和T3为缺失值。 第五种使用格式给出了初始值列表,将依次分配初始值列表中的值给变量列表中各个变量,即T1=100,T2=99,T3=98。 例如,我们有一组日期DATE和收盘价CLOSE股票数据,要生成一个带有3日移动平均价MOVEAVER的数据集。 所谓3日移动平均价,指最近3日收盘价的平均值,即当天、昨天和前天收盘价的平均值。 昨天和前天收盘价数据要在产生时用RETAIN语句事先保留下来,在计算当日移动平均价时才不至于取到缺失值,而不能计算。 为了便于理解,我们将程序编写如下: DataMAV; Retainday3i1; Inputdatedate7.close; Arraya{4}; a{i}=close; Retain; If_n_>=dayThendo; t=0; Doi=1today; t=t+a{i}; a{i}=a{i+1}; End; moveaver=t/day; i=day-1; End; i=i+1; Card; 19MAY9912.41 20MAY9913.65 21MAY9915.02 24MAY9916.52 25MAY9918.17 Procprintdata=MAV; Run; 程序中使用了自动变量_n_,它自动存放当前观测的逻辑顺序号。 移动平均价的计算要考虑初始情况,对于3日移动平均价来讲,第一条和第二条观测的3日移动平均价的数值是不存在的,从第三条观测开始有计算数据。 程序中的IF语句就是为了判断从第三条观测开始计算3日移动平均价而设置的。 第一条RETAIN语句的目的是设置常量day=3,下标变量i的初值为1,第二条RETAIN语句的主要目的是为了保留存放收盘价数据的数组a{i},我们为了使大家明白各个变量的变化过程,RETAIN语句后未专门指定数组a{i},而是保留用INPUT语句或赋值语句创建的所有变量。 如果指定数组a{i},产生的数据集只有DATE、CLOSE和MOVEAVER三个变量。 DO-END循环的作用是用T累加器求最近三日收盘价之和,T变量每次求和前要初始化为0。 为了始终保持数组a{1}、a{2}、a{3}中存放的是最近三日的收盘价数据,用a{i}=a{i+1}语句将刚计算过的数据前移,a{2}中数据存放a{1},a{3}中数据存放a{2},a{1}中原数据正常丢弃,留空a{3}准备读下一条记录的收盘价。 程序运行结果如图15.4所示。 图15.4用循环DO组、数组和RETAIN语句所产生的MAV数据集 对于只有少量观测的数据集,我们还可以编写如下程序: DataMAV(Keep=dateclosemoveaver); Retainday3; Inputdatedate7.close; Arraya{5}; a{_n_}=close; Retain; If_n_>=dayThendo; t=0; Doi=_n_-day+1to_n_; t=t+a{i}; End; moveaver=t/day; End; Card; 19MAY9912.41 20MAY9913.65 21MAY9915.02 24MAY9916.52 25MAY9918.17 Procprintdata=MAV; Run; 这个程序的特点是将历史记录的收盘价全部存放到数组a{i}中,因此程序编写简单和短小,容易理解,执行速度快。 但它有一个致命的缺点,数组a{i}的大小与记录的条数相关,当实际数据的记录数很大时,将占用很大的存储空间,甚至发生内存溢出错误而不能运行。 而前面程序中的数组a{i}大小只与移动平均的天数有关,相对来说是一个很小的数且是固定的数。 当然,以上的程序只是为了说明循环DO组和RETAIN语句的使用,在实际应用中,求移动平均线最简单的方法是采用求滞后值LAGn函数。 例如求3日移动平均线相应的程序如下: DataMAV; Inputdatedate7.close; Moveaver=(close+lag1(close)+lag2(close))/3; Card; 19MAY9912.41 20MAY9913.65 21MAY9915.02 24MAY9916.52 25MAY9918.17 Procprintdata=MAV; Run; 二、实现选择(SELECT语句) 在DATA步中使用SELECT语句,使SAS系统去执行几条语句或语句组中的一条语句。 本质上是一种多路分支结构,当然可以用IF—THEN结构的多层嵌套来实现,但当分支较多时,SELECT语句会很方便,结构也很清楚。 SELECT—WHEN结构非常类似其他程序语言中的DOCASE结构。 SELECT语句开始一个SELECT组,SELECT组包括一些WHEN语句,当一个设定的条件为真时,执行对应的WHEN语句给出的一些SAS语句。 在SELECT组中至少必须有一个WHEN语句。 如果所有的WHEN条件均不成立,那么执行OTHERWISE语句给出的SAS语句。 最后用一个END语句结束这个SELECT组。 SELECT语句的一般格式如下: Select<(select表达式)>; When(when表达式)任意可执行SAS语句; When(when表达式)任意可执行SAS语句; …… Otherwise任意可执行SAS语句; End; 如果有SELECT的表达式,首先比较select表达式和第一个when表达式计算结果的大小,相等为真,执行这个WHEN后面的SAS语句;不相等为假,执行下一条WHEN语句或OTHERWISE语句。 如果没有SELECT的表达式,when表达式中一定要含有比较操作符,先求出第一个when表达式的真假结果,如果为真,执行这个WHEN后面的SAS语句,如果为假,执行下一条WHEN语句或OTHERWISE语句。 要特别注意,一个SELECT组是按顺序判断WHEN语句,一旦某个WHEN语句满足条件执行了,就跳出这个SELECT组。 即使后面WHEN语句也满足条件,也不再执行了。 所以,程序员要注意安排好WHEN语句的顺序和条件。 例如,我们有一个CLASS数据集,存放的是学生的成绩表,我们要创建一个新的数据集CLASS02,新增平均成绩AVER变量和等级考评GRADE变量,GRADE的值是根据AVER值的大小给出A、B、C、D四个等级。 程序如下: LibnameStudy“d: \sasdata\mydir”; DataClass02; SetStudy.Class; aver=(test1+test2+test3)/3; Select; When(aver>=90)grade=”A”; When(aver>=80)grade=”B”; When(aver>=70)grade=”C”; Otherwisegrade=”D”; End; Procprintdata=Class02; Run; 程序运行结果见图15.5所示。 图15.5用SELECT—WHEN组产生的数据集结果 三、实现分支(IF语句) 在SAS语言中,IF语句有两种使用格式: ●IF语句的格式之一: IF条件表达式THENSAS语句; ●IF语句的格式之二: IF条件表达式; 当我们在创建SAS数据集时使用IF语句的格式之一,可以根据被处理的观测是否使IF条件表达式为真,来决定是否执行THEN后面的SAS语句。 如果条件表达式为假,执行ELSE后面的语句,没有ELSE语句执行IF语句的下一条语句。 IF—THEN/ELSE语句可以多层嵌套,但要注意IF—THEN/ELSE语句的程序编写格式和匹配。 另外,THEN和ELSE后还可以跟DO语句,格式见DO语句的程序格式之一。 1.使用IF—THEN/ELSE语句 例如,我们有一个SURVEY数据集,其中有人的身高HEIGH和体重WEIGHT数据,现在我们要创建一个新的数据集SURVEY02,新的数据集新增一个变量FATNESS,用以标识这个人是否肥胖。 假设,人的标准体重计算公式.: 男性之标准体重(千克)=身长(厘米)-100,女性之标准体重(千克)=身长(厘米)-102。 如果超过标准体重的20%,就算肥胖,用Y来表示,否则用N来表示。 程序如下: LibnameStudy“d: \sasdata\mydir”; DataSurvey02; SetStudy.Survey; ifsex="M"then ifweight>=(heigh*100-100)*1.2thenfatness="y"; elsefatness="n"; else Ifweight>=(heigh*100-102)*1.2thenfatness="y"; elsefatness="n"; Procprintdata=Survey02; Run; 在使用IF—THEN/ELSE语句的多层嵌套结构时要注意两点: 一是要以锯齿型来排列一组组IF—THEN/ELSE语句,目的是为了通过程序书写格式就能明确IF—THEN/ELSE语句的层次和配对。 二是如果THEN和ELSE后的SAS语句不直接写在同行的后面,而是写在下一行,THEN和ELSE后不能加分号“;”,表示本程序行并没有结束,直到遇到分号“;”为止。 程序运行结果我们在这里就省略了。 2.使用IF语句 当我们在创建SAS数据集时使用IF语句的格式之二,常用来创建满足IF条件表达式的新数据集。 例如,我们要从SURVEY数据集中挑选出所有男性观测来产生一个新数据集SURVEY03。 程序如下: LibnameStudy“d: \sasdata\mydir”; DataSurvey03; SetStudy.Survey; ifsex="M"; Procprintdata=Survey03; Run; 四、实现转移(GOTO语句) GOTO是一种无条件转移语句,它告诉SAS系统立即转到GOTO语句所指的那条语句,并从那条语句开始执行后面的语句。 GOTO语句的实现,是通过GOTO语句中和目标语句前使用相同的语句标号才识别目标和实现转移的。 要注意GOTO语句只能在同一个DATA步中进行转移。 GOTO语句的基本使用格式如下: Data数据集; ……; GOTO语句标号; ……; 语句标号: ……; ……; Run; GOTO语句是一种非结构化程序设计语句,它通过语句标号可转移到任意目的地的特性常常破坏程序的结构化,许多其他语言都不主张使用或禁止使用。 当然GOTO语句的功能是可以通过其他结构化控制转移语句替代实现。 但是GOTO语句也有明显的特点,能很方便地和灵活地进行转移。 GOTO语句最常见的使用,是出现在IF—THEN语句中,作为THEN分句。 例如,我们要对当前输入的一组成绩数据TEST进行动态统计,计数到目前输入为止不及格(<60分)的人数COUNTIF,和累计的总分ACCTEST(包括不及格的分数)。 为了使大家明白整个过程,我们创建一个记录过程的数据集CLASS03,且显示出来。 程序如下: LibnameStudy“d: \sasdata\mydir”; DataStudy.Class03; inputtest@@; retaincountif0acctest0; Iftest>=60thenGOTOok; countif=countif+1; ok: acctest=acctest+test; cards; 1009045558030 Procprintdata=Study.Survey02; Run; 程序运行结果如图15.6所示。 图15.6用GOTO语句设计的条件计数器和累加器 程序中的ok: acctest=acctest+test累加语句,如果条件不成立,执行完countif=countif+1计数语句后,也要执行此累加语句。 如果我们在countif=countif+1计数语句后,ok: acctest=acctest+test累加语句前插入一条RETURN返回语句,RETURN语句的作用是返回到DATA步的开头处理新的输入数据,相应地程序的作用也发生了变化,不是累加全部的成绩,而是累加及格的成绩,计数器仍然计数不及格的人数。 最后的结果是,OBS=6时: COUNTIF=3,ACCTEST=270。 五、实现连接(LINK语句) LINK连接语句的作用是立即转到由LINK语句指示的语句标号,并从那里开始继续执行语句直到一个RETURN语句被执行。 执行RETURN语句立即返回到LINK语句后面的那个语句并从那里继续执行。 与GOTO语句一样,LINK语句和语句标号所在的目标语句必须在同一个DATA步中。 LINK语句的基本使用格式如下: Data数据集; ……; LINK语句标号; ……; ……; RETURN; 语句标号: ……; ……; Run; LINK—RETURN结构从程序执行的本质看,是调用子程序和子程序返回。 类似于其他程序语言中转子语句和返回语句。 它们都是可
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- SAS 系统 数据 分析 DATA 控制 语句