Hi,
I am creating a stack bar plot using PROC TEMPLATE and SGRENDER.
However, I noticed that the legend values are in the alphabetical order, while I would like them to be in a the 'natural" order.
I.e., White, Black, Other not White, Other, Black OR Black, Other, White.
Basically, is there a way to control the values of the legend?
I tried using numeric values associated with my race group, but then I don't want to display 1,2,3 in the legend.
When I format the 1,2,3 to represent White, Black, Other...the legend values are once again alphabetized.
Thank you!
data clusterbarstack;
length cluster $9 stack $5;
input year cluster $ stack $ pct num_stack;
label pct="Patients (%)";
datalines;
2010 A White 90 1
2010 A Black 8 2
2010 A Other 2 3
2010 B White 85 1
2010 B Black 15 2
2010 B Other 0 3
2010 C White 50 1
2010 C Black 45 2
2010 C Other 5 3
2011 A White 92 1
2011 A Black 7 2
2011 A Other 1 3
2011 B White 85 1
2011 B Black 12 2
2011 B Other 3 3
2011 C White 51 1
2011 C Black 40 2
2011 C Other 9 3
;
run;
proc template;
define statgraph delete;
DYNAMIC _WIDTH _HEIGHT
_XAXIS _YAXIS _CLUSTER _STACK
_YSTART _YEND _YINC _XSTART _XEND _XINC _YFMT _XFMT _YLO _YHI _XLO _XHI;
begingraph / designwidth=_WIDTH designheight=_HEIGHT;
layout gridded;
layout datalattice columnvar=_XAXIS / headerlabeldisplay=value columnheaders=bottom border=false
columngutter=14
rowaxisopts=(offsetmin=0 offsetmax=0 display=(ticks tickvalues label) displaysecondary=none)
row2axisopts=(offsetmin=0 offsetmax=0 display=none)
columnaxisopts=(display=(line ticks tickvalues));
layout prototype / cycleattrs=true walldisplay=none;
barchart x=_CLUSTER y=_YAXIS / name="leg" group=_STACK GROUPORDER = deSCENDING ;
endlayout;
endlayout;
discretelegend "leg" /location=outside /*VALIGN = BOTTOM;**/ across=1 halign=right valign=top opaque=true border=false sortorder=ASCENDINGFORMATTED
;
endlayout;
endgraph;
end;
run;
proc sgrender data=clusterbarstack template=delete;
DYNAMIC _WIDTH="2400px" _HEIGHT="1200px"
_XAXIS="year" _YAXIS="pct" _CLUSTER="cluster"
/* _STACK="stack";*/
_STACK="num_stack";
run;
proc format;
value race 1 = "White" 2 = "Black" 3 = "Other";
run;
*apply format;
proc sgrender data=clusterbarstack template=delete;
DYNAMIC _WIDTH="2400px" _HEIGHT="1200px"
_XAXIS="year" _YAXIS="pct" _CLUSTER="cluster"
/* _STACK="stack";*/
_STACK="num_stack";
format num_stack race.;
run;
I did this using attrmap as I had a character variable and wanted a specific order. code snippet below.
discreteattrmap name="colorbysign" / ignorecase=true;
value "PD " /
fillattrs=GraphData1(color=red )
markerattrs=GraphData1(color=red )
lineattrs=GraphData1(color=red );;
value "SD " /
fillattrs=GraphData1(color=blue )
markerattrs=GraphData1(color=blue )
lineattrs=GraphData1(color=blue ); ;
value "PR " /
fillattrs=GraphData1(color=orange )
markerattrs=GraphData1(color=orange )
lineattrs=GraphData1(color=orange ); ;
value "CR " /
fillattrs=GraphData1(color=green )
markerattrs=GraphData1(color=green )
lineattrs=GraphData1(color=green );;
value "NE " /
fillattrs=GraphData1(color=grey )
markerattrs=GraphData1(color=grey )
lineattrs=GraphData1(color=grey ); ;
enddiscreteattrmap;
legenditem type=line name="PD" / LABEL='PD' lineattrs=GraphData1(color=red THICKNESS=10)
labelattrs=(size=9 family="Arial") ;
legenditem type=line name="SD" / LABEL='SD' lineattrs=GraphData1(color=blue THICKNESS=10)
labelattrs=(size=9 family="Arial");;
legenditem type=line name="PR" / LABEL='PR' lineattrs=GraphData1(color=orange THICKNESS=10)
labelattrs=(size=9 family="Arial") ;
legenditem type=line name="CR" / LABEL='CR' lineattrs=GraphData1(color=green THICKNESS=10)
labelattrs=(size=9 family="Arial");;
legenditem type=line name="NE" / LABEL='NE' lineattrs=GraphData1(color=grey THICKNESS=10)
labelattrs=(size=9 family="Arial");;
discreteattrvar attrvar=signvar var=_var attrmap="colorbysign";
discretelegend "PD" "SD" "PR" "CR" "NE" / title=_label titleattrs=(size=9 family="Arial") BORDER=false;
Some example data would help. Also which version of SAS as the capabilities in SG graphics have changed in each recent release? If you're running a SAS 9.2 and get solutions requiring SAS 9.4 it will likely not help.
There is example data, right after I wrote thank you..
I am using SAS 9.3
Anca
Normally, legend items are in the order reported by each plot. However, as you have set SORTORDER on the DiscreteLegend, the entries will be in the order you have set.
Understood,
however, with or without the SORTORDER option, the legend values can only be ORDERED based on the alphabetic values - descending or ascending.
Can one modified the values of the legend to be in some very specific order - once again, not alphabetically.
Anca
A dirty version would be to create a numeric variable with values in the order you want AND a custom format for the text. Most of these procedures will use the order of the value but display the formatted value unless you specify to sort by the formatted value.
I am doing that,
but unless I apply a format like this:
proc format;
value race 1 = "1.White" 2 = "2.Black" 3 = "3.Other";
run;
where I sneak a digit for my preferred format.
So, bottom line is that the values cannot be ordered specifically.
Anca.
If you set GROUPORDER=DATA on the BARCHART and remove the sort order on the legend, you should get the legend in natural order.
Plots will normally report group values in the order they are encountered in the data. So, if you put them in the order you want, that should be the order in the legend. There is no way to request a custom order in the legend. Only alphabetical sorts are supported by sortorder. Using attr maps or index, you can put groups in your order, and still get the colors you want.
Sanjay,
I was afraid I may have to use the attr maps - which I used once for a very specific example.
Do you think you could provide some example code?
Thank you.
Anca.
Anca, sometimes, i put dummy observations with the group values in the order i want at the front of the data with missing values for other columns. This ensures the groups I want are in the data in the order I want. The missing values ensures the obs are not drawn. This is also a good way to get all group values into the legend even when some of them may not be in the data today.
I did this using attrmap as I had a character variable and wanted a specific order. code snippet below.
discreteattrmap name="colorbysign" / ignorecase=true;
value "PD " /
fillattrs=GraphData1(color=red )
markerattrs=GraphData1(color=red )
lineattrs=GraphData1(color=red );;
value "SD " /
fillattrs=GraphData1(color=blue )
markerattrs=GraphData1(color=blue )
lineattrs=GraphData1(color=blue ); ;
value "PR " /
fillattrs=GraphData1(color=orange )
markerattrs=GraphData1(color=orange )
lineattrs=GraphData1(color=orange ); ;
value "CR " /
fillattrs=GraphData1(color=green )
markerattrs=GraphData1(color=green )
lineattrs=GraphData1(color=green );;
value "NE " /
fillattrs=GraphData1(color=grey )
markerattrs=GraphData1(color=grey )
lineattrs=GraphData1(color=grey ); ;
enddiscreteattrmap;
legenditem type=line name="PD" / LABEL='PD' lineattrs=GraphData1(color=red THICKNESS=10)
labelattrs=(size=9 family="Arial") ;
legenditem type=line name="SD" / LABEL='SD' lineattrs=GraphData1(color=blue THICKNESS=10)
labelattrs=(size=9 family="Arial");;
legenditem type=line name="PR" / LABEL='PR' lineattrs=GraphData1(color=orange THICKNESS=10)
labelattrs=(size=9 family="Arial") ;
legenditem type=line name="CR" / LABEL='CR' lineattrs=GraphData1(color=green THICKNESS=10)
labelattrs=(size=9 family="Arial");;
legenditem type=line name="NE" / LABEL='NE' lineattrs=GraphData1(color=grey THICKNESS=10)
labelattrs=(size=9 family="Arial");;
discreteattrvar attrvar=signvar var=_var attrmap="colorbysign";
discretelegend "PD" "SD" "PR" "CR" "NE" / title=_label titleattrs=(size=9 family="Arial") BORDER=false;
PERFECTION!
THANK YOU!
Note: With SAS 9.4M1 onwards, GTL and SGRENDER also support Data Set Attribute Maps, just like SG. Now, you can define common attribute maps outside your template, and use them with any template without having to change the template itself by using the DATTRMAP option on the SGRENDER proc statement and the new DATTRVAR statement. See example in blog article: Group order in GTL - Graphically Speaking
Thanks Sanjay!
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.