SAS中BY _ALL_语句机制及计数差异问题咨询
示例代码
DATA ESPERIMENTO; INPUT NOME $ ID ETA; DATALINES; FABRIZIO 10 12 FABRIZIO 10 12 FABRIZIO 10 12 FABRIZIO 10 11 FABRIZIO 11 12 FABRIZIO 11 12 FABRIZIO 10 11 MATTIA 11 11 MATTIA 11 11 MATTIA 11 11 GIULIA 12 12 GIULIA 11 12 GIULIA 12 11 GIULIA 12 12 ; RUN; PROC SORT DATA=WORK.ESPERIMENTO OUT=ESPERIMENTO; BY _ALL_; RUN; DATA CONTEGGIO; SET ESPERIMENTO; BY _ALL_; IF FIRST.NOME THEN COUNT_NOME=0; COUNT_NOME + 1; IF FIRST.ID THEN COUNT_ID=0; COUNT_ID + 1; IF FIRST.ETA THEN COUNT_ETA=0; COUNT_ETA + 1; RUN; PROC PRINT DATA=CONTEGGIO; TITLE "PRINT_OF_CONTEGGIO"; RUN;
疑问解答
1. 为何仅当FIRST.命令作用于表中最右侧变量时,SAS才识别完全重复的行/观测值?
BY _ALL_的排序逻辑是按所有变量从左到右的顺序依次比较,最终生成的最小分组是「所有变量值完全相同的观测集合」。而FIRST.var的触发规则是:在BY分组的层级结构里,当观测进入一个以var为最后一层的新分组时,FIRST.var为1。
当var是最右侧变量时,它的分组层级是最细的——只有所有左侧变量值都不变、且当前var值变化,或者左侧变量值变化导致进入新大组时,才会触发FIRST.var,而这恰好对应「完全重复行的新分组」。如果var是左侧变量,比如NOME,它的分组层级是最粗的,只要NOME值变化就触发,和后面的ID、ETA无关,自然无法识别完全重复行。
2. 若SAS按BY语句中列出的变量处理数据,为何无法编写类似FIRST._ALL_的语句?
FIRST./LAST.是SAS为单个变量生成的自动标记变量,语法上只能跟具体的变量名,不支持_ALL_这类批量变量简写。
本质上,FIRST.var的作用是标记「当前观测是否是其所在分组中var值的首次出现」,而「所有变量首次出现」这个逻辑并不存在——因为BY分组本身就是所有变量的组合,要判断是否进入新的完全重复组,直接用最右侧变量的FIRST.var即可,SAS没有必要提供FIRST._ALL_这种语法。
3. 为何SAS似乎仅记住FIRST.语句中指定变量左侧的排序变量,而忽略右侧的?
这是BY分组的层级优先级决定的:BY变量的顺序是从左到右优先级递减,分组时先按左侧变量划分大组,再在大组内按右侧变量分小组。
FIRST.var的触发只和左侧变量是否不变有关:只要左侧变量的值和上一个观测相同,哪怕右侧变量变化,都不会触发FIRST.var;只有当左侧变量不变、且当前var值变化,或者左侧变量变化时,才会触发FIRST.var。右侧变量的变化不会影响FIRST.var的逻辑,所以看起来像是被忽略了。
4. 若FIRST.语句仅考虑指定变量,为何作用于最右侧变量时,SAS会整体比较行/观测值?
当var是BY列表的最后一个变量时,它的FIRST.var触发时机恰好对应「整个BY组合的变化」:
- 要么是左侧所有变量值都不变,仅当前var值变化,导致完全重复行的分组变化;
- 要么是左侧变量值变化,进入新的大组/小组,此时var在新分组内是首次出现。
而BY _ALL_的BY组合就是「所有变量值完全相同的行」,所以FIRST.最右侧变量触发时,必然是进入了新的完全重复行分组,看起来像是SAS在整体比较行,但本质是BY层级排序的自然结果,并非FIRST.var直接比较了所有变量。
5. 简言之:能否解释COUNT_NOME、COUNT_ID和COUNT_ETA三者为何存在差异?
三个计数器的差异完全源于对应的FIRST.var触发时机不同:
COUNT_NOME:每次NOME值变化(进入新的NOME大组)时重置,累加的是同一个NOME下的观测总数,比如FABRIZIO的所有观测会被归为一组,计数器从1一直累加到该NOME的总观测数。COUNT_ID:每次同一个NOME下的ID值变化(进入新的NOME+ID小组)时重置,累加的是同一个NOME+ID组合下的观测数,比如FABRIZIO 10的所有观测会被归为一组。COUNT_ETA:每次同一个NOME+ID下的ETA值变化(进入新的完全重复行组)时重置,累加的是完全相同行的重复次数,比如FABRIZIO 10 11的2个观测,计数器会从1累加到2,进入下一个完全重复组时重置为1。
内容的提问来源于stack exchange,提问作者Fabrizio Ferrari

