SAS编程实践---宏“Mkfrm”:创建某一个域的空数据集

2023-11-09  本文已影响0人  RSP小白之路

写在前面。

今天是个小工具宏,大佬们见笑了。如果您看完有所指正,那我会很感谢您。如果您看完有所收获,那是我的荣幸。

在做SDTM或者ADaM数据集时,我和同事一般的习惯是:

写某一个域的数据集时,比如dm、adsl,...,先根据spec文件中的说明,创建一个包含这个域中所有变量的空数据集,再去根据spec说明创建包含所需变量的数据集,最后再把做好的数据集和这个空的数据集set一下。

这样最终的数据集中变量的顺序、名称、标签、类型和长度等就与spec中说明一致了。

很多不同的方法实现,个人感觉使用proc sql还是很方便的。

但是还是要敲不少代码,如果spec写的足够规范质量很高,直接读入来创建这个空的数据集(框架)是个不错的主意。

注意,前提是spec写的足够好spec写的足够好spec写的足够好


准备步骤

目标拆解

一样的,先进行一下目标的拆解:

Variable Label Type Length or Display Format
STUDYID 研究编号 C 200
USUBJID 受试者唯一编号 C 200
TRTSEQP 给药顺序 C 200
TRTSEQPN 给药顺序(数值) N 8
FASFL 是否进FAS C 200

注意,我们公司是用C和N标识变量类型,这和后面一个条件判断有关;

如果你们不是使用N/C,那后面的代码需要进行适当修改

STUDYID USUBJID TRTSEQP TRTSEQPN FASFL

宏编写步骤

注释

宏的作用,注意事项,参数的说明在注释中已经写清楚了,如下:


/*******************************************************************************************
Purpose: 从导入SAS的spec数据集中,或者从外部spec文件中,获取一个域对应数据集中变量的名称、标签、类型和长度的4个变量,产生这个域的空数据集;

dtin:如果已经将spec文件读入SAS会话中产生数据集,则将dtin参数赋值为该数据集名称;

      注意,该数据集的前4个变量必须依次为:名称、标签、类型和长度;

      如果是要从外部spec文件创建的情形,则不要给dtin参数赋值;

sepcfile:如果没有将spec文件读入SAS会话中,则需要使用filename语句产生spec文件路径的引用,
          例如,filename name ".../spec.xlsx";,则将name赋值给参数sepcfile;

dbms:适用于从外部spec文件创建的情形,赋值给import过程步的DBMS参数;

sheet:适用于从外部spec文件创建的情形,赋值给import过程步的SHEET参数,SHEET名称应该在给定的EXCEL中;

startrow:适用于从外部spec文件创建的情形,赋值给import过程步的datarow参数,开始读取数据的行;
          注意,默认不读取变量名,因此,直接赋值给需要读取的数据的开始行;

frm:一般也就是域名,用于这个域的空数据集名称的构建;

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

sepc信息来源的考虑

我考虑了两种情况:

  1. 你把spec所在的路径给出了,并且使用filename语句引用给了一个标识符。好,那这个宏就按照标识符引用的路径去读你的spec,然后进行后续的步骤;

  2. 你已经把spec文件读进SAS会话创建了数据集。好的,那这个宏就使用这个数据集进行后续步骤。

所以数据的预处理使用了条件判断:

从SAS数据集中读取数据

%if ( %sysfunc(exist(&dtin. ,data )) and &dtin. ^= %str() )   %then %do;
%put WARNING: 将从SAS数据集中读取数据;
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;

注意为真的条件,输入数据集参数dtin你进行了赋值,并且这个数据集名称你不是乱写的,是有数据的,这个宏才能够从中读取数据并且进行一系列宏变量赋值操作。

这步骤中宏变量赋值的思路我讲一下:

  1. 使用proc contents获取给的spec数据集变量的信息,主要是前4个变量的名称,必须依次为名称、标签、类型和长度,然后赋值给了4个宏变量

  2. 然后使用proc sql将给的spec数据集中的这四个宏变量指定的变量的所有观测分别赋值给4组宏变量

从外部spec文件中读取数据

%else %do;
%put WARNING: 将从外部spec文件中读取数据;

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;

注意,不论是从已存在的数据集还是外部文件,数据集中必须包含名称、标签、类型和长度这4个个变量的信息,并且顺序也是固定

上述代码主要涉及的就是一些宏变量的赋值,我有说明的不清楚的地方的话,可以查看我这篇文章:

SAS编程实践 宏变量赋值(一文尽力涵盖)

proc sql创建空数据集

最后就是这个宏核心功能步,其实就是在proc sql中使用了循环和条件判断

    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;

注意,前一个条件判断,是为了把数据类型N或者C转换为sql中的NumChar;如果你们不是使用N/C标识变量类型,那么这个判断需要根据实际情况修改。

另一个条件判断,

是因为使用proc sql创建空数据集时的语句中,最后一句后面没有逗号,而前面都有,没有这个判断,会报错的。

这个宏没有核心步骤后的处理和输出步,不过还是清除了一下中间临时数据集。

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

总结

以上就是我写这个宏的最主要代码,完整代码如下:

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

/*******************************************************************************************
Purpose: 从导入SAS的spec数据集中,或者从外部spec文件中,获取一个域对应数据集中变量的名称、标签、类型和长度的4个变量,产生这个域的空数据集;

dtin:如果已经将spec文件读入SAS会话中产生数据集,则将dtin参数赋值为该数据集名称;

      注意,该数据集的前4个变量必须依次为:名称、标签、类型和长度;

      如果是要从外部spec文件创建的情形,则不要给dtin参数赋值;

sepcfile:如果没有将spec文件读入SAS会话中,则需要使用filename语句产生spec文件路径的引用,
          例如,filename name ".../spec.xlsx";,则将name赋值给参数sepcfile;

dbms:适用于从外部spec文件创建的情形,赋值给import过程步的DBMS参数;

sheet:适用于从外部spec文件创建的情形,赋值给import过程步的SHEET参数,SHEET名称应该在给定的EXCEL中;

startrow:适用于从外部spec文件创建的情形,赋值给import过程步的datarow参数,开始读取数据的行;
          注意,默认不读取变量名,因此,直接赋值给需要读取的数据的开始行;

frm:一般也就是域名,用于这个域的空数据集名称的构建;

**************************************************************************************************/
%if ( %sysfunc(exist(&dtin. ,data )) and &dtin. ^= %str() )   %then %do;
%put WARNING: 将从SAS数据集中读取数据;
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文件中读取数据;

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;

新手小白,疏漏在所难免,如果您看完有所指正,那我会很感谢您。如果您看完有所收获,那是我的荣幸。

上一篇下一篇

猜你喜欢

热点阅读