这是一个项目的ADaM_SPEC中的ADSL页的截图,这一页面中规定了ADSL最后输出的变量的name、label、排列顺序以及生成规则。当我们在进行ADaM的编程的时,在数据集内容基本完成阶段,会集中设置数据变量的属性,使其与SPEC中的顺序保持一致。这样的属性设置一般有两种方法,第一,手动设置每一个变量的属性;第二,自动化设置变量属性。
手动设置很简单,不做介绍。需要注意的是,保持数据集中的变量顺序与SPEC一致,不少人在平时的编程中不被重视这一点。手动设置的优点很明显,操作简便,直接复制粘贴。只需要把SPEC上各个属性设置手动粘贴到代码中。这样的缺点也很直接,琐碎耗时。以图片中的ADSL举例,这个数据集总共有73个变量,一个个复制粘贴变量属性,这过程实在是太过耗时。而且,一个项目所需要的数据集数量通常是10多个。如果每一个数据集都这样复制粘贴属性,效率太低。
于是,根据SPEC自动设置变量属性的需求就产生了。如何操作呢?如上图所示,变量的Name、Label
、Type、Length已经存在SPEC表格中,我们需要将这些信息读取到SAS中,并将信息使用到变量属性设置中去。下面我来讲解实现这样操作的程序。
示例程序是一个宏程序,有两个宏参数:domain、dirspec。这两个参数的意义很好理解,所处理的数据集名称以及SPEC地址。为了演示方便,我手动给这两个参数赋值后,直接运行宏内部的程序。
前面4行代码,一个直观的理解是将SPEC从原始路径,复制到WORK 逻辑库的物理路径下,并重命名为ADaM.XLS。第一行的options语句,noxwait的文档解释是:Specifies that the command processor automatically returns to the SAS session after the specified command is executed. You do not have to type EXIT。翻译成中文:指定在执行指定的命令之后,命令处理器自动返回到SAS会话,不必键入EXIT。简单讲,复制操作是在SAS外部进行的,X语句运行结束后,自动回到SAS操作中。接着,SPEC地址就换成复制粘贴后的地址了。我的理解,这样操作是为了避免原始文件在代码运行中损坏。
接下来是三个import过程步导入EXCEL各个表单的信息,下面展示原始页和导入之后的情况,我将DADASET赋值为ADCM进行举例。
Datasets页:输出数据集为_adam_Domains。导入代码中,getnames=yes,表单首行作为变量名称读入,数据集观测从表单中第二行内容开始读入ADSL页:输出数据集为_adam_ADSL。getnames=yes,表单第一行列名被当做内容读入。代码看到这里,我当时觉得很奇怪,第一行列名在编程操作中应该不会使用,这里为什么不直接getnames=yes直接从第二行读入呢?想到一个可能的理由,这里的列名两个单词长度有点长,后面使用起来不方便,直接默认列名使用简单快捷。这里回看Dasets页数据集展示,要使用的列名都很简短,Dataset、Keys、Class。
ADCM页:输出数据集为_adam_xls。与ADSL页类似,没将首行值读入成变量名。
EXCEL文件中信息导入SAS后,下面开始处理数据集信息、label和排序变量。因为在Proc Sort中,排序变量必须以空格间隔,这里去掉了EXCEL中读入的 “,”。第一个Data步的作用是保留目前需要操作的数据集的记录。
第二个Data步处理导入的ADSL信息,处理时只保留了EXCEL表单中6个变量,删掉了读入的列名信息和Type(F3)为空的变量。生成新数据集时,重命名默认的变量名,便于理解变量含义。从新的变量名以及删掉首行读入的变量名来看。代码作者很大可能就是不想使用过长的代码名称(想通别人的操作,趣味性很大~~)我们可以看到F10这个变量并没有重命名,在原始的EXCEL中,这个变量名是Common Variable Flag,不止ADSL数据集,其他domain也要包含这个flag 为Y的变量。
这一块的代码是一段宏程序的判断,根据所处理数据集的不同类型,进行不同的处理。不过从代码看,目前对这两类数据集(OCCDS和BDS)处理都是相同的,都是去除Type为空以及从EXCEL中读取的首行列名的信息。跟ADSL不同,ADCM的SPEC并没有STUDYID、USUBJID、SUBJID的信息,后面的代码应该会出。
这部分的代码是处理数据集变量的属性,从上一步的操作上,我们已经得到了ADCM变量名、变量label和变量类型。我们整理这些变量信息如何应用呢?在手动设置变量过程,通常使用,attrib语句。自动设置属性的思路是,变量属性的信息不再通过手动输入,而是程序自动读取。所以这一部分读取到的信息,需要整理符合attrib语法的形式。
我们来看这一部分代码,前面关于label的两个语句,是去除空格和换行的。从外部读取的内容,通常会含有跨行和空格,如果不处理,程序运行会出错。Varname的处理,只是去掉空格。这段程序的主要部分,是对不同属性的变量进行定义,有细微的不同。字符型在定义长度时,需要在数字前加上美元符号“$”;数值变量需要考虑一些变量不单单是纯粹的数字,像时间变量,还需要设置数值格式。
可以看到,attrb列中值已经是attrib语句的标准格式,现在需要将他们整合到一起。接下来的Proc SQL中有四个select语句,分别生成4个宏变量。宏变量attrb包含adamspc数据集中attb列中所有属性值;宏变量keep1包含adamspc中变量varname的所有值,这里需要注意一点,原有的ADaM_SPEC中不包含STUDYID、USUBJID的信息,我们后续保留变量的时候需要添加;宏变量Sortvar保留排序变量的信息,这个信息源头是EXCEL的Datasets页;宏变量Dataset_label包含数据集的label信息,这也来自于EXCEL中的Datasets页。到这一步,SPEC中的数据集属性信息已经提取完毕了。
这个宏的目的是自动设置ADaM输出数据集的变量属性,在调用这个宏的之前,输出数据集的内容必须是加工完成只剩下属性设置了。这里属性设置主要有两部分,第一部分是label、format、length的设置(attrib语句);第二部分,变量顺序的设置(retain语句)。这里的属性设置,ADSL数据集和其他数据集是分开处理的。这里的第二个data步其实是多余的,因为attrib语句会设定好变量的顺序。
对于除ADSL的其他数据集来说,除了设置自身变量属性,还需要merge ADSL数据集中的Common Variables。代码中的操作是,先把所有common variables变量名保存到宏变量CoreVar中,方便编程时引用。这里数据集的merge的By变量是studyid usubjid subjid,因为其他数据集的SPEC中没有这三个变量的信息,所以在keep变量时要将这三个变量加上。在merge上ADSL中的Common Variables的内容后,用retain语句调整数据集中的变量顺序,最后按照排序变量排序。
至此,ADaM数据集根据SPEC自动设置变量属性的操作已经完成。