Вот макрос отчета о смене чистой комнаты, который может принимать параметры
%shift_report (
data=have,
parameter=lab,
groupBy=study group, groupExpression=catx(' ','Study',study,'Group',group),
subject=subject,
range=snarfle_range
)
Чистый подход позволяет использовать форматы с несколькими метками, чтобы упростить учет комбинаций базовых диапазонов, которые будут отображаться в отчете. Например:
value $baselineRange (multilabel notsorted)
'Low' = 'Low'
'Normal' = 'Normal'
'High' = 'High'
'Low', 'Normal' = 'Low or Normal'
'High','Normal' = 'High or Normal'
;
Расширенные функции Proc MEANS
COMPLETETYPES
, CLASSDATA=
и MLF PRELOADFMT ORDER=DATA
были использованы для подсчета количества каждой пары сдвига диапазона в группе.
Процедуры SAS не имеют встроенного механизма для указания, что одна ячейка должна содержать <n> (%)
, поэтому эти значения ячеек должны быть вычислены. Я решил сделать это в вычислительных блоках REPORT, а не на шаге DATA.
Для разработки и тестирования макроса общего назначения обычно требуется больше данных, чем показано в вопросе, поэтому я написал генератор данных для имитации сбора данных. мера единорога рывок :
proc format;
value SnarfleRange
10-30 = 'Low'
30-55 = 'Normal'
55-95 = 'High'
. = 'Missing'
;
value $baselineRange (multilabel notsorted)
'Low' = 'Low'
'Normal' = 'Normal'
'High' = 'High'
'Low', 'Normal' = 'Low or Normal'
'High','Normal' = 'High or Normal'
;
run;
data have;
call streaminit(123);
do lab = 'Lab1', 'Lab2';
do study = 1 to 3;
do group = 1 to 3;
do subject = 1 to 10;
visit_top = ceil(rand('uniform', 8)); drop visit_top;
do _n_ = 1 to visit_top;
length visit $10;
visit_timestamp + 1; %* proxy for an actual timestamp;
if _n_ = 1
then visit = 'Baseline';
else visit = cats('Visit',_n_);
snarfle_measure = 10 + ceil(rand('uniform',85));
if rand('uniform') < 0.25 and _n_ = visit_top then
snarfle_measure = .;
snarfle_range = put (snarfle_measure, SnarfleRange.);
output;
end;
end;
end;
end;
end;
run;
%macro shift_report (data=, parameter=, groupBy=, groupExpression=, subject=, range=);
/* presume data sorted by lab, then study, then group, then subject, then visit order
* presume first subject visit is baseline and last subject visit is final
*
* presume ranges are Low, Normal, High
* presume baseline ranges reported are Low, Normal, High, Low|Normal, High|Normal;
*/
data firstlast_rows;
set &data;
by ¶meter &groupBy &subject;
* keep first and last measures, excluding subjects with only baseline;
if (first.subject or last.subject) and (not first.subject=last.subject);
if last.subject then visit = 'Final';
output;
rename ¶meter = Parameter;
run;
* Reshape to have one row per subject;
proc transpose data=firstlast_rows out=subject_base_final;
by Parameter &groupBy &subject;
var ⦥
id visit;
run;
* Count number of subjects in group;
proc freq noprint data=subject_base_final;
by Parameter &groupBy;
table Parameter / out=group_counts;
run;
* Prep classData for full shift report;
* Will allow report to show a 0 count when no subject has a ceratain shift;
data classData;
length baseline final $7;
do baseline = 'Low', 'Normal', 'High';
do final = 'Low', 'Normal', 'High', 'Missing';
output;
end;
end;
run;
/*
* Note:
* A PreLoadFmt of a format defined with option NOTSORTED will cause
* order=data to follow the order of the format definition
*/
* count the number of subjects that had which range shift from baseline;
proc means noprint data=subject_base_final classData=classData completeTypes;
by Parameter &groupBy;
class baseline / MLF order=data preloadfmt ; %* Multi-label format;
class final / order=data preloadfmt ;
types baseline * final;
format baseline $baselineRange.;
output out=shift_freqs n=n;
run;
* Reshape data for Proc REPORT;
proc transpose data=shift_freqs out=shift_table;
by Parameter &groupBy baseline notsorted;
var n;
id final;
run;
* Concatenate group count data with range shift count data;
* Needed for percent computation and first row reported for group;
data shift_table_groupn;
set group_counts shift_table;
by Parameter &groupBy;
report_group = &groupExpression; %* compute value to be shown in report for group column;
retain group_COUNT;
if not missing (COUNT) then group_COUNT = COUNT; %* repeat group count (# subjects), is needed for % computation;
%* percent should only be 100 and only present for data from group_counts (freq output);
if missing (percent)
then row_N = sum(low,normal,high);
else row_N = count;
run;
options missing = ' ';
proc report data=shift_table_groupn;
column
Parameter report_group group_count row_N
baseline baseline_n
low low_pct
normal normal_pct
high high_pct
missing missing_pct
;
define Parameter / order order=data;
define report_group / order order=data;
define group_COUNT / display noprint;
define row_N / display noprint;
define baseline / display noprint;
define low / display noprint;
define normal / display noprint;
define high / display noprint;
define missing / display noprint;
define baseline_n / 'BaseLine' computed;
define low_pct / 'Low' computed;
define normal_pct / 'Normal' computed;
define high_pct / 'High' computed;
define missing_pct/ 'Missing' computed;
compute after report_group;
line ' ';
endcomp;
compute baseline_n / character length=25;
baseline_n = ifc(row_N in (. 0), ' ', cats(baseline) || ' (N = ' || cats(row_N) || ')');
endcomp;
compute low_pct / character length=25;
if not missing(low) then low_pct=low;
if low > 0 then
low_pct = cats(low) || ' (' || cats(round(100*low/group_count)) || '%)';
endcomp;
compute normal_pct / character length=25;
if not missing(normal) then normal_pct=normal;
if normal > 0 then
normal_pct = cats(normal) || ' (' || cats(round(100*normal/group_count)) || '%)';
endcomp;
compute high_pct / character length=25;
if not missing(high) then high_pct=high;
if high > 0 then
high_pct = cats(high) || ' (' || cats(round(100*high/group_count)) || '%)';
endcomp;
compute missing_pct / character length=25;
if not missing(missing) then missing_pct=missing;
if missing > 0 then
missing_pct = cats(missing) || ' (' || cats(round(100*missing/group_count)) || '%)';
endcomp;
run;
options missing = '.';
%mend;
* Use whatever ODS destination and output location you want;
ods html5 file='shift_report.html' path='c:\temp';
%shift_report (
data=have,
parameter=lab,
groupBy=study group, groupExpression=catx(' ','Study',study,'Group',group),
subject=subject,
range=snarfle_range
)
ods _all_ close;