proc report spanning header color - sas

Data set in contains 4 columns col1-col4. I'm trying to create an output which separates 4 columns into two parts.
In the below code, by adding a fake variable blank, I can add one empty column between Part A and Part B.
options missing='';
proc report data=in missing
style(header)=[background=steelblue];
column ('Part A' col1 col2) blank ('Part B' col3 col4);
define blank/computed ' ' style=[background=white];
define col1 / display style[background=tan];
...
compute blank;
blank = .;
call define(_col_,'style','style={background=white borderbottomcolor=white}');
endcomp;
run;
The problem is I need
two different colors for spanning headers and the "original" headers.
the column between two spanning headers should be all white.
But the code is not able to the achieve 2nd purpose.
Current output looks like
1st row ------ Part A Part B (steelblue for entire row)
2nd row ------ col1 col2 col3 col4 (col1-col4 are tan, the column between col2 and col3 and white)
But the desired output is
1st row ------ Part A Part B (steelblue for Part A & B, but the column between them should be white)
2nd row ------ col1 col2 col3 col4 (col1-col4 are tan, the column between col2 and col3 and white)
I found this post but I can't even replicate Cynthia' output. The proc format seems doesn't work.
Proc Report - Coloring for Spanning Headers
This is fairly easy in excel - just insert a new empty column and no fill that column. How can I do this in SAS?

You don't mention ODS destination. This works for HTML and PDF(sort of).
I think the key assuming it actually does what you want is the use of 'a0'x the ascii non-breaking space. But this is not fully tested.
title;
options missing='';
proc format;
value $color
'a0'x = 'white'
other='steelblue'
;
proc report data=sashelp.class missing
style(header)=[background=$color. borderbottomcolor=$color.];
column ('Part A' name sex) ('a0'x blank) ('Part B' age weight height);
define _all_ / display style=[background=tan];
define blank / computed 'a0'x
style=[background=white borderbottomcolor=white]
style(header)=[background=white borderbottomcolor=white];
compute blank / char length=1;
blank = ' ';
call define(_col_,'style','style={background=white borderbottomcolor=white}');
endcomp;
run;

The code Cynthia published contains syntax errors (titles inside the proc report + missing ; on the style(header) lines).
With fixes, this works for me (SAS 9.3 AIX) :
proc format;
value $color
'REPORT' = '#9999FF'
'Australia' = '#FF6600'
'States' = 'pink'
'Wombat' = 'lightgreen'
other = 'lightblue';
value $altclr
'REPORT' = '#9999FF'
'Australia' = '#FF6600'
'States', 'Height', 'Weight' = 'pink'
'Wombat', 'Name', 'Age', 'Sex' = 'lightgreen'
other = 'lightblue';
run;
ods listing close;
ods tagsets.excelxp file = "%SYSFUNC(pathname(work))./Test.xml"
options ( embedded_titles='yes') style = sansprinter;
title 'All Headers Different Colors Based on Formats';
proc report data = sashelp.class(obs=3) nowd
style(header) = { background = $color. font_size= 10pt };
column ('REPORT'('Australia' ('Wombat' name age sex )('States' height weight)));
run;
title 'Some Headers Same Colors Based on Formats (one header diff)';
proc report data = sashelp.class(obs=3) nowd
style(header) = { background = $altclr. font_size= 10pt };
column ('REPORT'('Australia' ('Wombat' name age sex )('States' height weight)));
define name / 'Name';
define age / 'Age';
define sex / 'Sex';
define height / 'Height';
define weight / 'Weight' style(header)={background=lightyellow};
run;
ods _all_ close;

Related

Forest plot in SAS

I have downloaded this code from the web and I need to create a forest plot for each of the variables that have values as 'YES' and 'NO'. It works when each variable have different values however my data has same values for each of the variables. I tried several options but it is not working. Original code is downloaded from http://onsasprogramming.blogspot.com/2016/01/forest-plot-of-hazard-ratios-by-patient.html.
%let graphs='.';
%let dpi=100;
%let w=8in;
%let h=4.5in;
/*--Leading blanks in the subgroup variable must be non--blank spaces --*/
/*--Use character value 'A0', or copy from Windows System Character Map--*/
/*--Regular leading blanks will be stripped, losing the indentation --*/
data forest;
input Indent Subgroup $ HR LCL UCL ;
DATALINES;
0 Diabets . . .
2 YES 1.049419501 1.014598 1.085436
2 NO 1.149419501 1.114598 1.185436
0 OBESITY . . .
2 YES 1.000419048 0.979467 1.021819
2 NO 1.010419048 0.179467 1.121819
0 HTN . . . .
2 YES 0.790764015 0.737 0.988322
2 NO 0.790764015 0.637 0.988322
;
run;
/*--Replace '.' in subgroup with blank--*/
data forest2;
set forest;
subgroup=translate(subgroup, ' ', '.');
val=mod(_N_-1, 6);
indent=ifn(indent eq 2, 1, 0);
if val eq 1 or val eq 2 or val eq 3 then ref=subgroup;
run;
/*--Create font with smaller fonts for axis label, value and data--*/
proc template;
define style listingSF;
parent = Styles.Listing;
style GraphFonts from GraphFonts
"Fonts used in graph styles" /
'GraphDataFont' = (", ",7pt)
'GraphValueFont' = (", ",6pt)
'GraphLabelFont' = (", ",6pt, bold);
end;
run;
/*--Define templage for Forest Plot--*/
/*--Template uses a Layout Lattice of 6 columns--*/
proc template;
define statgraph Forest;
dynamic _show_bands _color _thk;
begingraph;
entrytitle 'Forest Plot of Hazard Ratios by Patient Subgroups ';
discreteattrmap name='text';
value '0' / textattrs=(weight=bold);
value other;
enddiscreteattrmap;
discreteattrvar attrvar=type var=indent attrmap='text';
layout lattice / columns=2 columnweights=(0.25 1.0);
/*--First Subgroup column, shows only the Y2 axis--*/
layout overlay / walldisplay=none xaxisopts=(display=none)
yaxisopts=(reverse=true display=none
tickvalueattrs=(weight=bold));
referenceline y=ref / lineattrs=(thickness=_thk color=_color);
axistable y=subgroup value=subgroup / indentweight=indent textgroup=type;
endlayout;
/*--Third column showing odds ratio graph--*/
layout overlay / xaxisopts=(TYPE=LOG label=' <---PCI Better---- ----Medical Therapy Better--->'
linearopts=(tickvaluepriority=true
tickvaluelist=(0.0 0.5 1.0 1.5 2.0 2.5)))
yaxisopts=(reverse=true display=none) walldisplay=none;
referenceline y=ref / lineattrs=(thickness=_thk color=_color);
scatterplot y=subgroup x=HR / xerrorlower=LCL xerrorupper=UCL
markerattrs=(symbol=squarefilled);
referenceline x=1;
endlayout;
endlayout;
entryfootnote halign=left textattrs=(size=7)
'The p-value is from the test statistic for testing the interaction between the '
'treatment and any subgroup variable';
entryfootnote halign=left 'This graph uses the new AXISTABLE plot to display the textual columns';
endgraph;
end;
run;
/*--Need format to show missing as blank--*/
proc format;
value misblank
. = ' ';
run;
/*----Create Graph-----*/
ods listing style=htmlblue gpath=&graphs image_dpi=&dpi;
ods graphics / reset noscale width=&w height=&h imagename='GTL_ForestPlot';
proc sgrender data=Forest2 template=Forest;
format pvalue group pcigroup misblank7.2;
dynamic _color='cxf0f0f0' _thk=12;
run;
The forest plot should give YES and NO under HTN and Obesity but it doesn't. Any tip would be appreciated.

Overlaying time series for individuals and mean values in a single graph using SAS SGPLOT

I am comparing the evolution of plasma concentrations over time for different treatments of patients.
We applied each treatment to different subjects and for each treatment we want a graph with the evolution for each subject in black, as well as for the the mean in red.
It should look like this
but it does look like this
My data has variable
trtan and trta for treatment number and name
subjid for the patient receiving that treatment
ATPT for timepoint
AVAL for Individual Concentrations
MEAN for average Concentrations
I am using SGPLOT to produce this line plot. y axis has concentrations while x axis has time points, I am sorting data by treatment, subject and timepoint before passing to Proc SGPLOT.
Lines for indivizual subjects are fine, Issue is with mean line plot, Since dataset is sorted by subject i am getting multiple mean plots by subject as well.
My requirement is to have multiple indivizual plots and an overlaying mean plot. Can anyone advise how can i solve this.
I am using below code. How can I repair it?
proc sort data = pc2;
by trtan trta subjid atptn atpt;
run;
proc sgplot data = pc2 dattrmap = anno pad = (bottom = 20%) NOAUTOLEGEND ;
by trtan trta;
series x = atptn y = aval/ group = trta
lineattrs = (color = black thickness = 1 pattern = solid );
series x = atptn y = mean/ group = trta attrid = trtcolor
lineattrs = (thickness = 2 pattern = solid );
xaxis label= "Actual Time (h)"
labelattrs = (size = 10)
values = (0 12 24 36 48 72 96 120 168)
valueattrs = (size = 10)
grid;
yaxis label= "Plasma Concentration (ng/mL)"
labelattrs = (size = 10)
valueattrs = (size = 10)
grid;
run;
This is not a problem with the mean only.
Leave out the mean, ass min=-20 to your yaxis specification, and you will see the same problem.
Alternatively run this code
data pc2;
do subj = 1 to 3;
do time = 1 to 25;
value = 2*sin(time/3) + rand('Normal');
output;
end;
end;
run;
proc sgplot data=pc2;
series x=time y=value;
run;
and you will get
The solution is to have one plot for each subject, so first sort the data by time and transpose it to have one variable subj_1 etc. for each subject.
proc sort data=pc2 out=SORTED;
by time subj;
run;
proc transpose data=TEST out=TRANS prefix=subj_;
by time;
id subj;
run;
I leave it as an exercise for you to add the mean to this dataset.
Then run sgplot with a series statement per subject. To build these statements, we interrogate the meta data in dataset WORK.TRANS
proc sql;
select distinct 'series x=time y='|| name ||'/lineattrs = (color=black)'
into :series_statements separated by ';'
from sasHelp.vColumn
where libname eq 'WORK' and memName eq 'TRANS'
and (name like 'subj%' or name = mean;
quit;
proc sgplot data=TRANS;
&series_statements;
run;
The result, without the mean, looks like this for my example:
Of course, you will have to do some graphical fine tuning.
We can achive it simply by taking the mean by ATPT and then instead of merging the mean record to the PK data by ATPT, you need to append the records and then you can run your code and it will give you the result you are expecting, please let me know if it does not work, it seems to have worked for me.

Proc tabulate,in my column i have more than 50 variable how can i reduce it to only 5?

proc tabulate data=D.Arena out=work.Arena ;
class Row1 Column1/ order=freq ;
table Row1,Column1 ;
run;
after running this i received these results and now i want to restrict the columns to only 5 variables
Use a 'where' statement to restrict the col1 values being tabulated.
You can restrict based on a value property such as starts with the letter A
where col1 =: 'A';
You can restrict based on a value list:
where col1 in ('Apples', 'Lentils', 'Oranges', 'Sardines', 'Cucumber');
Sample data:
data have;
call streaminit(123);
array col1s[50] $20 _temporary_ (
"Apples" "Avocados" "Bananas" "Blueberries" "Oranges" "Strawberries" "Eggs" "Lean beef" "Chicken breasts" "Lamb" "Almonds" "Chia seeds" "Coconuts" "Macadamia nuts" "Walnuts" "Asparagus" "Bell peppers" "Broccoli" "Carrots" "Cauliflower" "Cucumber" "Garlic" "Kale" "Onions" "Tomatoes" "Salmon" "Sardines" "Shellfish" "Shrimp" "Trout" "Tuna" "Brown rice" "Oats" "Quinoa" "Ezekiel bread" "Green beans" "Kidney beans" "Lentils" "Peanuts" "Cheese" "Whole milk" "Yogurt" "Butter" "Coconut oil" "Olive oil" "Potatoes" "Sweet potatoes" "Vinegar" "Dark chocolate"
);
do row1 = 1 to 20;
do _n_ = 1 to 1000;
col1 = col1s[ceil(rand('uniform',50))];
x = ceil(rand('uniform',250));
output;
end;
end;
run;
Frequency tabulation, also showing ALL counts
* col1 values shown in order by value;
proc tabulate data=have;
class row1 col1;
table ALL row1,col1;
run;
* col1 values shown in order by ALL frequency;
proc tabulate data=have;
class row1;
class col1 / order=freq;
table ALL row1,col1;
run;
* Letter T col1 values shown in order by ALL frequency;
proc tabulate data=have;
where col1 =: 'T';
class row1;
class col1 / order=freq;
table ALL row1,col1;
run;
A top 5 only list of Col1s would require a step that determines which col1s meet that criteria. A list of those col1s can be used as part of a where in clause.
* determine the 5 col1s with highest frequency count;
proc sql noprint outobs=5;
select
quote(col1) into :top5_col1_list separated by ' '
from
( select col1, count(*) as N from have
group by col1
)
order by N descending;
quit;
proc tabulate data=have;
where col1 in (&top5_col1_list);
class row1;
class col1 / order=freq;
table ALL row1,col1;
run;
Col1s in order of value
Col1s in order of frequency
T Col1s
Top 5 Col1s

Merge cells horizontally in RTF output using proc report

I am trying to create a summary row above each group in my data. I have 2 questions:
How do I merge the first 2 cells horizontally (the ones in red below) in the summary rows.
How do I remove the duplicated F and M in the Sex column (at the moment I can work around this by changing only those cell's text colours to white, but hopefully there's a better way)
The output is an RTF file, and I'm using SAS 9.4 - the desktop version.
Is this possible using proc report?
Code:
options missing=' ';
proc report data=sashelp.class nowd;
columns sex name age weight;
define sex / order;
break before sex / summarize;
run;
I don't think you can merge cells in the summarize line.
Some trickery with compute blocks and call define can alter the cell values and appearances.
For example (Just J names for smaller image):
proc report data=sashelp.class nowd;
where name =: 'J';
columns sex name age weight;
define sex / order;
define age / sum;
define weight / sum;
break before sex / summarize style=[verticalalign=bottom];
compute name;
* the specification of / order for sex sets up conditions in the name value
* that can be leveraged in the compute block;
if name = ' ' then do;
* a blank name means the current row the compute is acting on
* is the summarization row;
* uncomment if stat is not obvious or stated in title;
* name = 'SUM';
* 'hide' border for appearance of merged cell;
call define (1, 'style', 'style=[fontsize=18pt borderrightcolor=white]');
end;
else do;
* a non-blank name means one of the detail rows is being processed;
* blank out the value in the sex column of the detail rows;
* the value assignment can only be applied to current column or those
* to the left;
sex = ' ';
end;
endcomp;
compute after sex;
* if you want more visual separation add a blank line;
* line ' ';
endcomp;
run;

Using SAS I want to print the 5 rows with the highest value in a certain column

I have a dataset with several columns. I can get the extreme observations for a column or set of columns like this ...
PROC Univariate data = Work.tempVal
nextrobs = 5 ;
ods select ExtremeObs ;
ods output ExtremeObs = ExtremeObs;
var B C;
run;
What I would like to do is print out the dataset row for each one of the extreme observations. So I am getting the column that I am targeting for extremity but I want the rest of the columns as well.
Turns out the id keyword includes other columns
So ...
PROC Univariate data = Work.tempVal
nextrobs = 5 ;
ods select ExtremeObs ;
ods output ExtremeObs = ExtremeObs;
var B;
id A C D;
run;
will return columns A, B, C, and D where B is an extreme observation.
You can use proc sql with the outobs= option, and the appropriate sort order.
For example, to get the rows with the top 5 maximum values :
proc sql outobs=5 ;
create table top5 as
select *
from mydata
order by targetvar descending ;
quit ;
Obviously, if you've got multiple rows with the same maximum value, you may want to use a different approach.