Hi,
I am trying to produce a graph from sgpnel. Please see the attatched to see desired output.
the requirement is to display the N count below the X axis. I am finding a major challenge to do annotation in sgpanel output.
Initially, I displayed N count along the X axis using colaxistable statement in proc sgpanel procedure. but this option will provide us N count inside the graph area, which customer don't want now.
1) Are we able to do annotations to get N count below X axis in sgpanel procedure.
Please let me know if you need more information.
Any input will help me.
Thanks in advance.
Thinking about it again, to take advantage of LAYOUT LATTICE you can also use the annotate facility to create the N entries outside. See below for an example, it uses the same data for plotting additionally also an annotate dataset is create to draw the N values (in red).
/*
* make SGanno macro available
*/
%sganno
data anno_a;
set group_a_stat end=eod;
%SGTEXT(
id="GROUP_A"
, label=cats(value_a_n)
, xc1=group_a
, x1space="datavalue"
, y1=-7.5
, y1space="wallpercent"
, textcolor="red"
)
if eod = 1 then do;
%SGTEXT(
id="GROUP_A"
, label="label"
, x1=0
, x1space="wallpercent"
, y1=-7.5
, y1space="wallpercent"
, anchor="right"
, border="true"
, widthunit="percent"
, width=150
)
end;
run;
data anno_b;
set group_b_stat end=eod;
%SGTEXT(
id="GROUP_B"
, label=cats(value_b_n)
, xc1=group_b
, x1space="datavalue"
, y1=-7.5
, y1space="wallpercent"
, textcolor="red"
)
if eod = 1 then do;
%SGTEXT(
id="GROUP_B"
, label="label"
, x1=0
, x1space="wallpercent"
, y1=-7.5
, y1space="wallpercent"
, anchor="right"
, border="true"
, textcolor=""
, widthunit="percent"
, width=150
)
end;
run;
data anno;
set anno_a anno_b;
run;
/*
* create your own GTL
*/
proc template;
define statgraph two_vbox;
dynamic _ticklist_;
begingraph / ;
layout lattice / columns=2 rowdatarange=union columndatarange=unionall ;
column2headers;
entry "Group A colum2headers";
entry "Group B colum2headers";
endcolumn2headers;
layout overlay /
pad=(bottom=5pct)
xaxisopts=(type=Discrete discreteOpts=(sortOrder=ascendingFormatted)
display=(line ticks tickvalues))
;
entry "Group A layout" / valign=top;
BoxPlot X=group_a Y=value_a / primary=true LegendLabel="value_a" NAME="VBOXa";
annotate / id="GROUP_A";
endlayout;
layout overlay /
xaxisopts=(type=Discrete discreteOpts=(sortOrder=ascendingFormatted) display=(line ticks tickvalues))
;
entry "Group A layout" / valign=top;
BoxPlot X=group_b Y=value_b / primary=true LegendLabel="value_b" NAME="VBOXb";
annotate / id="GROUP_B";
endlayout;
endlayout;
endgraph;
end;
run;
proc sgrender data=group_combined template=two_vbox sganno=anno;
run;
I have two treatments.
Since the COLAXISTABLE does not support a LOCATION= option to have it outside and SGPANEL does not support annotate with data, one way is to use the Graph Template Language (GTL) and draw the two groups individually. This also means you have to prepare the data for the two groups using different variable names and combine them into the one data set for Proc SGENDER. Find below a code example, as you can see it is more effort. It is not complete, you will need to make some changes, but it is a start.
/*
* data for group a
*/
data group_a;
set sashelp.cars;
where origin = "Europe";
group_a = type;
value_a = invoice;
keep group_a value_a;
run;
/*
* data for axistable
*/
proc means data=group_a nway noprint;
class group_a;
var value_a;
output out=group_a_stat(drop=_freq_ _type_) median= n=/autoname;
run;
/*
* data for group b
*/
data group_b;
set sashelp.cars;
where origin = "USA";
group_b = type;
value_b = invoice;
keep group_b value_b;
run;
/*
* data for axistable
*/
proc means data=group_b nway noprint;
class group_b;
var value_b;
output out=group_b_stat(drop=_freq_ _type_) median= n=/autoname;
run;
/*
* combine data vertical
*/
data group_combined;
set group_a group_a_stat group_b group_b_stat;
run;
/*
* check GTL for individual graph
*/
proc sgplot data=group_combined tmplout="c:\temp\sgplot_gtl.sas";
vbox value_a / category=group_a;
xaxistable value_a_n / x=group_a;
run;
ods graphics / width=1200 height=900;
ods path
(prepend) work.template (update)
;
/*
* create your own GTL
*/
proc template;
define statgraph two_vbox_axis_outside;
dynamic _ticklist_;
begingraph / collation=binary;
layout lattice / columns=2 rowdatarange=union;
layout lattice / columnweights=preferred rowweights=preferred columndatarange=union columns=1;;
layout overlay / xaxisopts=(type=Discrete display=(line ticks tickvalues));
entry "Group A" ;
BoxPlot X=group_a Y=value_a / primary=true LegendLabel="value_a" NAME="VBOXa";
endlayout;
Layout Overlay / xaxisopts=(discreteOpts=(sortOrder=ascendingFormatted)) xaxisopts=(type=Discrete discreteOpts=(tickValueList=_ticklist_ tickvaluefitpolicy=SplitRotate tickValueListPolicy=Union)) walldisplay=none xaxisopts=(display=none griddisplay=off displaySecondary=none) x2axisopts=(display=none griddisplay=off displaySecondary=none);
AxisTable Value=value_a_N X=group_a / labelPosition=min Display=(Label) label="my label" labeljustify=right;
endlayout;
endlayout;
layout lattice / columnweights=preferred rowweights=preferred columndatarange=union columns=1;;
layout overlay / xaxisopts=(discreteOpts=(sortOrder=ascendingFormatted)) xaxisopts=(type=Discrete discreteOpts=(tickValueList=_ticklist_ tickvaluefitpolicy=SplitRotate tickValueListPolicy=Union)) x2axisopts=(discreteOpts=(sortOrder=ascendingFormatted)) x2axisopts=(type=Discrete discreteOpts=(tickValueList=_ticklist_ tickvaluefitpolicy=SplitRotate tickValueListPolicy=Union));
entry "Group B";
BoxPlot X=group_b Y=value_b / primary=true LegendLabel="value_b" NAME="VBOXb";
endlayout;
Layout Overlay / xaxisopts=(discreteOpts=(sortOrder=ascendingFormatted)) xaxisopts=(type=Discrete discreteOpts=(tickValueList=_ticklist_ tickvaluefitpolicy=SplitRotate tickValueListPolicy=Union)) walldisplay=none xaxisopts=(display=none griddisplay=off displaySecondary=none) x2axisopts=(display=none griddisplay=off displaySecondary=none);
AxisTable Value=value_b_N X=group_b / labelPosition=min Display=(Label) ;
endlayout;
endlayout;
endlayout;
endgraph;
end;
run;
proc sgrender data=group_combined template=two_vbox_axis_outside;
run;
Thinking about it again, to take advantage of LAYOUT LATTICE you can also use the annotate facility to create the N entries outside. See below for an example, it uses the same data for plotting additionally also an annotate dataset is create to draw the N values (in red).
/*
* make SGanno macro available
*/
%sganno
data anno_a;
set group_a_stat end=eod;
%SGTEXT(
id="GROUP_A"
, label=cats(value_a_n)
, xc1=group_a
, x1space="datavalue"
, y1=-7.5
, y1space="wallpercent"
, textcolor="red"
)
if eod = 1 then do;
%SGTEXT(
id="GROUP_A"
, label="label"
, x1=0
, x1space="wallpercent"
, y1=-7.5
, y1space="wallpercent"
, anchor="right"
, border="true"
, widthunit="percent"
, width=150
)
end;
run;
data anno_b;
set group_b_stat end=eod;
%SGTEXT(
id="GROUP_B"
, label=cats(value_b_n)
, xc1=group_b
, x1space="datavalue"
, y1=-7.5
, y1space="wallpercent"
, textcolor="red"
)
if eod = 1 then do;
%SGTEXT(
id="GROUP_B"
, label="label"
, x1=0
, x1space="wallpercent"
, y1=-7.5
, y1space="wallpercent"
, anchor="right"
, border="true"
, textcolor=""
, widthunit="percent"
, width=150
)
end;
run;
data anno;
set anno_a anno_b;
run;
/*
* create your own GTL
*/
proc template;
define statgraph two_vbox;
dynamic _ticklist_;
begingraph / ;
layout lattice / columns=2 rowdatarange=union columndatarange=unionall ;
column2headers;
entry "Group A colum2headers";
entry "Group B colum2headers";
endcolumn2headers;
layout overlay /
pad=(bottom=5pct)
xaxisopts=(type=Discrete discreteOpts=(sortOrder=ascendingFormatted)
display=(line ticks tickvalues))
;
entry "Group A layout" / valign=top;
BoxPlot X=group_a Y=value_a / primary=true LegendLabel="value_a" NAME="VBOXa";
annotate / id="GROUP_A";
endlayout;
layout overlay /
xaxisopts=(type=Discrete discreteOpts=(sortOrder=ascendingFormatted) display=(line ticks tickvalues))
;
entry "Group A layout" / valign=top;
BoxPlot X=group_b Y=value_b / primary=true LegendLabel="value_b" NAME="VBOXb";
annotate / id="GROUP_B";
endlayout;
endlayout;
endgraph;
end;
run;
proc sgrender data=group_combined template=two_vbox sganno=anno;
run;
Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!
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.
Ready to level-up your skills? Choose your own adventure.