写在前面。
今天是个小工具宏,大佬们见笑了。如果您看完有所指正,那我会很感谢您。如果您看完有所收获,那是我的荣幸。
在做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信息来源的考虑
我考虑了两种情况:
-
你把
spec
所在的路径给出了,并且使用filename
语句引用给了一个标识符
。好,那这个宏就按照标识符引用的路径
去读你的spec
,然后进行后续的步骤; -
你已经把
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
你进行了赋值
,并且这个数据集名称你不是乱写的,是有数据的
,这个宏才能够从中读取数据并且进行一系列宏变量赋值操作。
这步骤中宏变量赋值的思路
我讲一下:
-
使用
proc contents
获取给的spec
数据集变量的信息,主要是前4
个变量的名称,必须依次为名称、标签、类型和长度
,然后赋值给了4个宏变量
; -
然后使用
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个个变量的信息
,并且顺序也是固定
。
上述代码主要涉及的就是一些宏变量的赋值
,我有说明的不清楚的地方的话,可以查看我这篇文章:
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
中的Num
和Char
;如果你们不是使用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;
新手小白,疏漏在所难免,如果您看完有所指正,那我会很感谢您。如果您看完有所收获,那是我的荣幸。
网友评论