关于Listing换行方面的内容,我总结有3个:
- Header内容的换行;
- 观测内容的换行;
- 输出列过多换行。
1.Header的换行
正常来讲,Listing中的列宽是根据输出列中具体的内容长度来确定的;如果列中内容较少,该列Header又不长的情况下,我们可以设置列宽完整地在一行中输出Header。但如果列中内容较少、Header过长,这时候就不宜设置较大列宽,应该在Header中手动设置换行,多行显示Header。Header的换行在Table中也经常出现。
常用的Header换行有两种方法,一种是PROC REPORT中的SPLIT=
选项,指定分割字符进行换行;另一种是利用“转义字符+n”进行换行。
1.1 SPLIT= 选项
临床试验分析中,Lisiting内容一般都是通过PROC REPORT输出到RTF中,PROC REPORT中的SPLIT=
选项可以指定Header的分割字符。PROC REPORT在到达分割字符时中断文本,并在下一行继续文本。不过要注意,分隔字符本身并不是列标题或文本值的一部分,但分隔字符的每次出现都计入标签的字符数,如果超过256个字符会发生截断。同时,该选项只适用于列标题分割,不会对列的内容进行分割。
我以SASHELP逻辑库中的CLASS数据集进行演示,将NAME、SEX两列输出到RTF文件中,列名分别设为“Name”、“Male Female”,SEX列名分两行输出。分隔符设置为#
,为了测试列的内容是否换行,在Name列第一行的内容中补充“#A”。具体代码如下:
data class;
set sashelp.class;
if _n_ = 1 then name = strip(name)||"#A";
run;
ods rtf file = "../class.rtf";
proc report data = class split = "#";
column name sex;
define name /display "Name";
define sex /display "Male#Female";
run;
ods rtf close;
处理后的CLASS数据集如下:
RTF输出如下,列标题已经实现跨行,列的内容中的分隔符不会实现跨行:
1.2 “转义字符+n”实现换行
先介绍一下转义字符(Escape character),我直接引用百度百科的内容:
转义字符是很多程序语言、数据格式和通信协议的形式文法的一部分。对于一个给定的字母表,一个转义字符的目的是开始一个字符序列,使得转义字符开头的该字符序列具有不同于该字符序列单独出现时的语义。因此转义字符开头的字符序列被叫做转义序列。
百科中也给了一些转义字符含义的示例:
简单讲“转义字符+字母序列”可以表达某种特别的含义,不同的编程语言各自的转义字符可能不同。SAS是通过ODS ESCAPECHAR= 'escape-character';
语句来定义转义字符的,转义字符应该是很少使用的字符,例如:@
,^
,或#
。对于RTF的转义字符有个注意点,\
是一个特殊的RTF字符。因此,对于RTF输出,建议使用除\
以外的转义字符。SAS中转义字符没有默认值,但我们可以像使用转义字符一样使用特殊转义序列(*ESC*)
,这与转义字符的作用是一致的。
参考上面的转义字符的示例图,发现“转义字符+n”表示换行,我们可以由此实现Header的换行。我将转义字符设置为^
,进行测试,代码如下:
data class;
set sashelp.class;
if _n_ = 1 then name = strip(name)||"^A";
run;
ods rtf file = "../class.rtf";
ods escapechar = "^";
proc report data = class;
column name sex;
define name /display "Name";
define sex /display "Male^nFemale";
run;
ods rtf close;
数据集内容如下:
输出的RTF如下:
从RTF输出结果可以发现,^n
实现了Header的换行。同时观察Name列第一行值,^A
这个转义序列没有对应的特殊含义,转义字符不显示,字母A
照常输出。
2. 观测内容的换行
在Listing的Output中,我们会经常遇到观测内容的换行,如果仅仅机械设置列宽,让内容自动换行,这样的换行大概率不尽人意。举个简单的例子,有关AE的Listing通常会这样输出时间变量,Ae Start Date (Day)。如果输出列较多,为了节省空间,Header中的(Day)通常换行显示,列中内容也通常将Date与Day换行显示。
如果靠不断调整列宽来实现(Day)的换行,这个工作会很繁琐。并且在自己设备上换行成功后,当使用其他设备打开输出文件时,由于软件的基本设置可能不同,这样的换行位置可能会发生变化。因此,不推荐通过调整列宽来实现列内容的换行。关于观测内容特定位置的换行,推荐1.2节的方法,使用“转义字符+n”实现。下面我代码演示一下:
data ae;
aestdtc = "2021-01-26";
aestdy = 1;
col1 = put(input(aestdtc,yymmdd10.), date9.)||"(*ESC*)n("||strip(put(aestdy,best.))||")";
run;
ods rtf file = "../ae.rtf";
proc report data = ae;
column col1;
define col1 /display center "Ae Start Date(*ESC*)n(Day)" style(column)=[cellwidth=1in] ;
run;
ods rtf close;
数据集内容如下:
以上Header和列内容的换行使用的是特殊转义序列(*ESC*)n
,我们也可以自定义转义字符进行实现(ods escapechar = "^"
),Header的换行使用PROC REPORT的Split=
的选项,结果与上面一致。
ods escapechar = "^";
data ae;
aestdtc = "2021-01-26";
aestdy = 1;
col1 = put(input(aestdtc,yymmdd10.), date9.)||"^n("||strip(put(aestdy,best.))||")";
run;
ods rtf file = "../ae.rtf";
proc report data = ae split="#";
column col1;
define col1 /display center "Ae Start Date#(Day)" style(column)=[cellwidth=1in] ;
run;
ods rtf close;
3.输出列过多的换行
在做Lisiting时,输出列过多是一个常见的现象。不同的公司、不同的项目对于Listing过多列的处理要求可能不同。这里我介绍一下我接触到的两种方式,不管是哪种方式,一行放不下的列肯定是要换行输出的。换行输出会遇到一个问题,换行后查看后半部分列内容时,可能与前面内容不方便对应。这时候有两种处理办法,第一种,换行时在下一行也显示前一行的ID变量;第二种,在一页中只输出一个ID的记录,避免查看时产生误导。两种方式没有什么绝对的优劣,主要看在实际项目中的基于项目情况的选择。
下面我用SASHELP逻辑库中的CLASS数据集进行演示,我先多次拼接实现“列过多”,然后不做其他处理进行RTF输出。
data class;
merge sashelp.class(rename=(sex=sex1 age=age1 height=height1 weight=weight1))
sashelp.class(rename=(sex=sex2 age=age2 height=height2 weight=weight2))
sashelp.class(rename=(sex=sex3 age=age3 height=height3 weight=weight3))
sashelp.class(rename=(sex=sex4 age=age4 height=height4 weight=weight4));
by name;
run;
ods rtf file = "../class.rtf";
proc report data = class;
column name sex1-sex4 age1-age4 height1-height4 weight1-weight4;
run;
ods rtf close;
RTF输出结果如下:
我们可以看到一页无法放下这个17个变量,最后4个变量自动进行换行输出;换行后由于记录数过多,还产生了跨页的情形。如果要在一页中查看某个人的后4个变量的信息,一下子很难对应到具体的那一条记录。现在,我用两种方式分别处理这种情况。
3.1 PROC REPORT中Define语句的ID选项
Define语句中的ID选项,会定义指定的变量为ID变量。ID变量及其左侧的所有列会显示在报表的每一页的左侧;当输出的列过多发生换行时,换行后最左侧会显示ID变量,便于识别输出内容。
考虑到换行后RTF不能放下所有记录,我们将数据集记录前10条输出到第一页,后面的记录输出到第二页。
前面自动换行后,第二部分只显示了4个变量,即便加上ID变量也只是5个变量,显得上下不协调。如果想要实现上下两部分显示宽度差不多的,一般通过调节各个变量的列宽,使得上部分变量列宽总和、下部分变量列宽综合以及页面展示宽度大致相等。这里就不进一步调整了,直接看下示例代码:
data class;
merge sashelp.class(rename=(sex=sex1 age=age1 height=height1 weight=weight1))
sashelp.class(rename=(sex=sex2 age=age2 height=height2 weight=weight2))
sashelp.class(rename=(sex=sex3 age=age3 height=height3 weight=weight3))
sashelp.class(rename=(sex=sex4 age=age4 height=height4 weight=weight4));
by name;
if _n_<=10 then pg=1;
else pg = 2;
run;
ods rtf file = "../class.rtf";
proc report data = class;
by pg;
column name sex1-sex4 age1-age4 height1-height4 weight1-weight4;
define name / id;
run;
ods rtf close;
第一页输出内容如下:
我们可以看到换行后的第二部分左侧出现了ID变量Name,这样就方便查看了。
3.2 一页只输出一个ID的记录
如果没有使用ID选项,RTF一页输出多条记录,会造成查看不方便。但如果一页只生成一条记录,即便没有ID变量,也不会对查看造成误导,例如下面的情况:
data class;
merge sashelp.class(rename=(sex=sex1 age=age1 height=height1 weight=weight1))
sashelp.class(rename=(sex=sex2 age=age2 height=height2 weight=weight2))
sashelp.class(rename=(sex=sex3 age=age3 height=height3 weight=weight3))
sashelp.class(rename=(sex=sex4 age=age4 height=height4 weight=weight4));
by name;
run;
ods rtf file = "../class.rtf";
proc report data = class;
by name;
column name sex1-sex4 age1-age4 height1-height4 weight1-weight4;
run;
ods rtf close;
输出的第一页结果如下:
有人可能会疑问,RTF一页就输出这么点内容,页面看起来不会太“空旷”吗?有可能出现这种情况。但在实际项目中也经常有Listing的Title和Footnotes内容较多的情况,一个页面中实际的数据展示空间可能就只够一条记录的内容了。这时候一页输出一条记录,即便发生换行,也是可以接受的。
3.3 Define语句的Page选项的介绍
当Listing输出列过多时,如果不要求一条记录的所有列都在同一页中展示,我们还可以使用Page选项让多余的列进行换页输出。
Define语句中的Page选项,会在使用该选项的列之前,插入分页符。这样的话,这一列以及右侧的列都会在新的一页中输出。我对变量Height2进行该选项,Height2及其右侧变量都会换页输出,来看一下这个选项的效果:
data class;
merge sashelp.class(rename=(sex=sex1 age=age1 height=height1 weight=weight1))
sashelp.class(rename=(sex=sex2 age=age2 height=height2 weight=weight2))
sashelp.class(rename=(sex=sex3 age=age3 height=height3 weight=weight3))
sashelp.class(rename=(sex=sex4 age=age4 height=height4 weight=weight4));
by name;
run;
ods rtf file = "../class.rtf";
proc report data = class;
column name sex1-sex4 age1-age4 height1-height4 weight1-weight4;
define name / id;
define height2/ page;
run;
ods rtf close;
输出结果如下:
以上就是有关Listing换行内容的分享,若有疑问,欢迎评论反馈!