暑期在辉瑞参加培训结束的时候需要做个case study,要生成8张report。一般都是用proc report生成报表然后在做post processing,我们组在这个的基础上做了data+data+data的报表生成。就是用data step生成analysis dataset,然后用data step生成report datasets,最后在用data step输出。前两步基本没什么问题。最后一步data输出遇到了一些问题。
通常用data步输出采用的是下面的方式:
Table 5.6
Laboratory Normal Range Shift Table
--------------------------------------------------------------------
Lab Category: Baseline Value
Lab Name ------------------------------------------------------------
(Lab Units) Active Placebo
-------------------------- ----------------------------------
Low Normal High Low Normal High
----------------------------------------------------------------------------------------
Week 1 Low xx xx xx xx xx xx
Normal xx xx xx xx xx xx
High xx xx xx xx xx xx
etc.
要生成上面的报表,用data _null_的话,代码通常是下面的样子:
options nodate nonumber;
title1 "Table 5.6";
title2 "Laboratory Shift Table";
title3 " ";
data _null_;
set freqs end = eof;
by lbcat lbtest lbstresu week trtcd;
**** SUPPRESS TOTALS.;
where baseflag ne '' and lbnrind ne '';
**** SET OUTPUT FILE OPTIONS.;
file print titles linesleft = ll pagesize = 50 linesize = 80;
**** WHEN NEWPAGE = 1, A PAGE BREAK IS INSERTED.;
retain newpage 0;
**** WRITE THE HEADER OF THE TABLE TO THE PAGE.;
if _n_ = 1 or newpage = 1 then
put @1 "-----------------------------------"
"-----------------------------------" /
@1 lbcat ":" @39 "Baseline Value" /
@1 lbtest
@17 "--------------------------------"
"----------------------" /
@1 "(" lbstresu ")" @25 "Placebo" @55 "Active" /
@17 "-------------------------- "
"--------------------------" /
@20 "Low Normal High Low Normal"
" High" /
@1 "-----------------------------------"
"-----------------------------------" /;
.
.
.
.
.
put @1 "Week " week
@10 "Low" @18 values(1,1) @27 values(1,2)
@36 values(1,3) @46 values(1,4)
@55 values(1,5) @64 values(1,6) /
@10 "Normal" @18 values(2,1) @27 values(2,2)
@36 values(2,3) @46 values(2,4)
@55 values(2,5) @64 values(2,6) /
@10 "High" @18 values(3,1) @27 values(3,2)
@36 values(3,3) @46 values(3,4)
@55 values(3,5) @64 values(3,6) /;
**** IF IT IS THE END OF THE FILE, PUT A DOUBLE LINE.;
if eof then
put @1 "-----------------------------------"
"-----------------------------------" /
"-----------------------------------"
"-----------------------------------" //
"Created by %sysfunc(getoption(sysin))"
"on &sysdate9..";
**** IF ONLY THE LAST WEEK IN A TEST, THEN PAGE BREAK.;
else if last.lbtest then
do;
put @1 "-----------------------------------"
"-----------------------------------" /
@60 "(CONTINUED)" /
"Created by %sysfunc(getoption(sysin)) "
"on &sysdate9.."
这样的代码很麻烦,当要输出新样式的表头时,要整个的改变code,而下面的做法就可以通用的适用于一切报表,而且简单方便。
[attachment=160]
也就是对表头和footnote做切割,切割成和report dataset变量数目一致的几个部分,每个部分的切割位置和他下面的数据应该保持一致。
然后把这些部分输入成dataset。
[attachment=161]
再把表头的数据集,footnote的数据集和report dataset合并。得到下面的效果:
[attachment=163]
[attachment=164]
这样的生成的新的reprot dataset就很方便输出了,下面的macro就用来输出这样的reprot dataset:
<br />
options mprint mlogic symbolgen;<br />
<br />
<br />
%macro datastep_report(dataset= ,libname=,ps=60,page=80,print=d:\case study\data_data.txt);<br />
options nodate nocenter nonumber ls=140 missing=' ';<br />
proc sql noprint;<br />
select count(*),name,length,label<br />
into:n_var,<br />
:name1-:name1000,<br />
:length1-:length1000,<br />
:label1-:label1000<br />
from sashelp.vcolumn<br />
where libname="&libname" and memname="&dataset";<br />
quit;<br />
<br />
proc sql noprint;<br />
select count(*),text<br />
into:n_title,<br />
:title1-:title1000<br />
from sashelp.vtitle<br />
where type="T";<br />
select count(*),text<br />
into:n_footnote,<br />
:footnote1-:footnote1000<br />
from sashelp.vtitle<br />
where type="F";<br />
quit;<br />
<br />
title;<br />
footnote;<br />
<br />
proc printto print="&print";<br />
run;<br />
<br />
data _null_;<br />
set &libname..&dataset end=eof nobs=obs;<br />
file print;<br />
num_obs=&ps-&n_title-&n_footnote;<br />
n=ceil(_n_/num_obs);<br />
page_end=mod(_n_,num_obs);<br />
page_begin=mod(_n_-1,num_obs);<br />
_page_num=ceil(obs/num_obs);<br />
<br />
if page_begin=0 then do;<br />
%page_begin;<br />
%observation;<br />
end;<br />
<br />
else if page_end=0 or eof then do;<br />
%observation;<br />
%page_end;<br />
end;<br />
<br />
else do;<br />
%observation;<br />
end;<br />
run;<br />
<br />
proc printto;<br />
run;<br />
%mend;<br />
%macro page_begin;<br />
*output title;<br />
%do i=1 %to &n_title;<br />
%if &i=1 %then %do;<br />
put @1 "&&title&i" @&page 'page ' n 'of ' _page_num;<br />
%end;<br />
%else %do;<br />
put @1 "&&title&i";<br />
%end;<br />
%end;<br />
*output variable label; <br />
put<br />
%let l0=1;<br />
%do i=1 %to &n_var;<br />
%let j=%eval(&i-1);<br />
%global l&i;<br />
%let l&i=%eval(&&length&i+&&l&j);<br />
@%eval(&&l&j) "&&label&i"<br />
%end;<br />
%mend;<br />
%macro page_end;<br />
%let x=%sysfunc(compress(l&n_var));<br />
put &&&x.*"-";<br />
%do i=1 %to &n_footnote;<br />
put @1 "&&footnote&i";<br />
%end;<br />
%mend;<br />
%macro observation;<br />
put<br />
%let l0=1;<br />
%do i=1 %to &n_var;<br />
%let j=%eval(&i-1);<br />
%global l&i;<br />
%let l&i=%eval(&&length&i+&&l&j);<br />
@%eval(&&l&j) &&name&i $%eval(&&l&j)-%eval(&&l&i)<br />
%end;<br />
;<br />
%mend;<br />
这个macro只需要输入reprot dataset的lib(要大写)和name,其他的信息都将从sashelp.vcolumn and sashelp.vtitle中读入.
值得一提是这里处理 page x of y的方式。
这里因为已经有了表头(包括title)和footnote的数据集,我们可以计算出当pagesize固定的时候(比如80),输出表头需要多少行,输出footnote需要多少行,剩下的行数就是用来输出reprot dataset的,比如说还剩下60行。假设report dataset需要输出200条观测值,那么就可以把这200条观测分割成4个数据集,第一个到第三个都有60条观测值,第四个数据集有20条观测值。这时候就可以用表头的数据集、footnote的数据集、第一个数据集(60个观测值)拼接成一个数据集,输出的时候他的page 就是 page 1 of 4。类似的可以拼接其他的三个数据集。而page依次从2到4。
语言表达能力比较差,希望不要造成太大的误会,呵呵。