官术网_书友最值得收藏!

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;
主站蜘蛛池模板: 扎囊县| 班戈县| 堆龙德庆县| 阿拉善盟| 松桃| 石门县| 盐山县| 曲阜市| 阿勒泰市| 峡江县| 大关县| 五峰| 绥中县| 芦山县| 武冈市| 道真| 乐亭县| 陵水| 抚宁县| 浪卡子县| 泽库县| 芒康县| 色达县| 孝昌县| 通州市| 磐石市| 郑州市| 浦江县| 永嘉县| 大同县| 怀柔区| 当雄县| 阜阳市| 遵义市| 大连市| 大港区| 镇平县| 大兴区| 新邵县| 香格里拉县| 宜都市|