不開玩笑,你可能真對 subsetting if 有誤解
quiz
現(xiàn)有四行數(shù)據(jù),如下:

想實現(xiàn):根據(jù)第一列 name 決定是否讀取后兩列內(nèi)容,只讀取 name 為 ruth 的行數(shù)據(jù),如何操作?
厘清思路
如果這四行數(shù)據(jù)是來自于 SAS dataset,自然,我們可以直接使用 where、if。二者差別也很明顯,where 作用于數(shù)據(jù)被讀入 PDV 之前,提前對數(shù)據(jù)集進行篩選,SAS 無需讀入全部輸入數(shù)據(jù)集觀測進入 PDV,從而更為有效地控制讀取效率,if 則對 PDV 中數(shù)據(jù)篩選,無法提前控制。說點題外的,由此引發(fā)了,若 variable 為 data step 后續(xù)創(chuàng)建,where 中無法引用該變量,而 if 可以。
而這里的 quiz 要求,此四行數(shù)據(jù)來自 external file that contains raw data。
在
這篇文章里,我們提到,若存在 input statement,SAS compiler 會創(chuàng)建 input buffer 及 PDV,相信大家都還沒忘記。
明確的一點是:當存在 input 時,where 無法作用于 external raw input data 進行觀測的篩選,但是,if 仍然可以對 PDV 中數(shù)據(jù)篩選。牢記這一點,可以避免一些錯誤。
ok,第一個障礙解決了。既然是這樣,那我這么寫你看對不對?
data want;
? ?infile datalines;
? ?input employee_name $ @;
? ?if employee_name= "ruth" then input age 7-8 idnum 10-11;
? ?datalines;
ruth ?39 11
Jose ?32 22
Sue ? 30 33
John ?40 44
? ?;
run;
proc print; run;
邏輯上似乎沒啥問題,quiz 只要求 ruth 那一行,為什么結(jié)果是這樣?

結(jié)果顯示得很直接了,上面的 if 只控制了 age 和 idnum 的讀取與否,并未實現(xiàn)行觀測的篩選,這一點要特別注意!要想實現(xiàn)根據(jù) employee_name 篩選行觀測,怎么辦,用下面的:
data want;
? ?infile datalines;
? ?input employee_name $ @;
? ?if employee_name= "ruth";
? ?input age 7-8 idnum 10-11;
? ?datalines;
ruth ?39 11
Jose ?32 22
Sue ? 30 33
John ?40 44
? ?;
run;
proc print; run;

大家都看出來了,其實這里的 if 是個 subsetting if statement。我這里要重點強調(diào)下 subsetting if 的邏輯:若 if 后條件為真,繼續(xù)處理該行觀測,否則,停止處理該行觀測且該行觀測不會被寫入輸出數(shù)據(jù)集,并直接返回 data step 開始處進行下一次循環(huán)。
結(jié)合上面 code。若 employee_name 不是 ruth,后續(xù)的一切 statement 都不會被執(zhí)行,且該行觀測不被入選,SAS 直接跳轉(zhuǎn)到下一行數(shù)據(jù)的循環(huán)處理之中;只有 ruth 那行數(shù)據(jù)滿足條件,進行后續(xù)的操作。因而滿足 quiz 的要求。
講到這你以為就結(jié)束了么??
細節(jié)
我們看各種 SAS instructions,總會被提示:subsetting if 相當于 if condition then output,不要告訴我你不是這么理解的。
這種等價是不是真的完全成立呢?如果你認為成立,那么問題來了:按照 output 的規(guī)則,output 之后生成或改變的變量值不會被寫入輸出數(shù)據(jù)集,也就是說,最終輸出數(shù)據(jù)集中,age 和 idnum 根本就不會有值,應該都是缺失值!因為 compile phase 雖然創(chuàng)建了包含 input 中變量的 PDV,但 execution phase 時,第二個 input 是在 subsetting if 之后完成給變量 age 和 idnum 賦值。
但是,你看:
data want;
? ?infile datalines;
? ?input employee_name $ @;
? ?if employee_name= "ruth";
? ?input age 7-8 idnum 10-11;
? ?idnum= 1000;
? ?datalines;
ruth ?39 11
Jose ?32 22
Sue ? 30 33
John ?40 44
? ?;
run;
proc print; run;
不僅和 output 的規(guī)則相背,甚至 idnum 還完成了第二次賦值。

