pinseng发了名为《请教一个SAS _n_的问题》的帖子。
他想问以下程序中if _n_=1的意义是什么:
data temp;
input x y@;
datalines;
1 2
3 4
5 6
. 9
6 7
7 .
1 8
6 3
;
run;
data q3;
if _n_=1 then do until (last);
set temp nobs=obs end=last;
sum_x+x;
sum_y+y;
end;
set temp;
if x=. then x=sum_x/obs;
if y=. then y=sum_y/obs;
run;
源自:
https://bbs.pinggu.org/thread-1246757-1-1.html
---------------------------------------------------------------------------------------
问题本身已经解决了,这里只是想总结一下我在参与回复的过程中所探索出的一些东西——“set语句的法则”,它是我尝试过很多小程序后的一个结论。
仔细想想还是有些复杂的:
***************************************************
set语句的法则——当DATA b;数据步中出现多个set a;语句时,遵循如下法则:
(1)计算机对程序中每一处的set a;语句分别设置各自对a集的读取进程,每个set a;语句第一次执行时均从a集的第一条记录开始读入,之后,分别按各自被执行的次数一条一条读取a的记录,注意,DATA步的大循环不会打断这种读取进程的推进,每个特定位置的set语句,仍可在新循环里沿着自己上一次DATA步循环时的a集读取进程继续往下读。
(2)同一DATA b;循环中,每次只针对该循环对应的特定b记录,多次写入set a;语句读出的a记录数据。且对于b的该特定记录行从a处读入的变量,写入的新数据会覆盖旧数据。
(3)DATA步的上一次循环中执行的最后一处set a;为b集读入的a集变量值也将在该次循环开始时被再次写入到这个b集的新的记录行里,形成与上一行b记录重复的格局。即,set a;创建的变量在b集中被当作保留变量。但是要注意,新记录也一如既往的没有禁止覆盖!如果新循环中执行了set,则默认值将被覆盖。若新的DATA循环中没有set语句被执行,且保留变量没被其他语句修改,那么b的新记录行保留默认值。
(4)只要DATA新循环中没有任何set语句被执行,那在完成该DATA循环后,程序自动终止。
(5)无论程序中哪一处的set a;语句在先行读到a的最后一条记录的进度基础上不知“适可而止”,仍被DATA b;程序再次执行,导致“超出极限读取”的,该data b;循环将不产生(理解为抹消也可以)对应的整条b集记录,且data b;数据步终止。
***************************************************
解释:
-------------------------------------------------------------
例1:
OUTPUT:
Obs v1 v2
1 1 10
data b;程序段中出现了两处set a;语句。计算机将分别设置它们各自的读取计划。具体情况如以下流程:
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
( i )b的循环次数(即对应的b记录行数)-------》第1次(针对b的第1条记录):
(a)先看do循环,do之内的set被执行了5次,v2+2也执行了5次:
do第1次循环:v1=1 do第1次循环:v2=2 ------------》 第1行记录:v1=1,v2=2
do第2次循环:v1=2 do第2次循环:v2=4 ------------》 第1行记录被覆盖为:v1=2,v2=4
do第3次循环:v1=3 do第3次循环:v2=6 ------------》 第1行记录被覆盖为:v1=3,v2=6
do第4次循环:v1=4 do第4次循环:v2=8 ------------》 第1行记录被覆盖为:v1=4,v2=8
do第5次循环:v1=5 do第5次循环:v2=10 -------- ---》 第1行记录被覆盖为:v1=5,v2=10
(b)do循环之后,执行下一句——set a; 该处的set还是第一次执行:
初次执行该语句:v1=1 ------------------------------------------》 第1行记录被覆盖为:v1=1,v2=10(v2未遭到覆盖!)
(i i )b的循环次数(即对应的b记录行数)-------》第2次(针对b的第2条记录):
开头时,在b集第2条记录里,默认认为v1=1。这体现了法则(3)的内容。
之后,程序碰到do循环,虽然last值仍保留成数字1,表示do中的set语句已经触及a的底部,但是do语句每次开始第1次循环时,都不对判断条件加以把关。故虽然until (last)被满足,但do还是被执行了。
do中的set a;明明已经读取到了a的底部,但仍然被执行了,造成“超限读取”,依据法则(5),程序到此终止,且抹消掉b的第2条记录。
故DATA b;循环了2次,但b集只产生了1条记录。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
对输出结果的解释到此完毕。
-------------------------------------------------------------
例2:
OUTPUT:
空,无输出内容!表明b集无数据!
LOG:
v2=1 v1=1 _ERROR_=0 _N_=1
v2=2 v1=2 _ERROR_=0 _N_=1
v2=3 v1=3 _ERROR_=0 _N_=1
v2=4 v1=4 _ERROR_=0 _N_=1
v2=5 v1=5 _ERROR_=0 _N_=1
LOG中的输出表明b集的第1次循环确实执行过,但为什么没有形成第1条记录呢?——“超限读取”!法则(5)表明b集不可能有记录。就算前面演算得再精彩,黑板擦一擦,什么都没了。
-------------------------------------------------------------
一个更难的例子:
例3:
OUTPUT:
Obs v2 v1
1 4 1
2 6 2
3 8 3
4 10 4
LOG:
1
1
2
1
2
1
2
1
2
这个例子我不详细说了,但这里我提一些问题,以启发各位的思考:
(A)为什么LOG栏的输出结果中,在第1个数字2出现之后,还会间隔着出现数字1呢?提示:do until (...)开始第一次循环时是不对条件(...)把关的!
(B)为什么记录1中v1=1,而v2却是4?请结合法则中的(2)思考。
(C)为什么记录只有4行,而不是5行?请结合法则中的(2)与(5)思考。
(D)data b;总共循环了几次?请结合法则(5)思考。
这更像是一个练习,且难度较大。
-------------------------------------------------------------
更多的例子:
例4:
OUTPUT:
Obs v1 v2
1 50 6
2 60 6
3 60 .
OUTPUT:
Obs v1 v2
1 50 6
2 60 6
为什么第一段程序的结果与第二段程序的结果不同呢?
两段程序的DATA b;都循环了3次,第一段程序在第3次循环中没有执行任何set语句,故第3行的b记录中v1的值默认与上一行相同。这主要体现了法则(3)、(4)的内容。
第二段程序在第3次循环中又执行了一次set a;语句,a只有2行记录,但该处的set前后共运行了3次,是“超限读取”,因此不形成第3条记录,且程序终止。
-------------------------------------------------------------
例1、例2、例3、例4都弄懂之后,对set语句法则的理解应该就没有问题了。
对set语句法则的解释,到此结束。