SAS編程實(shí)踐---宏“Mkfrm”:創(chuàng)建某一個(gè)域的空數(shù)據(jù)集

寫在前面。

今天是個(gè)小工具宏,大佬們見笑了。如果您看完有所指正,那我會很感謝您。如果您看完有所收獲,那是我的榮幸。

在做SDTM或者ADaM數(shù)據(jù)集時(shí),我和同事一般的習(xí)慣是:

寫某一個(gè)域的數(shù)據(jù)集時(shí),比如dm、adsl,...,先根據(jù)spec文件中的說明,創(chuàng)建一個(gè)包含這個(gè)域中所有變量的空數(shù)據(jù)集,再去根據(jù)spec說明創(chuàng)建包含所需變量的數(shù)據(jù)集,最后再把做好的數(shù)據(jù)集和這個(gè)空的數(shù)據(jù)集set一下。

這樣最終的數(shù)據(jù)集中變量的順序、名稱、標(biāo)簽、類型和長度等就與spec中說明一致了。

很多不同的方法實(shí)現(xiàn),個(gè)人感覺使用proc sql還是很方便的。

但是還是要敲不少代碼,如果spec寫的足夠規(guī)范,質(zhì)量很高,直接讀入來創(chuàng)建這個(gè)空的數(shù)據(jù)集(框架)是個(gè)不錯(cuò)的主意。

注意,前提是spec寫的足夠好,spec寫的足夠好,spec寫的足夠好。


準(zhǔn)備步驟

目標(biāo)拆解

一樣的,先進(jìn)行一下目標(biāo)的拆解:

  • spec中可能包含的部分信息
Variable Label Type Length or Display Format
STUDYID 研究編號 C 200
USUBJID 受試者唯一編號 C 200
TRTSEQP 給藥順序 C 200
TRTSEQPN 給藥順序(數(shù)值) N 8
FASFL 是否進(jìn)FAS C 200

注意,我們公司是用C和N標(biāo)識變量類型,這和后面一個(gè)條件判斷有關(guān);

如果你們不是使用N/C,那后面的代碼需要進(jìn)行適當(dāng)修改。

  • 目標(biāo)
STUDYID USUBJID TRTSEQP TRTSEQPN FASFL

宏編寫步驟

注釋

宏的作用,注意事項(xiàng),參數(shù)的說明在注釋中已經(jīng)寫清楚了,如下:


/*******************************************************************************************
Purpose: 從導(dǎo)入SAS的spec數(shù)據(jù)集中,或者從外部spec文件中,獲取一個(gè)域?qū)?yīng)數(shù)據(jù)集中變量的名稱、標(biāo)簽、類型和長度的4個(gè)變量,產(chǎn)生這個(gè)域的空數(shù)據(jù)集;

dtin:如果已經(jīng)將spec文件讀入SAS會話中產(chǎn)生數(shù)據(jù)集,則將dtin參數(shù)賦值為該數(shù)據(jù)集名稱;

      注意,該數(shù)據(jù)集的前4個(gè)變量必須依次為:名稱、標(biāo)簽、類型和長度;

      如果是要從外部spec文件創(chuàng)建的情形,則不要給dtin參數(shù)賦值;

sepcfile:如果沒有將spec文件讀入SAS會話中,則需要使用filename語句產(chǎn)生spec文件路徑的引用,
          例如,filename name ".../spec.xlsx";,則將name賦值給參數(shù)sepcfile;

dbms:適用于從外部spec文件創(chuàng)建的情形,賦值給import過程步的DBMS參數(shù);

sheet:適用于從外部spec文件創(chuàng)建的情形,賦值給import過程步的SHEET參數(shù),SHEET名稱應(yīng)該在給定的EXCEL中;

startrow:適用于從外部spec文件創(chuàng)建的情形,賦值給import過程步的datarow參數(shù),開始讀取數(shù)據(jù)的行;
          注意,默認(rèn)不讀取變量名,因此,直接賦值給需要讀取的數(shù)據(jù)的開始行;

frm:一般也就是域名,用于這個(gè)域的空數(shù)據(jù)集名稱的構(gòu)建;

**************************************************************************************************/

sepc信息來源的考慮

我考慮了兩種情況:

  1. 你把spec所在的路徑給出了,并且使用filename語句引用給了一個(gè)標(biāo)識符。好,那這個(gè)宏就按照標(biāo)識符引用的路徑去讀你的spec,然后進(jìn)行后續(xù)的步驟;

  2. 你已經(jīng)把spec文件讀進(jìn)SAS會話創(chuàng)建了數(shù)據(jù)集。好的,那這個(gè)宏就使用這個(gè)數(shù)據(jù)集進(jìn)行后續(xù)步驟。

所以數(shù)據(jù)的預(yù)處理使用了條件判斷:

從SAS數(shù)據(jù)集中讀取數(shù)據(jù)

%if ( %sysfunc(exist(&dtin. ,data )) and &dtin. ^= %str() )   %then %do;
%put WARNING: 將從SAS數(shù)據(jù)集中讀取數(shù)據(jù);
data _specdtin;
set &dtin. ;
run;

proc contents data= _specdtin out= _info noprint;
proc sort ; by varnum;
run;

proc sql   noprint;
    select   name into:col1 -:col4 from _info where varnum <= 4;
quit;

proc sql   noprint;
    select count(distinct &col1. ) ,   &col1. ,  &col2. ,  &col3.,  &col4.  into: varn, :vnam1 -:vnam999,   :vlabel1 -:vlabel999,  :vtype1 -:vtype999,  :len1 -:len999 from _specdtin;
quit;

%end;

注意為真的條件,輸入數(shù)據(jù)集參數(shù)dtin你進(jìn)行了賦值,并且這個(gè)數(shù)據(jù)集名稱你不是亂寫的,是有數(shù)據(jù)的,這個(gè)宏才能夠從中讀取數(shù)據(jù)并且進(jìn)行一系列宏變量賦值操作。

這步驟中宏變量賦值的思路我講一下:

  1. 使用proc contents獲取給的spec數(shù)據(jù)集變量的信息,主要是前4個(gè)變量的名稱,必須依次為名稱、標(biāo)簽、類型和長度,然后賦值給了4個(gè)宏變量;

  2. 然后使用proc sql將給的spec數(shù)據(jù)集中的這四個(gè)宏變量指定的變量的所有觀測分別賦值給4組宏變量。

從外部spec文件中讀取數(shù)據(jù)

%else %do;
%put WARNING: 將從外部spec文件中讀取數(shù)據(jù);

proc import datafile= &sepcfile.  out=_specdtin  
    dbms=&dbms.  replace;
    SHEET =%sysfunc( upcase( &sheet.) );
    datarow=2;
    getnames=no;
run;

proc sql   noprint;
    select count(distinct a) ,  a , b , c, d  into: varn, :vnam1 -:vnam999,   :vlabel1 -:vlabel999,  :vtype1 -:vtype999,  :len1 -:len999 from _specdtin;
quit;
%end;

注意,不論是從已存在的數(shù)據(jù)集還是外部文件,數(shù)據(jù)集中必須包含名稱、標(biāo)簽、類型和長度這4個(gè)個(gè)變量的信息,并且順序也是固定。

上述代碼主要涉及的就是一些宏變量的賦值,我有說明的不清楚的地方的話,可以查看我這篇文章:

SAS編程實(shí)踐 宏變量賦值(一文盡力涵蓋)

proc sql創(chuàng)建空數(shù)據(jù)集

最后就是這個(gè)宏核心功能步,其實(shí)就是在proc sql中使用了循環(huán)和條件判斷。

    proc sql noprint;
        create table %sysfunc( upcase( &frm._frm) )  (
            %do aa = 1 %to  &varn.;
                %if  %sysfunc(upcase( &&vtype&aa. )) =  %str(C) %then

            %do;
                %let vtyp&aa. = Char(&&len&aa.);
            %end;

        %if  %sysfunc(upcase( &&vtype&aa. )) =  %str(N) %then
            %do;
                %let vtyp&aa. = Num(&&len&aa.);
            %end;

        %if &aa. ^=  &varn. %then
            %do;
                &&vnam&aa.  &&vtyp&aa. "&&vlabel&aa.",
            %end;

        %if &aa. =  &varn. %then
            %do;
                &&vnam&aa.  &&vtyp&aa. "&&vlabel&aa."
            %end;
%end;
        );
        quit;

注意,前一個(gè)條件判斷,是為了把數(shù)據(jù)類型N或者C轉(zhuǎn)換為sql中的NumChar;如果你們不是使用N/C標(biāo)識變量類型,那么這個(gè)判斷需要根據(jù)實(shí)際情況修改。

另一個(gè)條件判斷,

是因?yàn)槭褂?code>proc sql創(chuàng)建空數(shù)據(jù)集時(shí)的語句中,最后一句后面沒有逗號,而前面都有,沒有這個(gè)判斷,會報(bào)錯(cuò)的。

這個(gè)宏沒有核心步驟后的處理和輸出步,不過還是清除了一下中間臨時(shí)數(shù)據(jù)集。

proc datasets lib=work noprint;
delete _:;
run;

總結(jié)

以上就是我寫這個(gè)宏的最主要代碼,完整代碼如下:

%macro Mkfrm(
dtin=,
sepcfile=,
dbms=,
sheet=,
startrow=,
frm=
);

/*******************************************************************************************
Purpose: 從導(dǎo)入SAS的spec數(shù)據(jù)集中,或者從外部spec文件中,獲取一個(gè)域?qū)?yīng)數(shù)據(jù)集中變量的名稱、標(biāo)簽、類型和長度的4個(gè)變量,產(chǎn)生這個(gè)域的空數(shù)據(jù)集;

dtin:如果已經(jīng)將spec文件讀入SAS會話中產(chǎn)生數(shù)據(jù)集,則將dtin參數(shù)賦值為該數(shù)據(jù)集名稱;

      注意,該數(shù)據(jù)集的前4個(gè)變量必須依次為:名稱、標(biāo)簽、類型和長度;

      如果是要從外部spec文件創(chuàng)建的情形,則不要給dtin參數(shù)賦值;

sepcfile:如果沒有將spec文件讀入SAS會話中,則需要使用filename語句產(chǎn)生spec文件路徑的引用,
          例如,filename name ".../spec.xlsx";,則將name賦值給參數(shù)sepcfile;

dbms:適用于從外部spec文件創(chuàng)建的情形,賦值給import過程步的DBMS參數(shù);

sheet:適用于從外部spec文件創(chuàng)建的情形,賦值給import過程步的SHEET參數(shù),SHEET名稱應(yīng)該在給定的EXCEL中;

startrow:適用于從外部spec文件創(chuàng)建的情形,賦值給import過程步的datarow參數(shù),開始讀取數(shù)據(jù)的行;
          注意,默認(rèn)不讀取變量名,因此,直接賦值給需要讀取的數(shù)據(jù)的開始行;

frm:一般也就是域名,用于這個(gè)域的空數(shù)據(jù)集名稱的構(gòu)建;

**************************************************************************************************/
%if ( %sysfunc(exist(&dtin. ,data )) and &dtin. ^= %str() )   %then %do;
%put WARNING: 將從SAS數(shù)據(jù)集中讀取數(shù)據(jù);
data _specdtin;
set &dtin. ;
run;

proc contents data= _specdtin out= _info noprint;
proc sort ; by varnum;
run;

proc sql   noprint;
    select   name into:col1 -:col4 from _info where varnum <= 4;
quit;

proc sql   noprint;
    select count(distinct &col1. ) ,   &col1. ,  &col2. ,  &col3.,  &col4.  into: varn, :vnam1 -:vnam999,   :vlabel1 -:vlabel999,  :vtype1 -:vtype999,  :len1 -:len999 from _specdtin;
quit;

%end;
%else %do;
%put WARNING: 將從外部spec文件中讀取數(shù)據(jù);

proc import datafile= &sepcfile.  out=_specdtin  
    dbms=&dbms.  replace;
    SHEET =%sysfunc( upcase( &sheet.) );
    datarow=2;
    getnames=no;
run;

proc sql   noprint;
    select count(distinct a) ,  a , b , c, d  into: varn, :vnam1 -:vnam999,   :vlabel1 -:vlabel999,  :vtype1 -:vtype999,  :len1 -:len999 from _specdtin;
quit;

%end;

    proc sql noprint;
        create table %sysfunc( upcase( &frm._frm) )  (
            %do aa = 1 %to  &varn.;
                %if  %sysfunc(upcase( &&vtype&aa. )) =  %str(C) %then

            %do;
                %let vtyp&aa. = Char(&&len&aa.);
            %end;

        %if  %sysfunc(upcase( &&vtype&aa. )) =  %str(N) %then
            %do;
                %let vtyp&aa. = Num(&&len&aa.);
            %end;

        %if &aa. ^=  &varn. %then
            %do;
                &&vnam&aa.  &&vtyp&aa. "&&vlabel&aa.",
            %end;

        %if &aa. =  &varn. %then
            %do;
                &&vnam&aa.  &&vtyp&aa. "&&vlabel&aa."
            %end;
%end;
        );
        quit;

proc datasets lib=work noprint;
delete _:;
run;

%mend;

新手小白,疏漏在所難免,如果您看完有所指正,那我會很感謝您。如果您看完有所收獲,那是我的榮幸。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容