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

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

作者: RSP小白之路 | 来源:发表于2023-11-09 08:45 被阅读0次

    写在前面。

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

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

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

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

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

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

    注意,前提是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;
    

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

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/dytcwdtx.html