寫在前面。
今天是個(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信息來源的考慮
我考慮了兩種情況:
你把
spec所在的路徑給出了,并且使用filename語句引用給了一個(gè)標(biāo)識符。好,那這個(gè)宏就按照標(biāo)識符引用的路徑去讀你的spec,然后進(jìn)行后續(xù)的步驟;你已經(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)行一系列宏變量賦值操作。
這步驟中宏變量賦值的思路我講一下:
使用
proc contents獲取給的spec數(shù)據(jù)集變量的信息,主要是前4個(gè)變量的名稱,必須依次為名稱、標(biāo)簽、類型和長度,然后賦值給了4個(gè)宏變量;然后使用
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è)變量的信息,并且順序也是固定。
上述代碼主要涉及的就是一些宏變量的賦值,我有說明的不清楚的地方的話,可以查看我這篇文章:
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中的Num和Char;如果你們不是使用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;
新手小白,疏漏在所難免,如果您看完有所指正,那我會很感謝您。如果您看完有所收獲,那是我的榮幸。