Morning,
I'm trying to use proc gchart in conjunction with the annotation facility to generate a stacked bar graph with a table annotated below. An example of my desired result is below (produced in Excel):
The closest example I found was at http://support.sas.com/kb/35/774.html so this is what I modeled my code off of. I did get my data set up and was able to make the example code work and produce a graph similar to what is in the preceding link (two bars per response variable with the actual response values displayed in a grid under the bar graph). That result is below:
I then began adjusting my code to create stacked bars and got my table frame and legend set up appropriately. The issue I am running into now is that the response values are scrunched to the left of my annotated table (see highlighted portion):
My code is below. This is not complete (i.e. the pieces that define the raw data behind the graph are not included for brevity). I mainly need help with the /*Populate the table */ portion and curious if anyone sees anything obvious that I need to adjust to get my data annotations correctly displayed in the table. Or perhaps my gchart code is not set up correctly to do what I want. If needed I can re-post code to facility reproducibility.
data impact&spectype._forprint;
length mid $15.;
set impact&spectype._forprint;
if dx ne 'Total' then do;
response=&spectype.totreq; mid='totreq'; output; *use to display N in annotated data;
response=totremain; mid='totremain'; output; *use to display N in annotated data;
response=pctrequest; mid='pctrequest'; output; *use for % in y-axis of stacked bar;
response=pctremain; mid='pctremain'; output; *use for % in y-axis of stacked bar;
end;
keep dx response mid totremain &spectype.totreq pctrequest pctremain n;
run;
proc sort; by dx mid; run;
data impact&spectype._anno;
set impact&spectype._forprint;
by dx mid;
length function color $8 text $20 style $ 20;
/* Populate the table */
if first.dx then do;
function='move'; xsys='2'; ysys='1';
midpoint=mid; group=dx; y=0;
output;
function='cntl2txt'; output;
function='label'; xsys='A'; ysys='3';
x=+3; y=17.25;
text=trim(left(put(&spectype.totreq,8.1)));
color='black'; position='+'; when='a';
output;
function='move'; xsys='2'; ysys='1';
midpoint=mid; group=dx; y=0;
output;
function='cntl2txt'; output;
function='label'; xsys='A'; ysys='3';
x=+3; y=13.75;
text=trim(left(put(totremain,8.1)));
color='black'; position='+'; when='a';
output;
end;
/* Generate the table frame */
function='move'; xsys='3'; ysys='3';
x=3; y=12;
output;
function='bar'; xsys='1'; ysys='3';
x=100; y=19;
style='empty'; color='black'; line=0;
output;
/* Generate the row headers */
function='label'; xsys='3'; ysys='3';
style='marker'; text='U'; color='cxde7e6f';
x=4; y=17.25; position='6';
output;
function='label'; xsys='3'; ysys='3';
style="Albany AMT"; text='Total requested'; color='black';
x=7; y=17.5; position='6';
output;
function='label'; xsys='3'; ysys='3';
style='marker'; text='U'; color='cx7c95ca';
x=4; y=13.75; position='6';
output;
function='label'; xsys='3'; ysys='3';
style="Albany AMT"; text='Total remaining'; color='black';
x=7; y=14; position='6';
output;
/* Generate the vertical lines in the table */
function='move'; xsys='1'; ysys='1';
x=0; y=0;
output;
function='draw'; xsys='1'; ysys='3';
x=0; y=12;
line=1; color='black';
output;
function='move'; xsys='1'; ysys='1';
x=100; y=0;
output;
function='draw'; xsys='1'; ysys='3';
x=100; y=12;
line=1; color='black';
output;
/* Generate the horizontal line in the table */
function='move'; xsys='3'; ysys='3';
x=3; y=15.5;
output;
function='draw'; xsys='1'; ysys='3';
x=100; y=15.5;
line=1; color='black';
output;
run;
%mend impact;
%impact (spectype=frozen);
ods graphics;
title1 font='arial' height=10pt "Total frozen aliquots requested for R050, by final clinical diagnosis";
axis1 label=(a=90 '% Aliquots')
order=(80 to 100 by 2)
minor=none;
axis2 label=none
value=none
origin=(20pct,22pct)
offset=(4pct,4pct);
axis3 label=none;
footnote1 h=.5 ' ';
pattern1 value=solid color=cx7c95ca;
pattern2 value=solid color=cxde7e6f;
ods pdf file="X:\XXX\COMMON\Requests\test.pdf";
proc gchart data=impactfrozen_forprint (where=(mid in ('pctremain','pctrequest')));
vbar dx / sumvar=response subgroup=mid group=dx g100 nozero
coutline=black
space=0 gspace=5 width=4
cframe=white autoref clipref
raxis=axis1 maxis=axis2 gaxis=axis3 nolegend
annotate=impactfrozen_anno;
run; quit;
ods pdf close;
Here's one way to do it with gchart and annotate:
%let name=bar_table; filename odsout '.'; goptions device=png; goptions border; ODS LISTING CLOSE; ODS HTML path=odsout body="&name..htm" style=htmlblue; goptions gunit=pct htitle=6 ftitle="albany amt/bold" htext=4.25 ftext="albany amt"; gopitons ctext=gray33; data dx; input nmid $ pctmid $ dx $ nresponse pctresponse; datalines; totreq pctreq COPD 18 0.20 totrem pctrem COPD 8800 99.80 totreq pctreq Control 5 0.52 totrem pctrem Control 949 99.48 totreq pctreq ILD 0 0 totrem pctrem ILD 8124 100 totreq pctreq Other 200 12.93 totrem pctrem Other 1546 87.07 ; run; data dx; set dx; format pctresponse percentn7.0; pctresponse=pctresponse/100; run; data anno_values; set dx; xsys='2'; ysys='3'; hsys='3'; when='a'; function='label'; position='5'; xc=dx; if nmid='totreq' then do; y=15; text=trim(left(nresponse)); output; end; if nmid='totrem' then do; y=7; text=trim(left(nresponse)); output; end; run; data anno_lines; length function $8 color $8 style $35; when='b'; color='cx989ea1'; /* vertical lines */ xsys='1'; x=0; ysys='1'; y=0; function='move'; output; xsys='1'; x=0; ysys='3'; y=2; function='draw'; output; xsys='1'; x=25; ysys='1'; y=0; function='move'; output; xsys='1'; x=25; ysys='3'; y=2; function='draw'; output; xsys='1'; x=50; ysys='1'; y=0; function='move'; output; xsys='1'; x=50; ysys='3'; y=2; function='draw'; output; xsys='1'; x=75; ysys='1'; y=0; function='move'; output; xsys='1'; x=75; ysys='3'; y=2; function='draw'; output; xsys='1'; x=100; ysys='1'; y=0; function='move'; output; xsys='1'; x=100; ysys='3'; y=2; function='draw'; output; xsys='3'; x=3; ysys='3'; y=18; function='move'; output; xsys='3'; x=3; ysys='3'; y=2; function='draw'; output; /* horizontal lines */ xsys='3'; x=3; ysys='3'; y=18; function='move'; output; xsys='1'; x=100; ysys='3'; y=18; function='draw'; output; xsys='3'; x=3; ysys='3'; y=10; function='move'; output; xsys='1'; x=100; ysys='3'; y=10; function='draw'; output; xsys='3'; x=3; ysys='3'; y=2; function='move'; output; xsys='1'; x=100; ysys='3'; y=2; function='draw'; output; /* white-out some of the lines behind the 'Frozen' label */ xsys='1'; x=10; ysys='3'; y=18.1; function='move'; output; xsys='1'; x=90; ysys='3'; y=27; function='box'; style='solid'; color='white'; output; /* labels to left of table */ function='label'; position='6'; color=''; style=''; xsys='3'; x=10; ysys='3'; y=15; text='Total requested'; output; xsys='3'; x=10; ysys='3'; y=7; text='Total remaining'; output; style='marker'; text='U'; xsys='3'; x=5; ysys='3'; y=15-.5; color="cxc6514a"; output; xsys='3'; x=5; ysys='3'; y=7-.5; color="cx4a82bd"; output; run; data anno_all; set anno_lines anno_values; run; pattern1 v=s c=cx4a82bd; pattern2 v=s c=cxc6514a; axis1 label=none order=(0 to 1 by 1) minor=none offset=(0,0); axis2 label=('Frozen') order=('ILD' 'COPD' 'Control' 'Other'); title1 a=90 h=20 ' '; /* blank space to the left of the bar chart */ footnote1 height=15pct ' '; /* blank space for the annotated table */ proc gchart data=dx anno=anno_all; vbar dx / type=sum sumvar=pctresponse subgroup=nmid nolegend raxis=axis1 maxis=axis2 coutline=gray77; run; quit; ODS HTML CLOSE; ODS LISTING;
Move to the modern graphing system - sgplot/GTL. Examples:
https://blogs.sas.com/content/?s=stacked
And you will find everything you ever wanted to know about graphs at:
Thanks. I think I found a solution. Here is some sample code I developed that is getting me to where I need to be (just need to add labels):
data dx;
input nmid $ pctmid $ dx $ nresponse pctresponse;
datalines;
totreq pctreq COPD 18 0.20
totrem pctrem COPD 8800 99.80
totreq pctreq Control 5 0.52
totrem pctrem Control 949 99.48
totreq pctreq ILD 0 0
totrem pctrem ILD 8124 100
totreq pctreq Other 200 12.93
totrem pctrem Other 1546 87.07
;
run; proc sort; by dx descending pctresponse; run;
ods graphics;
ods pdf file="X:\test1.pdf";
proc sgplot data=dx;
title 'Frozen specimens requested';
vbarparm category=dx response=pctresponse / group=pctmid dataskin=gloss;
xaxistable nresponse / class=nmid label labelpos=left location=inside;
xaxis display=(nolabel);
keylegend / location=inside position=topright;
yaxis offsetmax=0.1;
run;
ods pdf close;
Here's one way to do it with gchart and annotate:
%let name=bar_table; filename odsout '.'; goptions device=png; goptions border; ODS LISTING CLOSE; ODS HTML path=odsout body="&name..htm" style=htmlblue; goptions gunit=pct htitle=6 ftitle="albany amt/bold" htext=4.25 ftext="albany amt"; gopitons ctext=gray33; data dx; input nmid $ pctmid $ dx $ nresponse pctresponse; datalines; totreq pctreq COPD 18 0.20 totrem pctrem COPD 8800 99.80 totreq pctreq Control 5 0.52 totrem pctrem Control 949 99.48 totreq pctreq ILD 0 0 totrem pctrem ILD 8124 100 totreq pctreq Other 200 12.93 totrem pctrem Other 1546 87.07 ; run; data dx; set dx; format pctresponse percentn7.0; pctresponse=pctresponse/100; run; data anno_values; set dx; xsys='2'; ysys='3'; hsys='3'; when='a'; function='label'; position='5'; xc=dx; if nmid='totreq' then do; y=15; text=trim(left(nresponse)); output; end; if nmid='totrem' then do; y=7; text=trim(left(nresponse)); output; end; run; data anno_lines; length function $8 color $8 style $35; when='b'; color='cx989ea1'; /* vertical lines */ xsys='1'; x=0; ysys='1'; y=0; function='move'; output; xsys='1'; x=0; ysys='3'; y=2; function='draw'; output; xsys='1'; x=25; ysys='1'; y=0; function='move'; output; xsys='1'; x=25; ysys='3'; y=2; function='draw'; output; xsys='1'; x=50; ysys='1'; y=0; function='move'; output; xsys='1'; x=50; ysys='3'; y=2; function='draw'; output; xsys='1'; x=75; ysys='1'; y=0; function='move'; output; xsys='1'; x=75; ysys='3'; y=2; function='draw'; output; xsys='1'; x=100; ysys='1'; y=0; function='move'; output; xsys='1'; x=100; ysys='3'; y=2; function='draw'; output; xsys='3'; x=3; ysys='3'; y=18; function='move'; output; xsys='3'; x=3; ysys='3'; y=2; function='draw'; output; /* horizontal lines */ xsys='3'; x=3; ysys='3'; y=18; function='move'; output; xsys='1'; x=100; ysys='3'; y=18; function='draw'; output; xsys='3'; x=3; ysys='3'; y=10; function='move'; output; xsys='1'; x=100; ysys='3'; y=10; function='draw'; output; xsys='3'; x=3; ysys='3'; y=2; function='move'; output; xsys='1'; x=100; ysys='3'; y=2; function='draw'; output; /* white-out some of the lines behind the 'Frozen' label */ xsys='1'; x=10; ysys='3'; y=18.1; function='move'; output; xsys='1'; x=90; ysys='3'; y=27; function='box'; style='solid'; color='white'; output; /* labels to left of table */ function='label'; position='6'; color=''; style=''; xsys='3'; x=10; ysys='3'; y=15; text='Total requested'; output; xsys='3'; x=10; ysys='3'; y=7; text='Total remaining'; output; style='marker'; text='U'; xsys='3'; x=5; ysys='3'; y=15-.5; color="cxc6514a"; output; xsys='3'; x=5; ysys='3'; y=7-.5; color="cx4a82bd"; output; run; data anno_all; set anno_lines anno_values; run; pattern1 v=s c=cx4a82bd; pattern2 v=s c=cxc6514a; axis1 label=none order=(0 to 1 by 1) minor=none offset=(0,0); axis2 label=('Frozen') order=('ILD' 'COPD' 'Control' 'Other'); title1 a=90 h=20 ' '; /* blank space to the left of the bar chart */ footnote1 height=15pct ' '; /* blank space for the annotated table */ proc gchart data=dx anno=anno_all; vbar dx / type=sum sumvar=pctresponse subgroup=nmid nolegend raxis=axis1 maxis=axis2 coutline=gray77; run; quit; ODS HTML CLOSE; ODS LISTING;
Registration is open! SAS is returning to Vegas for an AI and analytics experience like no other! Whether you're an executive, manager, end user or SAS partner, SAS Innovate is designed for everyone on your team. Register for just $495 by 12/31/2023.
If you are interested in speaking, there is still time to submit a session idea. More details are posted on the website.
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.