1 创建和重定义变量
创建和重定义变量,是SAS中最受人欢迎的功能之一,你可以用以下基本形式的赋值语句,来创建和重定义变量:
variable = expression ;
variable 是变量名,可以是新变量或已有变量。expression 可以是常量、另一个变量 或 数学表达式。以下是基本类型赋值语句的示例:
表达式类型 | 赋值语句 |
---|---|
数值常量 | qwerty = 10; |
字符常量 | qwerty = 'ten'; |
变量 | qwerty = oldvar; |
加法 | qwerty = oldvar + 10; |
减法 | qwerty = oldvar - 10; |
乘法 | qwerty = oldvar * 10; |
除法 | qwerty = oldvar / 10; |
求幂 | qwerty = oldvar ** 10; |
变量qwerty 是字符还是数值,取决于定义它的表达式。而表达式遵循标准的数学运算优先级顺序:先求幂,然后乘除,最后加减,并可以通过括号来改变运算顺序。
2 使用SAS函数
使用函数时需要注意几点:
- 所有函数都必须带括号,即便没有任何参数
- 参数之间用逗号分隔
- 参数可以是变量名称、数字、带引号的字符常量、表达式
另外,当涉及变量值时,SAS区分你大小写,这时常使用 UPCASE() 函数,将所有字母转换为大写。
2.1 常用SAS字符函数
函数名、语法、定义及示例详见 page89 第三章 3.3节。
2.2 常用SAS数值函数
函数名、语法、定义及示例详见 page90 第三章 3.4节。
3 IF-THEN语句
3.1 使用if-then语句(单分支)
if-then语句,又称为条件逻辑,用法类似于python中分支结构里的单分支(只有一个真区间)。
语法:if condition then action ;
在条件condition为真时,执行then后面的action语句。condition可以是常量、变量或表达式,通过比较运算符、in运算符等进行比较。
比较运算符
符号 | 助记符 | 含义 |
---|---|---|
= | EQ | 等于 |
^=、~= | NE | 不等于 |
> | GT | 大于 |
< | LT | 小于 |
>= | GE | 大于等于 |
<= | LE | 小于等于 |
IN 运算符
in运算符是将变量值与值列表进行比较。
示例:if model in ('model T', 'model A') then make = 'ford';
DO 组合
一个IF-THEN语句只能由一个动作,如果想要执行多个动作,则可以添加关键字DO和END。
do语句将在其后出现直至与之匹配的end语句为止的所有SAS语句视为一个单元。do语句、end语句和它们之间的所有语句一起,被称为do组合。
语法:
if condition then do;
action1;
action2;
……
end;
逻辑运算符
IF-THEN语句中condition条件判断,还能包含逻辑运算符ADN、OR及其组合,来进行多个条件的组合判断。
符号 | 助记符 | 含义 |
---|---|---|
& | AND | 所有表达式必须为真 |
|、! | OR | 至少有一个表达式为真 |
示例:if condition1 and condition2 then action ;
3.2 使用if-then/else语句分组观测(双分支/多分支)
对观测进行分组的常用方法有:
- IF-THEN语句
- SELECT语句
- PROC FORMAT 生成的用户自定义格式的PUT函数
而创建分组变量的最简单和最常见方法,就是使用一系列IF-THEN语句。并且,通过在IF语句中添加关键字ELSE,可以告知这些语句是相关的,用法类似于Python中的双分支和多分支。
IF-THEN/ELSE 语句
基本语法:
if condition then action;
else if condition then action;
else if condition then action;
注意,ELSE语句是添加在IF-THEN语句前面,这样的语句可以是无限多个。ELSE语句保证分组的逻辑是相互排斥的,一旦观测满足某个条件,SAS就会跳过不执行该系列其余语句。
有时,ELSE语句系列中的最后一个ELSE会不同,它只包含一个动作而没有TF或THEN。这种ELSE充当默认值的角色,它将执行所有未能满足任何先前的IF语句的观测。但你只能有一个这样的语句,它必须是 IF-THEN/ELSE 系列中最后一个语句。
语法如下:
if condition then action;
else if condition then action;
else if condition then action;
……
else antion
示例:
if cost = . then costgroup = 'missing';
else if cost < 2000 then costgroup = 'low';
else if cost < 10000 then costgroup = 'medium';
else costgroup = 'high';
4 提取数据中的子集(if语句/delete语句)
IF语句
在SAS中,提取数据集中的某些观测而排除其余观测,最常见的方法是 IF 语句和 WHERE 语句。IF语句的基本语法形式如下:
if expression ;
如:if sex = 'f' ;
可以把 IF 语句看成是 IF-THEN语句的特例。其含义是:若表达式为真,则SAS继续执行data步;若表达式为假,则SAS停止处理该观测的后续语句,即该观测不会添加到正在创建的数据集,然后SAS接着处理下一个观测。
简单地理解,可以把IF语句看成是一个开关:
- if条件为真,开关打开,观测被处理
- if条件为假,开关关闭,观测不被处理
DELETE语句
与IF语句告诉SAS要哪些观测相反,delete语句告诉SAS不要哪些观测。如果sex变量只包含两个变量且灭有缺失值,则下面两条语句等价。
*if语句;
if sex = 'f';
*delete语句;
if sex = 'm' then delete;
总之,当更容易指定条件包含观测时,取子集用 IF 语句;而在更容易指定条件排除观测时,用 DELETE 语句。
5 使用SAS日期
SAS日期是等于 1960.1.1 以来的天数值。关于日期处理,SAS有专门的工具:日期输入函数、输出格式和操作日期的函数。
输入格式
可以使用格式化的输入方式,来读取日期型变量,把日期数据转换为 1960.1.1 以来的天数。
示例:input birthday ANYDTDTE9. ; /* ANYDTDTEw是一种特殊的输入格式,能读取几乎任意格式的日期 */
设置输入的默认世纪
当遇到类似 07/04/76 的两位数年份日期数据时,SAS必须判断年份是在那个世纪,是1976,2076,还是其他?系统选项 YEARCUTOFF= 选项指定SAS使用的百年跨度的第一年,可以使用OPTIONS语句修改此值:
示例:options yearcutoff = 1950 ;
上面语句含义是告知SAS,将两位数的日期解释为发生在1950年开始,往后跨度100年,即1950年 ~ 2049年间。
关于日期的更多输入格式,见page100。
此外,一旦使用SAS日期输入格式来读取变量,它就可以像其他数值变量一样,在算术表达式中随意使用。如
duedate = checkdate + 21 ; 也可以在SAS表达式中使用日期作为常量,如:day = '22apr2014'D ;
5.1 常用SAS日期函数
示例
age = int(yrdif(birthdate, today(), 'age')) ;
更多函数及其用法,参见page100。
5.2 常用SAS日期输出格式
如果打印SAS日期值,SAS将默认打印距离 1960.1.1 的实际天数,但这并不利于观察。所以,SAS提供了多种输出格式用来以不同的格式打印日期。
示例:format birthdate worddate18. ;
更多输出格式及其用法,参见 page100 和 page125。
6 保留迭代初始值和累加(retain语句与求和语句)
当SAS读取原始数据时,在DATA步每次迭代开始时会将所有变量设置为缺失值。这些值可以通过input语句或赋值语句来修改,但当SAS返回到data步的开始并处理下一个观测时,它们会被重新设置为缺失值。可以使用RETAIN语句和求和语句来改变这种方式。
RETAIN语句
ratain语句,能让变量的值从data步的一次迭代保留到下一个迭代。ratain语句能出现在data步的任何位置,常用的有两种语法形式:
RETAIN variable-list ; *variable-list指所要保留的变量;
RETAIN variable-list initial-value ; *initial-value是为变量指定初始值来代替缺失值进行迭代;
求和语句
求和语句不仅保留了上一次迭代的值,还将其值累加到表达式中,适用于将表达式的值累加到变量这种特殊场景。
语法:variable + expression ;
求和语句将表达式的值累加到变量中,同时将变量的 值从data步的一个迭代保留到下一个迭代。需要注意的是,该变量必须是数字,且初始值为0。可以使用RETAIN语句和SUM函数来重写此语句,两者等价。
语法格式:
RETAIN variable 0;
variable = sum(varible, expression);
综合示例如下:
libname zdata "D:datasas_file";
data zdata.gamestats;
infile "D:datasas_file estdata.dat";
input month 1 day 3-4 team $ 6-25 hits 27-28 runs 30-31;
retain maxruns;
maxruns = max(maxruns, runs);
runstodata + runs;
run;
proc print data = zdata.gamestats;
title "games total";
run;
7 利用数组简化程序(array语句)
数组是相似元素的有序集合
若想对很对变量做同样的事情,你可以编写一系列赋值语句或者IF语句,但更简便的方法是使用数组。在SAS中,数组是一组变量,你可以将数组定义为喜欢的任何变量组,前提是它们全是数值 或 全是字符。SAS中使用data步中的ARRAY语句来定义。
语法:array name (n) $ variable-list ;
其中,name为数组名字,n是数组中的变量个数。在(n)的后面是变量名列表。列表中的变量个数必须等于括号中给出的数字(也可以使用 {} 或 [] 来代替括号),这被称为显式数组。如果变量是字符且之前未定义,则需要加 $ 符号。
变量仅在data步运行时存在,不与数据集一起存储。可以给数组取任何名字,命名规则遵循SAS标准命名规则,但不能与数据集中的变量名相同。
为使用方便,可以使用数组名称和下标,来引用变量,类似于python中的切片,不同的是下标是从1开始,且用的是括号(),如下:
array store(4) macys penneys sears target ;
则store(1)指代变量 macys , store(4)指代变量 target 。
data zdata.songs;
infile "D:datasas_filesongs.dat";
input city $ 1-15 age wj kt tr filp ttr;
array song (5) age wj kt tr filp ttr;
do i = 1 to 5; /* DO循环类似于python中的for循环 */
if song(i) = 9 then song(i) = .;
end; /* 结束循环 */
run;
8 使用变量名列表的快捷方式
当编写的变量名称列表中变量数量过多时,推荐使用一种快捷方式 —— 变量缩写列表。
函数中的缩写列表
在函数中,缩写列表之前必须有关键字OF,如sum(of cat 8 - cat12)
数字范围列表
以相同字符开始,并以连续数字结尾的变量,可以时数字范围列表的一部分。只要数字时顺序相连的,就能以任何数字开始和结束。
变量列表 | 缩写列表 |
---|---|
input cat8 cat9 cat10 cat11 cat12; | input cat8 - cat12; |
名称范围列表
名称范围列表取决于SAS数据集中变量的内部顺序或为止,即data步中变量的出现顺序。如下:
data example;
input y a c h r;
b = c + r;
run;
要指定名称范围列表,先是第一个变量,接着时连个连字符(--),然后是最后一个变量。
变量列表 | 缩写列表 |
---|---|
put y a c h r b ; | put y -- b ; |
如果不确定内部顺序,可以使用proc contents中的 POSITION 选项来查找,作用是列出数据集中的变量,并按位置排序。
libaname mydir "D:datasas_file";
proc contents data = mydir.diatance position;
run;
名称前缀列表
以相同字符开头的变量可以是名称前缀列表的一部分,可以在某些SAS语句和函数中使用。例如:
变量列表 | 缩写列表 |
---|---|
dogbills = sum(dogvet, dogfood, dog_care); | dogbills = sum(of dog:); |
特殊SAS名称列表
有三个可用于任何位置的特殊名称列表:
- _ALL_:表示数据集中所有变量
- _CHARACTER_:表示数据集所有字符变量
- _NUMERIC_:表示数据集中所有数值变量
这些名称列表在要计算某观测的所有数值变量的均值时(MEAN(OF _NUMERIC_)),或列出某观测所有变量的值时(PUT _ALL_)非常有用。
data zdata.songs;
infile "D:datasas_filesongs.dat";
input city $ 1-15 age wj kt tr filp ttr;
array new (5) song1-song5; /* 数字变量列表 */
array old (5) wj--ttr; /* 名称范围列表 */
do i = 1 to 5;
if old(i) = 9 then new(i) = .;
else new(i) = old(i);
end;
avgscore = mean(of song1-song5); /* 缩写变量列表 */
run;