❝初学SAS的时候,往往注重程序语法结构,而忽略了SAS编译和执行阶段如何处理数据。所以很多时候,写的程序看起来没有问题,但是创建出的数据集观测数和预期的却不一致,一些变量也没有按照预期retain下来,尝试另起一个data步或者用一个新变量名问题就解决了,但是究竟问题出在哪里,也无从得知。今天就一起研究下SAS处理数据的机制吧!❞
SAS data步包含包含两个阶段:
- 编译阶段
- 执行阶段
编译阶段
这个阶段sas主要做:
- 扫描code是否存在语法错误;
- 结束后创建数据集的描述信息;
- 会产生两个对象:input buffer(输入缓冲区)、PDV(program data vector)
具体讲,就是确保program满足语法规则,例如sas数据集、变量名命名规则,关键拼写是否有误,引号括号不配对,每个语句是否以分号结束等等。编译阶段完成之后,sas数据集的描述信息就创建好了,例如变量名、变量属性。
语法错误核查完之后,SAS会创建input buffer(只在读取raw data时产生)。input buffer只是一个逻辑概念,并没有实际的物理储存位置,可以理解为一个临时储存记录的位置。
input buffer创建之后,PDV也随着创建,和input buffer类似,PDV也是一个逻辑概念。
PDV的变量:data步中的所有变量,以及自动生成的变量 by-group创建的变量,选项生成的变量等等。
variables | description |
---|---|
_n_ | Data步执行次数的计数变量 |
_error_ | 错误信息 表示当前观测执行时发生了错误 |
first.variable | 同一by组第一个观测 |
last.variable | 同一by组最后一个观测 |
_all_ | 所有变量 |
indsname= | 数据集名称 |
nobs= | 数据集总观测数 |
curobs= | 数据集当前观测数 |
end= | 布尔值 最后一个数据集最后一条观察为1,否则为0 |
in= | 布尔值 可以标识数据集 |
point= | 取指定观测 等号右边为变量 不能设置为数字 需和stop连用 |
执行阶段
创建数据部分,_N_初始化为1,_ERROR_初始化为0,First.var, Last.var初始化为1,其它的自动变量为缺失。
input语句执行,将raw data第一行copy进input buffer, 执行其他语句,最后输出该条观测到数据集中,然后返回DATA步继续执行,读取下一条观测。
在执行阶段,DATA步看起来更像是一个循环,读取一行数据,执行所有语句,创建一行观测记录,并重复这个操作。每次循环就称作一次迭代。
并非所有的语句都是在执行阶段执行,DATA步中的语句可以分为:声明语句、可执行语句。声明语句:在编译阶段就开始生效了,可以放在DATA步中的任何位置。比如LENGTH、FORMAT、LABEL、DROP、KEEP等。
可执行语句:必须要按照预期执行的顺序进行放置,比如input之前要先infile。
example
data total_points (drop=TeamName);
input TeamName $ ParticipantName $ Event1 Event2 Event3;
TeamTotal + (Event1 + Event2 + Event3);
datalines;
Knights Sue 6 8 8
Kings Jane 9 7 8
Knights John 7 7 7
Knights Lisa 8 9 9
Knights Fran 7 6 6
Knights Walter 9 8 10
;
run;
Input Buffer
PDV
Output语句
读进PDV的数据,默认是会被output到数据集中的,这称为隐式输出。如果使用了output语句(显式输出)来输出记录,隐式输出就不会起作用了。所以,如果使用了有条件的output语句,只有满足的条件的记录才会被输出到数据集中,其它记录不会再被默认输出,如果不同条件的记录要以不同的要求output到数据集,可以使用多个output语句。
Reading raw data v.s. Reading sas dataset
Raw data:每次迭代时,除了自动创建的变量、Retain语句中的变量、SUM语句创建的变量、_TEMPORARY_数组中创建的元素、infile/file选项中创建的变量,其余变量在PDV中都会被设置为缺失。
SAS data set:只在执行的第一次迭代时将PDV中的变量置为缺失,变量将会retain值直到被新的值代替。对于不是来自input dataset而是在data步中创建的新变量,会在执行的每一次迭代的开始被置空。如果想要使新创建的变量retain值,可以使用retain语句。
drop/keep语句和选项
drop/keep分别是删除和保留变量
- drop/keep语句和drop/keep选项作用在输出数据集是一样的;
- drop/keep选项如果作用在输入数据集,那么相应drop的变量或者没被keep的变量就不会进入PDV。当数据列非常多的时候,在输入数据集时就keep或者drop变量,能够提高运行效率;
- drop/keep同时使用,drop先起作用,然后再进行keep;如果drop和keep冲突的话(drop或者keep不存在的变量),那么SAS会报错。
where/if
where在数据进PDV之前就进行筛选,只能筛选输入数据集中的变量,在data步创建的新变量不可以筛选。
- if是在数据读进PDV后才进行筛选,可以筛选输入数据集中的变量,也可以筛选在数据步创建的新变量。
- 如果存在by语句,那么where是在by语句之前执行,if语句是在by语句之后执行。by语句可以产生first.var, last.var的PDV变量,所以where first.var❌,if first.var✔;
where语句的优势就是可以提高效率,因为在读取数据之前已经进行筛选减小了数据量,而if是每行都读入PDV再筛选是否输出到数据集中。
example
数据集Long如图:
运行如下程序:
data rewide(keep=sid programming stats english);
format Sid Programming Stats English;
array course{3} programming stats english;
do i = 1 to 3;
set long;
course{i}=score;
put _all_;
end;
run;
得到的数据集Rewide如图:
这段程序中,set语句被放置在do循环内。set语句每次只读取一行记录,到run结束,返回至开头读取下一条记录,循环往复。
第一条do end循环结束后,PDV会读取三行记录:
遇到Run;之后将当前的PDV记录输出至Rewide数据集中,然后指针+1,继续进行第二个Do end循环,输出S02的一条记录。
当我们对sas读取数据阶段有困惑时,可以使用put all语句,将PDV的所有变量都输出在Log中,查看每次执行时每个变量的赋值情况。