關(guān)于Listing換行方面的內(nèi)容,我總結(jié)有3個(gè):
- Header內(nèi)容的換行;
- 觀測(cè)內(nèi)容的換行;
- 輸出列過多換行。
1.Header的換行
正常來講,Listing中的列寬是根據(jù)輸出列中具體的內(nèi)容長(zhǎng)度來確定的;如果列中內(nèi)容較少,該列Header又不長(zhǎng)的情況下,我們可以設(shè)置列寬完整地在一行中輸出Header。但如果列中內(nèi)容較少、Header過長(zhǎng),這時(shí)候就不宜設(shè)置較大列寬,應(yīng)該在Header中手動(dòng)設(shè)置換行,多行顯示Header。Header的換行在Table中也經(jīng)常出現(xiàn)。
常用的Header換行有兩種方法,一種是PROC REPORT中的SPLIT=選項(xiàng),指定分割字符進(jìn)行換行;另一種是利用“轉(zhuǎn)義字符+n”進(jìn)行換行。
1.1 SPLIT= 選項(xiàng)
臨床試驗(yàn)分析中,Lisiting內(nèi)容一般都是通過PROC REPORT輸出到RTF中,PROC REPORT中的SPLIT=選項(xiàng)可以指定Header的分割字符。PROC REPORT在到達(dá)分割字符時(shí)中斷文本,并在下一行繼續(xù)文本。不過要注意,分隔字符本身并不是列標(biāo)題或文本值的一部分,但分隔字符的每次出現(xiàn)都計(jì)入標(biāo)簽的字符數(shù),如果超過256個(gè)字符會(huì)發(fā)生截?cái)?。同時(shí),該選項(xiàng)只適用于列標(biāo)題分割,不會(huì)對(duì)列的內(nèi)容進(jìn)行分割。
我以SASHELP邏輯庫(kù)中的CLASS數(shù)據(jù)集進(jìn)行演示,將NAME、SEX兩列輸出到RTF文件中,列名分別設(shè)為“Name”、“Male Female”,SEX列名分兩行輸出。分隔符設(shè)置為#,為了測(cè)試列的內(nèi)容是否換行,在Name列第一行的內(nèi)容中補(bǔ)充“#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數(shù)據(jù)集如下:

RTF輸出如下,列標(biāo)題已經(jīng)實(shí)現(xiàn)跨行,列的內(nèi)容中的分隔符不會(huì)實(shí)現(xiàn)跨行:

1.2 “轉(zhuǎn)義字符+n”實(shí)現(xiàn)換行
先介紹一下轉(zhuǎn)義字符(Escape character),我直接引用百度百科的內(nèi)容:
轉(zhuǎn)義字符是很多程序語言、數(shù)據(jù)格式和通信協(xié)議的形式文法的一部分。對(duì)于一個(gè)給定的字母表,一個(gè)轉(zhuǎn)義字符的目的是開始一個(gè)字符序列,使得轉(zhuǎn)義字符開頭的該字符序列具有不同于該字符序列單獨(dú)出現(xiàn)時(shí)的語義。因此轉(zhuǎn)義字符開頭的字符序列被叫做轉(zhuǎn)義序列。
百科中也給了一些轉(zhuǎn)義字符含義的示例:

簡(jiǎn)單講“轉(zhuǎn)義字符+字母序列”可以表達(dá)某種特別的含義,不同的編程語言各自的轉(zhuǎn)義字符可能不同。SAS是通過ODS ESCAPECHAR= 'escape-character';語句來定義轉(zhuǎn)義字符的,轉(zhuǎn)義字符應(yīng)該是很少使用的字符,例如:@,^,或#。對(duì)于RTF的轉(zhuǎn)義字符有個(gè)注意點(diǎn),\是一個(gè)特殊的RTF字符。因此,對(duì)于RTF輸出,建議使用除\以外的轉(zhuǎn)義字符。SAS中轉(zhuǎn)義字符沒有默認(rèn)值,但我們可以像使用轉(zhuǎn)義字符一樣使用特殊轉(zhuǎn)義序列(*ESC*),這與轉(zhuǎn)義字符的作用是一致的。
參考上面的轉(zhuǎn)義字符的示例圖,發(fā)現(xiàn)“轉(zhuǎn)義字符+n”表示換行,我們可以由此實(shí)現(xiàn)Header的換行。我將轉(zhuǎn)義字符設(shè)置為^,進(jìn)行測(cè)試,代碼如下:
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;
數(shù)據(jù)集內(nèi)容如下:

輸出的RTF如下:

從RTF輸出結(jié)果可以發(fā)現(xiàn),^n實(shí)現(xiàn)了Header的換行。同時(shí)觀察Name列第一行值,^A這個(gè)轉(zhuǎn)義序列沒有對(duì)應(yīng)的特殊含義,轉(zhuǎn)義字符不顯示,字母A照常輸出。
2. 觀測(cè)內(nèi)容的換行
在Listing的Output中,我們會(huì)經(jīng)常遇到觀測(cè)內(nèi)容的換行,如果僅僅機(jī)械設(shè)置列寬,讓內(nèi)容自動(dòng)換行,這樣的換行大概率不盡人意。舉個(gè)簡(jiǎn)單的例子,有關(guān)AE的Listing通常會(huì)這樣輸出時(shí)間變量,Ae Start Date (Day)。如果輸出列較多,為了節(jié)省空間,Header中的(Day)通常換行顯示,列中內(nèi)容也通常將Date與Day換行顯示。
如果靠不斷調(diào)整列寬來實(shí)現(xiàn)(Day)的換行,這個(gè)工作會(huì)很繁瑣。并且在自己設(shè)備上換行成功后,當(dāng)使用其他設(shè)備打開輸出文件時(shí),由于軟件的基本設(shè)置可能不同,這樣的換行位置可能會(huì)發(fā)生變化。因此,不推薦通過調(diào)整列寬來實(shí)現(xiàn)列內(nèi)容的換行。關(guān)于觀測(cè)內(nèi)容特定位置的換行,推薦1.2節(jié)的方法,使用“轉(zhuǎn)義字符+n”實(shí)現(xià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;
數(shù)據(jù)集內(nèi)容如下:


以上Header和列內(nèi)容的換行使用的是特殊轉(zhuǎn)義序列(*ESC*)n,我們也可以自定義轉(zhuǎn)義字符進(jìn)行實(shí)現(xiàn)(ods escapechar = "^"),Header的換行使用PROC REPORT的Split=的選項(xiàng),結(jié)果與上面一致。
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時(shí),輸出列過多是一個(gè)常見的現(xiàn)象。不同的公司、不同的項(xiàng)目對(duì)于Listing過多列的處理要求可能不同。這里我介紹一下我接觸到的兩種方式,不管是哪種方式,一行放不下的列肯定是要換行輸出的。換行輸出會(huì)遇到一個(gè)問題,換行后查看后半部分列內(nèi)容時(shí),可能與前面內(nèi)容不方便對(duì)應(yīng)。這時(shí)候有兩種處理辦法,第一種,換行時(shí)在下一行也顯示前一行的ID變量;第二種,在一頁中只輸出一個(gè)ID的記錄,避免查看時(shí)產(chǎn)生誤導(dǎo)。兩種方式?jīng)]有什么絕對(duì)的優(yōu)劣,主要看在實(shí)際項(xiàng)目中的基于項(xiàng)目情況的選擇。
下面我用SASHELP邏輯庫(kù)中的CLASS數(shù)據(jù)集進(jìn)行演示,我先多次拼接實(shí)現(xiàn)“列過多”,然后不做其他處理進(jìn)行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輸出結(jié)果如下:

我們可以看到一頁無法放下這個(gè)17個(gè)變量,最后4個(gè)變量自動(dòng)進(jìn)行換行輸出;換行后由于記錄數(shù)過多,還產(chǎn)生了跨頁的情形。如果要在一頁中查看某個(gè)人的后4個(gè)變量的信息,一下子很難對(duì)應(yīng)到具體的那一條記錄?,F(xiàn)在,我用兩種方式分別處理這種情況。
3.1 PROC REPORT中Define語句的ID選項(xiàng)
Define語句中的ID選項(xiàng),會(huì)定義指定的變量為ID變量。ID變量及其左側(cè)的所有列會(huì)顯示在報(bào)表的每一頁的左側(cè);當(dāng)輸出的列過多發(fā)生換行時(shí),換行后最左側(cè)會(huì)顯示ID變量,便于識(shí)別輸出內(nèi)容。
考慮到換行后RTF不能放下所有記錄,我們將數(shù)據(jù)集記錄前10條輸出到第一頁,后面的記錄輸出到第二頁。
前面自動(dòng)換行后,第二部分只顯示了4個(gè)變量,即便加上ID變量也只是5個(gè)變量,顯得上下不協(xié)調(diào)。如果想要實(shí)現(xiàn)上下兩部分顯示寬度差不多的,一般通過調(diào)節(jié)各個(gè)變量的列寬,使得上部分變量列寬總和、下部分變量列寬綜合以及頁面展示寬度大致相等。這里就不進(jìn)一步調(diào)整了,直接看下示例代碼:
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;
第一頁輸出內(nèi)容如下:

我們可以看到換行后的第二部分左側(cè)出現(xiàn)了ID變量Name,這樣就方便查看了。
3.2 一頁只輸出一個(gè)ID的記錄
如果沒有使用ID選項(xiàng),RTF一頁輸出多條記錄,會(huì)造成查看不方便。但如果一頁只生成一條記錄,即便沒有ID變量,也不會(huì)對(duì)查看造成誤導(dǎo),例如下面的情況:
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;
輸出的第一頁結(jié)果如下:

有人可能會(huì)疑問,RTF一頁就輸出這么點(diǎn)內(nèi)容,頁面看起來不會(huì)太“空曠”嗎?有可能出現(xiàn)這種情況。但在實(shí)際項(xiàng)目中也經(jīng)常有Listing的Title和Footnotes內(nèi)容較多的情況,一個(gè)頁面中實(shí)際的數(shù)據(jù)展示空間可能就只夠一條記錄的內(nèi)容了。這時(shí)候一頁輸出一條記錄,即便發(fā)生換行,也是可以接受的。
3.3 Define語句的Page選項(xiàng)的介紹
當(dāng)Listing輸出列過多時(shí),如果不要求一條記錄的所有列都在同一頁中展示,我們還可以使用Page選項(xiàng)讓多余的列進(jìn)行換頁輸出。
Define語句中的Page選項(xiàng),會(huì)在使用該選項(xiàng)的列之前,插入分頁符。這樣的話,這一列以及右側(cè)的列都會(huì)在新的一頁中輸出。我對(duì)變量Height2進(jìn)行該選項(xiàng),Height2及其右側(cè)變量都會(huì)換頁輸出,來看一下這個(gè)選項(xiàng)的效果:
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;
輸出結(jié)果如下:

以上就是有關(guān)Listing換行內(nèi)容的分享,若有疑問,歡迎評(píng)論反饋!