2.5 理解SAS運(yùn)行機(jī)制
SAS的學(xué)習(xí)曲線比較陡峭,其原因之一就是很多SAS學(xué)習(xí)者沒有深入理解SAS的運(yùn)行機(jī)制,其中最為重要的機(jī)制就是PDV(Program Data Vector)與DATA步自循環(huán)。
→2.5.1 PDV與DATA步自循環(huán)
很多時候,即使是寫了很多SAS程序、用了很長時間SAS的人,也總是會對SAS DATA步運(yùn)行出的結(jié)果感到莫名其妙,對發(fā)生的錯誤更是一頭霧水,但是如果能夠靜下心來,了解PDV、厘清SAS的運(yùn)行機(jī)制,很多疑惑或許就迎刃而解了。
SAS系統(tǒng)處理SAS DATA步時,分兩步:編譯和執(zhí)行。經(jīng)典的DATA步,基本按照圖2-14的流程來。

圖2-14 DATA步動作流程圖
具體而言,在編譯和執(zhí)行階段,SAS會分別進(jìn)行如表2-6所示的操作。
表2-6 編譯和執(zhí)行階段具體動作

在上面的過程中,有兩個概念不是很好理解:一是輸入緩沖區(qū)(Input Buffer);二是程序數(shù)據(jù)向量(Program Data Vector)。這兩個概念都是內(nèi)存里的一個邏輯區(qū)域,我們簡要示圖如圖2-15所示。

圖2-15 Input Buffery與PDV
Buffers是系統(tǒng)內(nèi)存的緩沖區(qū),我們可以先不細(xì)究。如圖2-15所示,分別展示了讀入原始數(shù)據(jù)和讀入SAS數(shù)據(jù)集時的流程。
(1)讀入原始數(shù)據(jù)時:原始數(shù)據(jù)先讀入Input Buffer,再從Input Buffer轉(zhuǎn)換到PDV,最后從PDV輸出到SAS數(shù)據(jù)集。
(2)讀SAS數(shù)據(jù)時:把數(shù)據(jù)集觀測直接讀入到PDV,再從PDV輸出到數(shù)據(jù)集。
我們再次以一個小程序為例,看看Input Buffer與PDV,了解SAS DATA步的運(yùn)行機(jī)制。
程序2-21 PDV演示程序
datademoPDV; input ID $ Chinese Math English; Sum=Chinese+Math+English; datalines; S001 80 99 93 S002 90 85 95 S003 83 88 81 ; run;
在編譯階段,SAS就知道這個要建立的數(shù)據(jù)集叫DemoPDV,有ID、Chinese、Math、English以及Sum五個變量,其中ID為字符型。SAS給它們建立好Input Buffer和PDV。
Input Buffer:內(nèi)存里開辟空間,以便中轉(zhuǎn)數(shù)據(jù)。

PDV:從Input語句或者SET、MERGE、UPDATE語句獲取變量信息,建立好數(shù)據(jù)變量。

運(yùn)行階段:
(1)設(shè)置INPUT中的變量為缺失(字符變量為空白,數(shù)字變量為小數(shù)點(diǎn)),并設(shè)置自動變量_N_=1,_ERROR_=0;


(2)INPUT語句讀入第一條記錄,Input Buffer和PDV的狀態(tài)。方框可以理解為在運(yùn)行的程序部分;
開始INPUT語句:
datademoPDV; input ID $ Chinese Math English; Sum=Chinese+Math+English; datalines; S001 80 99 93 S002 90 85 95 S003 83 88 81 ; run;

讀入第一個變量ID。
datademoPDV; input ID $ Chinese Math English; Sum=Chinese+Math+English; datalines; S001 80 99 93 S002 90 85 95 S003 83 88 81 ; run;

讀入第二個變量。

datademoPDV; input ID $ Chinese Math English; Sum=Chinese+Math+English; datalines; S001 80 99 93 S002 90 85 95 S003 83 88 81 ; run;
如此直到最后一個變量sum。
datademoPDV; input ID $ Chinese Math English; Sum=Chinese+Math+English; datalines; S001 80 99 93 S002 90 85 95 S003 83 88 81 ; run;

(3)完成所有DATA步后續(xù)語句,SAS自動完成輸出數(shù)據(jù)集。
datademoPDV; input ID $ Chinese Math English; Sum=Chinese+Math+English; datalines; S001 80 99 93 S002 90 85 95 S003 83 88 81 ; run;

將上面PDV里除了自動變量_ERROR_,_N_外,其他變量自動輸出到數(shù)據(jù)集DemoDPV。
(4)返回DATA步第一語句,初始化PDV。
datademoPDV; input ID $ Chinese Math English; Sum=Chinese+Math+English; datalines; S001 80 99 93 S002 90 85 95 S003 83 88 81 ; run;

(5)開始讀入第二條記錄的第一個變量ID。

(6)如此循環(huán)重復(fù),讀完最后一條記錄的最后一個變量,寫入數(shù)據(jù)集。

(7)再次返回第一條DATA語句,發(fā)現(xiàn)已經(jīng)沒有數(shù)據(jù)可以讀取,直到這時,DATA步才徹底結(jié)束。

如何粗略的驗證上述步驟呢?我們可以嘗試運(yùn)行程序2-22驗證PDV,看LOG窗口給我們的信息提示。
程序2-22 驗證PDV
datademoPDV; put "第" _n_ "次運(yùn)行前:" _all_; input ID $ Chinese Math English; Sum=Chinese+Math+English; put "第" _n_ "次運(yùn)行后:" _all_; datalines; S001 80 99 93 S002 90 85 95 S003 83 88 81 ; run;
LOG的結(jié)果顯示:
第1 次運(yùn)行前:ID= Chinese=. Math=. English=. Sum=. _ERROR_=0 _N_=1 第1 次運(yùn)行后:ID=S001 Chinese=80 Math=99 English=93 Sum=272 _ERROR_=0 _N_=1 第2 次運(yùn)行前:ID= Chinese=. Math=. English=. Sum=. _ERROR_=0 _N_=2 第2 次運(yùn)行后:ID=S002 Chinese=90 Math=85 English=95 Sum=270 _ERROR_=0 _N_=2 第3 次運(yùn)行前:ID= Chinese=. Math=. English=. Sum=. _ERROR_=0 _N_=3 第3 次運(yùn)行后:ID=S003 Chinese=83 Math=88 English=81 Sum=252 _ERROR_=0 _N_=3 第4 次運(yùn)行前:ID= Chinese=. Math=. English=. Sum=. _ERROR_=0 _N_=4
最后補(bǔ)充說明一下:上面所展示的都是SAS默認(rèn)的、最基礎(chǔ)的、最簡單的運(yùn)行機(jī)制。當(dāng)DATA步有循環(huán)、選擇語句,有OUTPUT、RETAIN等語句時,SAS的處理流程會有所不同。
→2.5.2 @與@@的困惑
初學(xué)SAS者,或多或少都會對@與@@的理解有些吃力。官方對@的說法是:INPUT語句尾部的@是行保持符,主要作用是保持?jǐn)?shù)據(jù)行停留在此行,不要跳到下一行。
@稱為單尾@,@@稱為雙尾@,很多情況下,我們連一個@也不用,我姑且稱之為無尾。那么什么情況下用無尾、什么情況下用單尾、什么情況下用雙尾呢?以下是筆者總結(jié)的一些原則:
● 當(dāng)DATALINES數(shù)據(jù)行里要讀入的數(shù)據(jù)列數(shù)=要讀入的變量數(shù),也就是說一行就是一條觀測時,無尾。
● 當(dāng)DATALINES數(shù)據(jù)行里要讀入的數(shù)據(jù)列數(shù)>要讀入的變量數(shù),而且是整數(shù)倍時,也就是說一行= K*數(shù)條觀測(K為≥1的整數(shù)),用@@。
● 當(dāng)一個DATA步里有多個INPUT語句時,我們需要單尾@。
程序2-23 @與@@示例程序
*=== 數(shù)據(jù)列數(shù)=變量數(shù); datatest1; input id x y z; datalines; 1 98 99 97 2 93 91 92 ; run; *=== 數(shù)據(jù)列數(shù)=變量數(shù),多個input語句; datatest2; input id@; input x@; input y@; input z@; datalines; 1 98 99 97 2 93 91 92 ; run; *=== 數(shù)據(jù)列數(shù)=k*變量數(shù); datatest3; input id x y z @@; datalines; 1 98 99 97 2 93 91 92 ; run;
關(guān)于@、@@與跳行,筆者曾簡單總結(jié)了如下原則:
● 無尾Hold不住立即跳。
● 一尾(@)Hold當(dāng)前INPUT語句不跳,但若剛好是DATA步最后一個INPUT語句,跳。
● 二尾(@@)打死都不跳。
● 最后,無論多少尾,數(shù)據(jù)行末尾必定自動跳。
例如,實(shí)例程序2-24 @與@@的辨析中第一個程序,由于INPUT X后面有@,且不是最后一個INPUT語句,故讀完X=1后,不跳行,繼續(xù)讀Y=2, 由于INPUT Y后無尾,立即跳行,故讀Z時為Z=4,又因INPUT Z后有@@,雖然這是最后一個DATA步的IPUT語句,不跳,程序返回開頭,開始讀第二條觀測,X=5,不跳,Y=6,跳,Z=7。故最終的結(jié)果為兩條觀測,X,Y,Z的值分別為:1,2,4;5,6,7。第二個程序,答案是1,4,5,各位讀者能運(yùn)用上面的原則得出答案嗎?
程序2-24 @與@@的辨析
datatest; input x @; /*單個@,能Hold住,讀后不跳*/ input y; /*沒有@,Hold不住,讀后跳*/ input z @@; /*兩個@,Hold住沒問題,但讀入y=6后,到達(dá)數(shù)據(jù)行末尾,因此自動跳*/ datalines; 1 2 3 4 5 6 7 ; run; data test; input x ; /*無@,Hold不住,讀后立即跳*/ input y @@; /*兩個@,Hold住,讀后不跳*/ input z @; /*單個@,但是是最后一個INPUT語句,跳*/ datalines; 1 2 3 4 5 6 7; run;
- FuelPHP Application Development Blueprints
- Beginning Java Data Structures and Algorithms
- Vue.js快速入門與深入實(shí)戰(zhàn)
- Raspberry Pi for Secret Agents(Third Edition)
- Java FX應(yīng)用開發(fā)教程
- SQL for Data Analytics
- Data Analysis with Stata
- Learning Data Mining with R
- Oracle Exadata專家手冊
- 零基礎(chǔ)學(xué)Python編程(少兒趣味版)
- Yii2 By Example
- C語言程序設(shè)計教程
- Backbone.js Patterns and Best Practices
- 數(shù)據(jù)結(jié)構(gòu)與算法詳解
- 軟件自動化測試實(shí)戰(zhàn)解析:基于Python3編程語言