批量改变SAS数据集字符型变量的长度

临床试验的SAS程序猿/媛都知道,FDA对所提交的数据集的大小是有限定的,因为数据集过大在操作时会有点麻烦(比如打开会很慢),所以当我们生成最终的数据集时就要进行一个操作:按照字符型变量值的最大长度来重新定义变量的长度,以删除多余的空格从而减少数据集的大小。下面贴上我去年写的实现这一目的的宏程序:

%macro relngth(slib=, mem=);
proc sql noprint;
    select cats(n(NAME)) into :vnum
        from dictionary.columns
        where LIBNAME=upcase("&slib") and MEMNAME=upcase("&mem") and  TYPE='char'
        order VARNUM
        ;

    select cats(NAME) into :var1 -:var&vnum
        from dictionary.columns
        where LIBNAME=upcase("&slib") and MEMNAME=upcase("&mem") and  TYPE='char'
        order VARNUM
        ;

    select %do i=1 %to %eval(&vnum-1); "&&var&i"||' char('||cats(max(lengthn(&&var&i)))||'), '|| %end;
                                       "&&var&vnum"||' char('||cats(max(lengthn(&&var&i)))||')'
        into :modlst
        from &slib..&mem
        ;

    alter table &slib..&mem
        modify &modlst
        ;
quit;
%mend relngth;

/*SDTM数据集所在的逻辑库名字*/
%let slib=TRANSFER;

options NOQUOTELENMAX;

data _null_;
    set sashelp.vtable(where=(LIBNAME="&slib"));
    call execute('%nrstr(%relngth(slib=&slib, mem='||cats(MEMNAME)||'))');
run;

注意,上面的程序中我并没有直接用METADATA中的DATADEF这个数据集,而是用了视图SASHELP.VTABLE,这是为了说明另一个问题:SASHELP.VTABLE虽然可以直接在DATA步中使用,但是不建议使用,因为在我们使用这个视图时SAS后台执行视图的操作并没有优化,而且在LOG中有可能看到类似下面的CEDA信息:

INFO: Data file libref.member.DATA is in a format native to another host or the file encoding does not match the session encoding. Cross Environment Data Access will be used, which may require additional CPU resources and reduce performance.

这些都会大大的影响程序运行效率,故建议使用数据字典,原因在SAS在线文档中有说明,搬运如下:

When querying a DICTIONARY table, SAS launches a discovery process that gathers information that is pertinent to that table. Depending on the DICTIONARY table that is being queried, this discovery process can search libraries, open tables, and execute views. Unlike other SAS procedures and the DATA step, PROC SQL can mitigate this process by optimizing the query before the discovery process is launched. Therefore, although it is possible to access DICTIONARY table information with SAS procedures or the DATA step by using the SASHELP views, it is often more efficient to use PROC SQL instead.

程序如下:

/*SDTM数据集所在的逻辑库名字*/
%let slib=TRANSFER;

options NOQUOTELENMAX;

proc sql;
    create table datadef as
        select MEMNAME
        from dictionary.tables
        where LIBNAME=upcase("&slib")
        ;
quit;

data _null_;
    set datadef;
    call execute('%nrstr(%relngth(slib=&slib, mem='||cats(MEMNAME)||'))');
run;

当然还可以使用PROC CONTENTS或者FILENAME PIPE方法来得到数据集DATADEF,程序如下:

/*PROC CONTENTS*/
ods output members=datadef;

proc contents data=&mlib.._all_ memtype=data;
run;

/*数据集变量列表
proc contents data=&mlib..cd out=varlist;
run;
*/

/*FILENAME PIPE*/
filename raw pipe "ls &_meta.*.sas7bdat | sed 's/.*\/\(.*\)\.sas7bdat/\1/'"; 
/*结果为单行的命令:"echo `ls &_meta.*.sas7bdat | sed 's/.*\/\(.*\)\.sas7bdat/\1/'`"*/

data datadef;
    infile raw;
    input;
    length DATASET $8;
    DATASET=upcase(cats(_INFILE_));
run;

filename raw clear;

不过还是直接使用METADATA中的DATADEF这个数据集最方便了,程序如下:

/*SDTM数据集所在的逻辑库名字*/
%let slib=TRANSFER;

/*METADATA所在的逻辑库名字*/
%let mlib=META;

options NOQUOTELENMAX;

data _null_;
    set &mlib..datadef(keep=DATASET);
    call execute('%nrstr(%relngth(slib=&slib, mem='||cats(DATASET)||'))');
run;

最后推荐几个链接:传送门一传送门二传送门三

曾宪华 /
本文采用 署名-非商业性使用-相同方式共享 3.0许可协议 属于 程序人生 分类, 被贴了 CDISC Column Resizing DICTIONARY Tables SASHELP Views FILENAME PIPE 书签

上一篇 根据变量值拆分SAS数据集
下一篇 SAS数据集中重复记录问题