第三章 修改SAS数据集
一.利用窗口编辑数据
上一章我们介绍过利用FSEDIT和FSVIEW过程建立SAS数据集,并在新的数据集中输入数据。现在,我们介绍利用FSEDIT和FSVIEW过程对已有的SAS数据集中的数据进行编辑和修改。
1.调用FSEDIT或FSVIEW过程
首先,我们要进入FSEDIT或FSVIEW窗口才能进行编辑,调用FSEDIT或FSVIEW过程的一般形式为:
PROC FSEDIToptions;
或 PROC FSVIEW options;
例:proc fsedit data=clinic.admit label; run;
或 proc feviewdata=clinic.admit; run;
在程序编辑器中输入上述程序,并提交,就可进入FSEDIT或FSVIEW窗口,对指定的SAS数据集进行编辑。
2.用FSEDIT过程编辑数据
1)改变当前显示的数据
在FSEDIT窗口中,每次显示一条观测的内容,为改变其中某一变量的内容,可利用TAB键将光标移至相应位置,输入新值,将旧值覆盖即可。
2)改变当前显示的观测
在FSEDIT窗口中,只能改变当前显示的观测中的内容。为了改变多条观测中的内容,你需要改变当前显示的观测,你可以利用下列操作命令在不同观测之间移动:
操作命令 功能
View à Next observation 显示下一条观测
View à Previous observation 显示下一条观测
3)改变当前显示的屏幕
有时候,SAS数据集中的变量太多,无法在一个屏幕(screen)中显示,只好用多个屏幕显示一条观测的内容,发生这种情况时,系统会在你FSEDIT窗口时给你一个提示,例如:
NOTE:This application uses 2 screens.
为了编辑观测中的某一个变量的数据,你可能需要在不同的屏幕之间移动,你可以使用下列操作命令:
操作命令 功能
View à Next screen 显示下一个屏幕
View à Previous screen 显示下一个屏幕
4)取消修改
如果你在修改某个数据后发现有错,你可以用下述操作取消修改:
FileàCancel
该数据会恢复原先值。注意,你必须在离开当前观测之前才能取消修改,一旦移到其它观测,就无法再恢复原先值了。
5)结束编辑
当你完成数据集的修改后,可用下述操作结束FSEDIT过程:
Fileà End
结束过程时,你对数据集所做的修改会被自动保存。
3.用FSVIEW过程编辑数据
在FSVIEW窗口中,你可以对以表格形式显示的数据进行修改。
1)修改数据
FSVIEW窗口刚打开时,其缺省模式是浏览模式,也就是说这时候你不能改变它的内容,在FSVIEW窗口的标题栏中会显示一个字母‘B’,例如:
FSVIEW: CLINIC.ADMIT2 (B)
为了对SAS数据集进行修改,要将FSVIEW窗口的模式改为编辑模式,可如下操作:
EditàUpdate àRECORD
这时,FSVIEW窗口的标题栏中会显示一个字母‘B’,例如:
FSVIEW: CLINIC.ADMIT2 (E)
在这种模式下,你每次可对一条观测进行修改,将光标移到你想要修改的观测处即可进行。
2)改变显示
FSVIEW窗口可显示多条观测,每一行显示一条观测,但一般来说,一个屏幕不可能显示一个数据集中所有的内容。你可以使用滚动栏,或功能键来改变当前屏幕中显示的内容。功能键应对应下列命令:
Forward
Backward
Right
Left
你可以打开KEYS窗口查看或改变功能键的设置。
3)结束编辑
当你完成数据集的修改后,可用下述操作结束FSEDIT过程:
Fileà End
结束过程时,你对数据集所做的修改会被自动保存。
4.WHERE条件
利用WHERE条件可从你的数据集中产生一个临时的子集,以简化编辑过程。
例如,你只需对数据集中变量AGE的值大于30的观测进行修改,利用WHERE条件,可使FSEDIT或FSVIEW窗口中只显示满足条件的观测。
1)设定一个WHERE条件
选择 Search à Where
然后在出现的Where窗口中输入你的条件表达式,例如:
age> 30
再选择‘OK’即可。这时,在FSEDIT或FSVIEW窗口的标题栏的右侧显示‘Where……’,同时,窗口中仅显示满足条件的观测。
2)条件表达式中的比较符号
条件表达式的一般形式为:
variablecomparison-operator value
其中variable是一个变量名,comparison-operator是一个比较符号,value是这个变量可取的值。可用的比较符号有:
= (eq) ^= (ne) > (gt)
< (lt) >= (ge) <= (le)
3)附加一个WHERE条件
在设定一个WHERE条件后,重新设定一个WHERE条件,新的条件将覆盖旧的条件。如果希望新旧条件都起作用的话,可附加一个WHERE条件:
选择 Search à Where also
然后在出现的Where窗口中输入新的条件表达式,再选择‘OK’即可。
4)取消一个WHERE条件
选择 Search à Undo last where
可取消最近的一个WHERE条件
5)取消所有WHERE条件
若想取消所有WHERE条件,可作如下操作:
选择 Search à Where
然后在出现的Where窗口中,不输入任何条件表达式,直接选择‘OK’即可。这时,在FSEDIT或FSVIEW窗口的标题栏的右侧不再显示‘Where……’,同时,窗口中显示数据集中所有的观测。
二.利用DATA步修改数据
1)利用DATA步修改SAS数据集内容
上一章我们介绍了利用DATA步从原始数据文件中读取数据,建立SAS数据集的过程和方法。这里,我们介绍利用原有的SAS数据集,对其中的数据进行系统的修改,从而建立新的数据集的方法。也就是说,新建的数据集是通过对原有数据集的修改而得的,原有数据集可以保持不便。
为了完成这一工作,在DATA步中将主要包含下列语句:
SAS 语句 |
作用 |
DATA语句 |
命名新的数据集及其包含的变量 |
SET语句 |
命名新的数据集及在建立新数据集时需要用到的变量 |
IF语句 |
选择观测 |
赋值语句 |
创建变量或改变变量值 |
2)命名数据集
你可以用LIBNAME语句指定一个数据库,然后就可以用一个DATA步对数据库中的数据集进行操作,包括读取已有的数据集的内容和建立新的数据集。
首先,在DATA语句中命名一个你要创建的数据集,一般形式为:
DATASAS-data-set options;
然后,用SET语句指定一个你将要从中读取数据的SAS数据集,一般形式为:
SETSAS-data-set options;
例: data lab123.drug1h;
set research.cltrials;
run;
注意,在上述例子中,没有INPUT语句,也没有赋值语句,也没有加入一些选项,如果提交这样几个简单的数据步的话,系统会建立一个名为LAB123.DRUG1H的数据集,它的内容与原有的数据集RESEARCH.CLTRIALS的内容完全一样。
3)根据条件对观测进行处理
由于我们的目的不是复制一个数据集,而是要在原有数据集的基础上作一定的修改,因此要对从原有数据集中读取的观测和变量进行某种处理,再写入新数据集中。例如,你可以选择只有满足某种条件的观测才会被写入新数据集中,可采用IF语句,IF语句的形式和用法在上一章已介绍过,这里不再重复。
例: data lab123.drug1h;
set research.cltrials;
if placebo='yes';
run;
4)创建和改变变量
有时候,仅仅对观测进行选择并不能满足你的要求,你可能希望新的数据集中有旧数据集中所没有的变量,或则,某个变量的值要进行一些改变。为此,你可以在数据步中使用赋值语句,其一般形式为:
variable=expression;
其中variable是一个新建的或原有的变量名,expression是任何有效的SAS表达式。当同一个变量名同时出现在等号两边时,该变量的原有值被用于在等号右边估计表达式的值。
例: data lab123.drug1h;
set research.cltrials;
if placebo='yes';
cholchng=chol2-chol1;
glucose=glucose+glucose*.10;
avgchol=mean(chol1,chol2);
run;
注意,赋值语句是少数几个不以关键词开头的SAS语句之一。
在表达式中,你可以使用“+、-、*、/、**”等算术运算符号,也可以使用一些SAS函数。
你可以在HELP窗口中找到SAS提供的所有运算符,方法为:
Helpà SASLanguage à SASExpressions à SASOperators
除了这些运算符之外,SAS系统还提供很多函数,下表为一些简单例子:
类别 |
关键词 |
描述 |
算术函数 |
MIN MAX |
返回参数中的最小值 返回参数中的最大值 |
进位函数 |
ROUND |
根据设定的有效位数取最接近的值 |
简单统计函数 |
MEAN SUM |
计算参数的算术平均值 计算参数的和 |
字符函数 |
SCAN UPCASE |
从字符表达式中取一个词 将字符变为大写 |
你可在HELP窗口中找到所有SAS函数及其描述,具体方法为:
Helpà SASSystem à SASLANGUAGE à SASFunction àFunction Categories
5)选择变量
在新建立的SAS数据集中,你可能并不希望包含在原有数据集中以及在DATA步中出现的变量,为此,你可以在DATA语句和SET语句中使用DROP=或KEEP=选项。一般形式为:
DATASAS-data-set (DROP=variables);
SET SAS-data-set(DROP=variables);
或 DATA SAS-data-set(KEEP=variables);
SET SAS-data-set(KEEP=variables);
使用这两个选项的一般情况是:
A.如果你根本不需要某些变量,那么在SET语句中的DROP=选项中指定它们,这样,它们就不会被读取。
B.如果你在计算中需要某些变量,但不希望它们在新数据集中出现,那么在DATA语句中的DROP=选项中指定它们,这样,它们就不会被写入。
C.如果需要被丢掉(DROP)的变量比要保留(KEEP)的变量多的话,用KEEP=选项代替DROP=选项。
三.利用SAS函数转换数据
1.SAS函数的作用
SAS函数是一些预先写好的程序,它能帮你快速简单地完成各种数据处理工作。SAS函数能完成多种多样的任务,下面是一些SAS函数的分类:
类别 |
例 |
作用 |
Arithmetic(算术) |
ABS(argument) |
返回绝对值 |
Character(字符) |
LEFT(argument) |
字符串靠左对齐 |
Data Set(数据集) |
CLOSE(dsid) |
关闭一个SAS数据集 |
Date and Time(日期和时间) |
DATE() |
返回当天的日期值 |
External File(外部文件) |
DCLOSE(directory-id) |
关闭一个目录 |
Financial(财务金融) |
DEPSL(period,value,years) |
直线折旧 |
Mathematical(数学) |
LOG(argument) |
自然对数 |
Probability and Density(概率与密度) |
PROBNORM(x) |
标准正态分布函数 |
Random Number(随机数) |
RANUNI(seed) |
产生一个平均分布的随机数 |
Sample Statistic(样本统计) |
RANGE(argument……) |
返回样本范围 |
Special(特殊) |
LAGn(argument) |
返回滞后n阶的值 |
Variable(变量) |
VARTYPE(dsid,var-num) |
返回SAS数据集变量的类型 |
这一节,我们主要讨论下列SAS函数:
1)数据变换函数
2)日期数据处理函数
3)字符数据函数
2.SAS函数的用法
SAS函数可在数据步和一些统计过程的程序语句中使用,在任何SAS的表达式中也可以使用SAS函数。例如在赋值语句中使用MEAN函数:
Avgscore=mean(exam1,exam2,exam3);
1)SAS函数的一般形式:
Function-name(argument-1,argument2)
通常是函数名,后面跟若干参数,参数用括号括起。即使有些函数不需要参数,后面仍需跟一对括号:
function-name()
2) 函数参数
函数的参数可以是:
A.变量,如 mean(x1,x2,x3)
B.常量,如 mean(456,502,612,498)
C.表达式,如 mean(37*2,192/5,mean(22,34,56))
如果一个函数有多个参数,参数之间通常用逗号分隔。但是,对有些函数,变量表或数组变量可用于作为参数,变量表或数组变量要跟在一个关键词OF后面。例如:
mean(ofx1-x3)
mean(of newarray{*})
注意,mean(of x1-x3) 和mean(x1-x3)的区别。
3)目标变量
目标变量是指要将函数结果赋值于的那个变量,如
avgscore=mean(exam1,exam2,exam3);
AVGSCORE就是目标变量。除非事先设定,目标变量的长度被设为函数规定的缺省值。
注意,个别字符函数规定的缺省长度是200,使用这些函数时要注意,必要时,事先设定目标变量的长度,以免目标变量在数据集中占据过多的空间。
3.数据变换
1)字符型变量到数字型的自动转换
当下列情况发生时,字符型变量自动转换为数字型变量:
A.赋值于一个数字型变量,例: rate=payrate;
B.在算术运算中使用,例: salary=payrate*hourse;
C.与一个数字型变量进行比较,例: if payrate>=rate;
D.在要求数字型变量的函数中作为参数,例: newrate=sum(payrate,raise);
上述例中,PAYRATE是一个字符型变量,其它都是数字型变量。
注意,这里的自动转换是指系统产生一个临时的数字型变量来完成赋值或运算,字符型变量PAYRATE本身不会被一个数值型变量的值代替。
当自动转换发生时,系统会在LOG窗口中给出提示,例如:
NOTE: Character values have been converted tonumeric
Valuse atthe places given by: (Line):(column).
6:11
2)数字型变量到字符型的自动转换
与字符型变量自动转换为数字型变量的情况很相似,当下列情况发生时,数字型变量自动转换为字符型变量:
A.赋值于一个字符型变量,例: sitecode=site;
B.在与要求字符的运算符一起使用,例: sitecode=site||dept;
C.在要求字符型变量的函数中作为参数,例: region=trim(site);
上述例中,SITE是一个数字型变量,其它都是字符型变量。
同样,与字符型变量自动转换为数字型变量的情况相似,这里的自动转换是指系统产生一个临时的字符型变量来完成赋值或运算,并且,当自动转换发生时,系统会在LOG窗口中给出提示,例如:
NOTE: Numeric values have been converted tocharacter
Valuse atthe places given by: (Line):(column).
11:13
3)字符型变量到数字型的明确转换
虽然在一些比较简单的情况下,系统能完成字符型变量和数字型之间的自动转换,但是如果需要被转换成数字型变量的字符型变量不能被作为标准的数字读入,或则,需要被转换成的字符型变量有格式要求的话,就必须使用SAS函数来进行明确转换。
用INPUT函数可将字符型变量转换为数字型变量,其一般形式为:
INPUT(source,informat)
其中,source是被转换的字符型变量,informat是读取数字型变量的输入格式。对于字符型变量到数字型的明确转换,必须要设定一个输入格式。
4)数字型变量到字符型的明确转换
用PUT函数可将数字型变量转换为字符型变量,其一般形式为:
PUT(source,format)
其中,source是被转换的数字型变量,format是一个输出格式。
PUT函数和INPUT函数的形式非常相似,所不同的是,INPUT函数要求输入格式,而PUT函数要求输出格式。
4.用函数处理SAS的日期数据
由于在SAS系统中,日期数据是以数字型变量的形式保存,因此,你可以象使用其它数字型变量一样,用日期变量数据来进行各种数值运算。同时,SAS系统还提供了一些专门用于处理日期变量数据的函数。
1)MONTH函数
从一个SAS日期数据中取出月份数,一般形式:
MONTH(date)
MONTH函数返回一个数字值,范围为1到12。
2)YEAR函数
从一个SAS日期数据中取出年份数,一般形式:
YEAR(date)
MONTH函数返回一个数字值表示年份,比如1989。
3)MDY函数
从三个分别代表月、日、年的数字产生一个SAS日期值,一般形式为:
MDY(month,day,year)
其中,month的范围为1到12;day的范围为1到31;year可以用2位或4位数字来表示。
注意,如果在MDY函数中给定一个无效日期,那么,系统赋予目标变量一个遗漏数据。
4)TODAY函数和DATE函数
给出当天的日期值,一般形式为:
TODAY() 或 DATE()
这两个函数的形式和效果都一样,可以互相代替。注意,这两个函数不需要参数。
5.改变字符变量
1)SCAN函数
将一个字符型变量数据分割成词,并返回其中特定的一个词,一般形式为:
SCAN(argument,n,delimiters)
其中,argument是一个字符型变量或表达式,n设定要返回第n个词,delimiters是设定的分隔符。函数根据设定的分隔符将一个字符型变量分割成若干词,并返回其中第n个词。
你可以设定一个或多个分隔符,参数变量中连续的分隔符被视为一个,另外,开头的分隔符不起作用。如果在SCAN函数中不设定分隔符的话,系统使用以下所有分隔符作为缺省值:
空格 . < ( + | & ! $ * ) ; ^ - / , %
例如: lname=scan(name,1,',');
上例是以空格和逗号作为分隔符。
注意,SCAN函数对目标变量赋予200的长度。如有必要的话,可事先用LENGTH语句设定目标变量的长度。例:
datahrd.newtemp(drop=name);
set hrd.oldtemp;
length lnamefname mname $ 10;
lname=scan(name,1);
fname=scan(name,2);
mname=scan(name,3);
run;
2)SUBSTR函数
取出字符串或替换字符值,一般形式为:
SUBSTR(argument,position,n)
其中,argument是一个字符型变量或表达式,position是开始位置,n设定要取出或替换的字符数。
如果SUBSTR函数出现在赋值语句中等号的右边,则它从一个字符型变量中取出字符串,例:
minitial=substr(mname,1,1);
如果SUBSTR函数出现在赋值语句中等号的左边,则它在用一个字符串替代一个字符型变量中的一部分,例:
substr(region,1,3)='NNW';
3)SCAN函数和SUBSTR函数的比较
SCAN函数和SUBSTR函数都能从一个字符型变量中取出一个子字符串,它们的区别在于:
A.SCAN函数取出用分隔符分割的一个词
B.SUBSTR函数取出特定位置和特定长度的子字符串,不需要有分隔符
另外,SUBSTR函数能用于改变字符型变量的值,而SCAN函数不行。
4)TRIM函数
消除字符尾的空格,一般形式为:
TRIM(argument)
参数argument可以是一个字符型变量,如 trim(addr),或是一个其他的字符型函数,如 trim(left(idnum))。TRIM函数在将几个字符型变量合并是比较有用,例如:
datahrd.newtemp(drop=addr city state zip);
set hrd.temp;
newaddr=trim(addr)||','||trim(city)||', '||zip;
run;
注意,TRIM函数不影响变量保存的方式,你可以用TRIM函数消除某个字符型变量尾的空格并将它赋予一个新的变量,但是如果新值的长度小于新变量的长度的话,新值的尾部仍然会填补上足够的空格以适应新变量的长度。
5)INDEX函数
在一个字符型变量中搜索特定的字符串,一般形式为:
INDEX(source,excerpt)
Source是一个字符型变量名,excerpt是要搜索的字符串,如果找到,函数返回字符串的开始位置,如果找不到,返回0。INDEX函数可用于帮助寻找符合特定要求的观测,例如:
datahrd.datapool;
set hrd.temp;
ifindex(job,'word processing') > 0;
run;
注意,INDEX函数是区分大小写的,因此要搜索的字符串必须完全匹配才行。UPCASE函数和LOWCASE函数常常与INDEX函数一起使用以保证大小写不一致时也能找到目标字符串。
6)UPCASE函数和LOWCASE函数
UPCASE函数将字符型变量中所有字符变为大写,而LOWCASE函数将字符型变量中所有字符变为小写,一般形式为:
UPCASE(argument) 或 LOWCASE(argument)
例如: index(upcase(job),'WORD PROCESSING')
或 index(lowcase(job),'wordprocessing')
四.利用DO循环产生数据
1)建立DO循环
上一章我们介绍过DO循环和DO/END语句组,请注意区分。使用DO循环的的一个主要目的是在进行重复计算时可减少程序的语句,另外,也可以用DO循环来产生数据、有条件地执行语句,或读取数据。有四种DO语句可用于建立DO循环:
A.DO
B.DO WHILE
C.DO UNTIL
D.DO OVER
其中,DO OVER仅用于隐含数组,这里我们不讨论。DO和DO WHILE循环在上一章已经介绍过,DO UNTIL与DO WHILE刚好相反,其一般形式为:
DOUNTIL(expression);
More SASstatements;
END;
在循环尾对表达式expression进行估计,如果为真,则循环不再继续。因此,DO UNTIL循环至少会执行一次。
注意,DO UNTIL与DO WHILE的一个重要区别是,DO UNTIL在循环尾对表达式expression进行估计,而DO WHILE在循环头对表达式expression进行估计。
2)利用DO循环进行计算
利用DO循环可以很方便地进行重复的计算,例如:
dataearn;
value=2000;
do year=1 to20;
interest=value*.075;
value+interest; output;
end;
run;
此程序产生的数据集WORK.EARN只有一条观测,其变量VALUE的值为本金2000,每年复利7.5%,20年下来的本利和。如果在循环内加一条OUTPUT语句,则产生的数据集WORK.EARN将包含20条观测,它记录每年的利息INTEREST和本息和VALUE。
3)嵌套的DO循环
DO循环可以嵌套使用,使用时要注意:
A.对不同的DO语句要使用不同的指标变量
B.每个DO循环用一个END语句结束
例如:
dataearn;
value=2000;
do year=1 to20;
do month=1to 12;
interest=value*.075/12;
value+interest;
output;
end;
end;
run;
这样产生的数据集WORK.EARN将包含240条观测,它记录按7.5%的名义年利率,每个月复利一次,每个月的利息INTEREST和本息和VALUE。
五.利用数组来处理变量
1)数组的概念
SAS数组是一些SAS变量的临时组合,一个数组仅仅在DATA步过程中存在。在SAS语言中支持隐含的和明确的两种数组,其中隐含数组是为了与以前版本兼容而支持的,下面我们仅讨论明确定义的数组。
当你需要对多个变量进行同样的计算时,你将发现使用数组会有很大的帮助。例如,你要将一个数据集中记录的每周七天的温度由华氏转换为摄氏,比较下面两个程序:
data report; datareport;
set master.temps; set master.temps;
mon=5*(mon-32)/9; ----> array wkday(7) mon tue wed thr fri satsun;
tue=5*(tue-32)/9; do i=1to 7;
wed=5*(wed-32)/9; wkday(i)=5*(wkday(i)-32)/9;
thr=5*(thr-32)/9; end;
fri=5*(fri-32)/9; run;
sat=5*(sat-32)/9;
sun=5*(sun-32)/9;
run;
显然,右边的程序既简单,又不易出错。
2)定义一个数组
利用ARRAY语句可以明确地定义一个数组,其一般形式为:
ARRAYarray-name{dimension} elements;
其中,array-name是数组名,它必须是一个有效的SAS名字,同时,它不应与同一个DATA步中的变量名相同,另外,还应当避免使用SAS函数名来给数组命名,否则,该函数将不能使用。
dimension设定数组的度数(也可称为维数,为了避免与下面的二维数组的定义混淆,我们把它成为度数),它表明数组包含的变量数目。度数用括号括起,大、中、小括号均可。你可以用一个数字给定一个度数,或则,用一个星号(*)代替,当你用星号时,系统通过计数你给的元素数目来确定数组的度数。
elements是数组中的元素,也就是构成数组的那些变量的名字。你可以将这些变量名一一列出,也可以用下列方法指定一系列变量:
VAR1-VARn 可用数字划定范围的一系列变量
_NUMERIC_ 所有数字型变量
_CHARACTER_ 所有字符型变量
_ALL_ 所有变量
注意,一个数组中的所有元素必须全部都是数字型的,或者,全部都是字符型的。
3)指定数组中的元素
当你定义一个数组时,数组中的每个元素,根据它们出现的顺序都给定一个序号,调用这些元素时,用数组名后跟用括号括起的相应的序号即可。序号必须在数组的度数范围以内。通常,由于数组用于对不同变量的重复操作,往往用循环语句的指标变量来数组中元素的序号。
例: array qua(4) jan apr jul oct;
do i=1 to 4;
yeargoal=qua(i)*1.2;
end;
4)DIM函数
DIM函数可用于获得数组的度数,一般形式为:
DIM(array-name)
它返回数组中元素的个数,可以利用DIM函数来控制循环语句的循环次数,例:
arraywt(6) weight1-weight6;
do i=1 to dim(wt);
wt(i)=wt(i)*2.2046;
end;
这样,即使你改变数组的度数,也不需要修改循环语句了。
5)用ARRAY语句创建变量
你可以在ARRAY语句中创建新的变量,当你在ARRAY语句中创建新的变量时,你可以不用设定数组元素,系统自动在数组名后加一个相应的数字作为新创建的变量名,例如:
arraydiff(5);
遇到这样一个语句时,系统自动创建DIFF1、DIFF2、DIFF3、DIFF4和DIFF5这样五个新变量。当然,如果你不喜欢这样命名的话,也可以自己给定,如:
arraydiff(5) df1 df2 df3 df4 df5;
如果你希望新创建的变量是字符型的,可以用$符号来标明,例:
arrayfname(5) $;
在ARRAY语句中创建的字符型变量的缺省长度为8,你也可以直接设定它的长度,例:
arrayfname(5) $ 24;
6)给数组赋初值
有时候,你可能希望在定义数组的同时,给数组中的元素确定初值,你可以在ARRAY语句中完成这一工作,做法如下:
A.将初值放在元素列表后
B.对每一个数组元素设定一个相应的初值
C.用逗号或空格分隔这些初值
D.用括号括起这些初值
E.如果是字符型变量,把每个初值放在一对单引号中。
例如: array goal{4} g1 g2 g3 g4 (9000 9300 96009900);
array colors{3} color1-color3 ('red','white','blue');
7)创建临时的数组元素
如果你创建的数组仅仅是用于计算过程中,并不想把这些数据保存到SAS数据集中的话,你在定义数组的时候,不需要创建新的变量,只要在ARRAY语句中用_TEMPORARY_来作为数组元素,创建一些临时的数组元素供使用就可以了。例如:
arraygoal{4} _temporary_ (9000 9300 9600 9900);
8)多维数组
为了使用或计算方便,有时候你可能需要用到二维甚至多维的数组。下面我们以二维数组为例介绍其使用方法。
为了定义一个二维数组,在ARRAY语句中,数组名后必须跟两个度数,例如:
array new(3,4) x1-x12;
如果我们把二维数组理解成一个表格的话,第一个度数通常称为行数,第二个度数则称为列数,上例定义的数组可看作一个3x4的表格。数组元素的排列按先第一行,排满再排第二行的次序进行。也就是说:
new(1,1)=x1 new(1,2)=x2 new(1,3)=x3 new(1,4)=x4
new(2,1)=x5 new(2,2)=x6 new(3,3)=x7 new(2,4)=x8
new(3,1)=x9 new(3,2)=x10 new(3,3)=x11 new(3,4)=x12
同样,在使用中为了指定一个二维数组中的元素,要在数组名后跟两个序号,分别对应行号和列号。
多维数组的定义和使用可参照二维数组方法类推。
六. 宏变量
1)宏变量的概念
宏变量可以让你方便地替换程序中的文字。SAS宏变量实际上就是用一个名字代替一个字符串,当在程序中经常出现相同的字符串时,你可以定义一个宏变量,这样不但可以简化程序,需要修改时也非常方便。
SAS宏变量的值是一个字符串,它独立于SAS的DATA步,DATA步结束以后它仍然保持它的值,一直到你改变它。
除了在数据行中之外,你可以在SAS程序的任何地方定义和引用SAS宏变量。因此,如果你在DATA步中用CARDS输入数据,那么在CARDS下面的数据行中不能定义和引用SAS宏变量。其它地方都可以。
SAS宏变量分为两类:
A.自动宏变量
B.用户定义宏变量
顾名思义,自动宏变量由又SAS系统提供,而用户定义宏变量是由用户自行定义的。
2)自动宏变量
一旦你启动SAS系统,自动宏变量就会被建立,它们提供如下信息:
A.SAS开始执行的日期和时间
B.运行的SAS软件的版本号
C.最新建立的SAS数据集的名字
D.运行SAS的操作系统的名称缩写
下面是一些常见的SAS自动宏变量及它们提供的信息
宏变量名 变量提供的信息 例
SYSDATE Date SAS job executed or session began 18MAY99
SYSDAY Weekday SAS job executed or sessionbegan Tuesday
SYSTIME Time SAS job executed or session began 15:32
SYSSCP Operating system abbreviation WIN
SYSVER SAS software release 6.12
SYSLAST Name of most recently created data set HRD.TEMP91
其它的SAS自动宏变量可从HELP窗口中查寻,方法为:
Help à SAS System à SASLanguage à SASMacro Facility àAutomatic Macro Variables
SAS自动宏变量都以SYS开头,因此当用户自行定义宏变量时,建议最好不要以SYS开头,以作为区分。
3)调用宏变量
为调用宏变量,将宏变量名跟在一个&符号后即可,例如:
&SYSDATE
当你需要用到宏变量所提供的信息时,你可以在程序中的任何地方调用宏变量,例如:
footnote"Report Run on &sysday , &sysdate";
遇到宏变量,SAS系统首先把程序中的宏变量用相应的字符串代替,然后再进行编译,因此,上述FOOTNOTE语句被SAS系统理解成:
footnoot"Report Run on Tuesday , 19MAY99";
注意,如果在引号内的字符串中调用宏变量,必须使用双引号,这样,宏变量才会被解释,若使用单引号,宏变量不会被解释。
4)用户定义宏变量
除了直接调用系统提供的自动宏变量外,用户也可自行定义宏变量,一种方法是利用%LET语句,一般形式为:
%LETname=value;
例如: %let region=northwest;
注意,宏变量region的值是northwest,定义时不能用引号将宏变量的值引起来,否则,如果用: %let region='northwest';
宏变量region的值就变成'northwest',而不是northwest。
第二点要注意,宏变量的值是一个字符串,
例如: %let level=768;
这里,宏变量的值768不作为数值对待,而是由3个字符组成的字符串。同样,对
%letrate=700+700*.05;
系统不会去计算等号右边的值,而是直接把它当作由11个字符组成的字符串。
一般来说,%LET语句放在程序的开头,例如:
%letyear=1991;
data hrd.newtemp;
set hrd.temp;
ifyear(enddate)=&year;
proc printdata=hrd.newtemp;
title"temporary Employees for &year";
run;
5)SYMBOLGEN 系统选项
系统选项SYMBOLGEN用于规定当宏变量被解释时是否在LOG窗口中给出提示,它的一般形式为:
SYMBOLGEN 或 NOSYMBOLGEN
其缺省设置是NOSYMBOLGEN。你可以在OPTIONS窗口中或用OPTIONS语句改变它,例如:
optionssymbolgen;
这样,当一个宏变量被解释时,LOG窗口中会给出提示,如:
SYMBOLGEN:Macro variable YEAR resolves to 1991
这样,你可以通过查看LOG窗口,确认你的宏变量是否被正确地解释。
如果系统遇到一个宏变量,但是不能解释时,会在LOG窗口中给出警告,如:
WARNING:Apparent symbolic reference YEAR not resolved.
程序可以继续进行,但通常会发生错误。发生宏变量不能解释的可能原因有:
A.调用宏变量时拼写错误
B.调用宏变量之前没有进行定义
6)将宏变量与前后缀合并使用
在宏变量的使用中,有时候你会碰到复杂一些的情况,就是将宏变量的值与前后缀合并使用。下面我们看几个例子。
例一: %let yr=91;
datahrd.temp&yr;
系统会将DATA语句解释为: data hrd.temp91;
因此,将宏变量的值与前缀合并使用时,按宏变量的一般用法即可。
例二: %let period=end;
ifyear(&period.date)=1991;
系统会将IF语句解释为:if year(enddate)=1991;
例三: %let libref=hrd;
data&libref..temp91;
系统会将DATA语句解释为:data hrd.temp91;
从例二和例三我们看到,将宏变量的值与后缀合并使用时,要在宏变量名的后面加一个点(.),而如果后缀刚好是以点(.)开头的话,宏变量名的后面就会跟着两个点。
7)CALL SYMPUT子程序
用%LET语句可以定义你需要的大多数宏变量,但如果你需要用在DATA步的执行过程中产生的一者变量值来定义一个宏变量的话,就会用到SYMPUT子程序,其一般形式为:
CALLSYMPUT(name,value);
其中,CALL是一个关键词,表示调用一个子程序,SYMPUT是子程序名,name是要定义的宏变量名,它可以是在引号中的一个字符串,也可以是一个数据集的变量,或者数据步中的一个表达式。value是要赋予宏变量的值,它同样可以是一个数据集的变量,或者数据步中的一个表达式。
例如: callsymput('cost',fee);
这时,宏变量COST将被赋予在当前观测中变量FEE的值。
注意,上例中,宏变量名在引号中。如果变量FEE是数字型变量的话,在赋值过程中将自动转换为字符型。
使用SYMPUT子程序使还有两点要注意:
A.不能调用在同一个数据步中由CALLSYMPUT子程序定义的宏变量
B.不能在一个紧跟数据步的全局语句中调用在这个数据步中由CALL SYMPUT子程序定义的宏变量
因为,在这两种情况下,数据步尚未被执行,也就是说,宏变量尚未被定义,系统无法对宏变量进行解释。
七. 将多个SAS数据集合成一个
有时候,需要将两个或多个SAS数据集并成一个,合成的方式有两种:
A.拼接
B.合并
如果把一个SAS数据集看作一个表格的话,每行代表一条观测,每列代表一个变量。那么拼接的意思是将两个或多个数据集的内容纵向合并,行数相加;而合并的意思是将两个或多个数据集的内容横向合并,列数相加。
1)拼接SAS数据集
如果两个数据集具有完全相同的结构,他们的拼接非常简单,直接在DATA步中使用SET语句即可。
例一:两个数据集NA1和NA2:
NA1 |
NA2 |
|||||||
OBS |
NAME |
SEX |
JOBCODE |
OBS |
NAME |
SEX |
JOBCODE |
|
1 |
ZHANG |
M |
NA1 |
1 |
DING |
F |
NA2 |
|
2 |
KONG |
F |
NA1 |
2 |
LIU |
M |
NA2 |
|
3 |
CHEN |
M |
NA1 |
用:data newhire;
set na1 na2;
run;
可得新数据集NEWHIRE:
NEWHIRE |
|||
OBS |
NAME |
SEX |
JOBCODE |
1 |
ZHANG |
M |
NA1 |
2 |
KONG |
F |
NA1 |
4 |
CHEN |
M |
NA1 |
5 |
DING |
F |
NA2 |
6 |
LIU |
M |
NA2 |
如果两个数据集具有不同的变量,如:
例二:
NA1 |
NA2 |
|||||||
OBS |
NAME |
SEX |
JOBCODE |
OBS |
NAME |
SEX |
JCODE |
|
1 |
ZHANG |
M |
NA1 |
1 |
DING |
F |
NA2 |
|
2 |
KONG |
F |
NA1 |
2 |
LIU |
M |
NA2 |
|
3 |
CHEN |
M |
NA1 |
采用与上述完全相同的程序的话,系统会在相应位置加入遗漏值,得到:
NEWHIRE |
||||
OBS |
NAME |
SEX |
JOBCODE |
JCODE |
1 |
ZHANG |
M |
NA1 |
|
2 |
KONG |
F |
NA1 |
|
4 |
CHEN |
M |
NA1 |
|
5 |
DING |
F |
NA2 |
|
6 |
LIU |
M |
NA2 |
2)RENAME=选项
如果两个数据集中同样的变量使用了不同的变量名的话,如上述例二的两个数据集,可使用RENAME=选项进行拼接,使用程序为:
datanewhire;
set na1na2(rename=(jcode=jobcode));
run;
可得到一例一中完全一样的结果。
3)合并SAS数据集
合并SAS数据集要在DATA步中用MERGE语句,其一般形式为:
DATASAS-data-set;
MERGE SAS-DATA-SETS;
BY by-variable;
RUN;
用MERGE语句可以合并多个数据集,每个输入数据集必须事先按by-variable排序。
例三:两个数据集NA1和NA2:
NA1 |
NA2 |
||||||
OBS |
NAME |
SEX |
IDNUM |
OBS |
IDNUM |
SALARY |
|
1 |
ZHANG |
M |
1234 |
1 |
1234 |
2000 |
|
2 |
KONG |
F |
2345 |
2 |
3456 |
5000 |
|
3 |
CHEN |
M |
3456 |
3 |
2345 |
4000 |
用: proc sort data=na1; by idnum;
proc sort data=na2; by idnum;
data combine;
merge na1 na2;
by idnum;
run;
可得:数据集COMBINE:
COMBINE |
|
|||
OBS |
NAME |
SEX |
IDNUM |
SALARY |
1 |
ZHANG |
M |
1234 |
2000 |
2 |
KONG |
F |
2345 |
4000 |
3 |
CHEN |
M |
3456 |
5000 |
如果输入数据集的by-variable不能完全匹配,则输出数据集相应位置用遗漏数据表示,
例四:两个数据集NA1和NA2:
NA1 |
NA2 |
||||||
OBS |
NAME |
SEX |
IDNUM |
OBS |
IDNUM |
SALARY |
|
1 |
ZHANG |
M |
1234 |
1 |
1234 |
2000 |
|
2 |
KONG |
F |
2345 |
2 |
3456 |
5000 |
|
3 |
CHEN |
M |
3456 |
3 |
4567 |
4000 |
采用与上述完全相同的程序,得到:
COMBINE |
||||
OBS |
NAME |
SEX |
IDNUM |
SALARY |
1 |
ZHANG |
M |
1234 |
2000 |
2 |
KONG |
F |
2345 |
. |
3 |
CHEN |
M |
3456 |
5000 |
4 |
4567 |
4000 |
另外,RENAME=选项在MERGE语句中的用法和在SET语句中的用法一样,例如:
mergena1 na2 (rename=(id=idnum));
by idnum;
八.练习