Many examples for performing analysis of covariance (ANCOVA) are available in statistical textbooks, conference papers, and blogs. One of the primary interests for researchers and statisticians is to obtain a graphical representation for the fitted ANCOVA model. This post, co-authored with my SAS Technical Support colleague Kathleen Kiernan, demonstrates how to obtain the statistical analysis for a quadratic ANCOVA model by using the GLM procedure and graphical representation by using the SGPLOT procedure.

For the purposes of this blog, the tests for equality of intercepts and slopes have been omitted. For more detailed information about performing analysis of covariance using SAS, see SAS Note 24177, Comparing parameters (slopes) from a model fit to two or more groups.

## Fitting the model

Keep in mind that the statistical methods for ANCOVA are similar whether you have a one-way ANCOVA model or a repeated-measures ANCOVA model. The example data below consists of three treatments, the covariate X and the response value Y. Predicted observations were generated based upon augmenting observations to the training data set that is used to fit the final model.

```data ts132; input trt x y @@; datalines; 1 2.2 3.7 1 3.5 4.7 1 4.1 5.3 1 4.1 5.5 1 7.4 5.7 1 3.0 3.8 1 0.5 1.0 1 2.4 3.3 1 8.4 5.3 1 8.5 5.3 1 8.0 6.1 1 6.3 5.8 2 5.8 7.2 2 9.9 2.5 2 1.2 4.3 2 6.0 6.9 2 5.5 8.0 2 9.7 3.7 2 8.6 5.2 2 5.0 8.0 2 2.6 6.9 2 8.4 4.3 2 3.0 6.8 2 5.2 7.6 3 8.8 5.7 3 9.0 6.1 3 8.8 6.4 3 4.1 9.6 3 2.4 9.4 3 4.1 8.2 3 4.4 10.2 3 8.9 6.0 3 5.3 9.4 3 9.5 5.0 3 1.1 7.5 3 0.1 5.3 ; run; data new; do trt=1 to 3; x=0; y=.; output; x=10; output; end; run; data combo; set ts132 new; run;```

The following statements fit the final model with common slope in the direction of x and unequal quadratic slope in direction of X*X.

```proc glm data=combo; class trt; model y=trt x x*x*trt/solution noint; output out=anew p=predy; ods output parameterestimates=pe; quit;```

The Type III SS displays significance tests for the effects in the model.

The F statistic that corresponds to the TRT effect provides test intercepts (α123=0). The F statistic  that corresponds to X provides test β1=0. The X*X*TRT effect provides test β123=0. The significance levels are very small, indicating that there is sufficient evidence to reject the null hypothesis.

## Displayed next: parameter estimates

You can write the parameter estimates to a data set by using the PARAMETERESTIMATES output object in an ODS OUTPUT statement in order to display the model equations on a graph.

The default ODS graphics from PROC GLM provides the following graphic representation of the model:

However, researchers, managers and statisticians often want to modify the graph to include the model equation, to change thickness of the lines, symbols, titles and so on.

You can use PROC SGPLOT to plot the ANCOVA model and regression equations by using the OUTPUT OUT=ANEW data set from PROC GLM, as shown in this example:

```proc sgplot data=anew; scatter y=y x=x /group=trt; pbspline y=predy x=x / group=trt smooth=0.5 nomarkers; run;```

This code produces the following graph:

You can see that the respective graphs from PROC GLM and PROC SGPLOT look similar.

It is easier to use PROC SGPLOT to enhance graphs rather than to do the same by editing the ODS Graphics Template associated with the GLM procedure.

To color coordinate the respective treatments with the regression model equations and display them in the graph, you can use SG annotation. The TEXT function is used to place the text at specific locations within the graph.

The POLYGON and POLYCONT functions are used to draw a border around the equations. Note that you might need to adjust the values for X1 and Y1 in the annotate data set for your specific output.

```data eqn; set pe(keep=parameter estimate) end=last; retain trt1 trt2 trt3 x x_x1 x_x2 x_x3; length function \$9 textcolor \$30; x1space='datapercent'; y1space='wallpercent'; function='text'; x1=0; anchor='left'; width=120; select (compbl(parameter)); when ('trt 1') trt1=estimate; when ('trt 2') trt2=estimate; when ('trt 3') trt3=estimate; when ('x') do; if estimate <0 then x=cats(estimate,'x'); else x=cats(' + ',estimate,'x'); end; when ('x*x*trt 1') do; if estimate < 0 then x_x1=cats(estimate,'x*x*trt1'); else x_x1=cats(' + ',estimate,'x*x*trt1'); end; when ('x*x*trt 2') do; if estimate < 0 then x_x2=cats(estimate,'x*x*trt2'); else x_x2=cats(' + ',estimate,'x*x*trt2'); end; when ('x*x*trt 3') do; if estimate < 0 then x_x3=cats(estimate,'x*x*trt3'); else x_x3=cats(' + ',estimate,'x*x*trt3'); end; otherwise; end; if last then do; label=cats("Y=",trt1,x,x_x1); y1=3; textcolor="graphdata1:contrastcolor"; output; label=cats("Y=",trt2,x,x_x2); y1=10; textcolor="graphdata2:contrastcolor"; output; label=cats("Y=",trt3,x,x_x3); y1=17; textcolor="graphdata3:contrastcolor"; output; function='polygon'; x1=0; y1=0; linecolor='grayaa'; linethickness=1; output; function='polycont'; y1=21; output; x1=60; output; y1=0; output; end; run; proc sgplot data=anew sganno=eqn; scatter y=y x=x /group=trt; pbspline y=predy x=x / group=trt smooth=0.5 nomarkers; yaxis offsetmin=0.3; run;```

This code results in the following graph with model equations:

You might also want to add a title; change the X or Y-axis label; or change the color, thickness and pattern for the lines as well as the symbol for the data points, as follows:

• Use a TITLE statement to add a title to the graph.
• Use the LABEL= option in the XAXIS and YAXIS statements in PROC SGLPLOT to specify a label for the axis, with the LABELATTRS= option specifying attributes for those labels.
• Use the STYLEATTRS statement in PROC SGPLOT to specify the attributes for the lines and marker symbols.

The following PROC SGPLOT code produces a graph with the customizations described above:

```ods graphics / attrpriority=none; title 'Fitted Plot for Quadratic Covariate with Unequal Slopes'; proc sgplot data=anew sganno=eqn; styleattrs datacontrastcolors=(navy purple darkgreen) datalinepatterns=(1 2 44) datasymbols=(squarefilled circlefilled diamondfilled); scatter y=y x=x /group=trt; pbspline y=predy x=x / group=trt smooth=0.5 nomarkers; yaxis offsetmin=0.3 label='Y value' labelattrs=(size=10pt color=black weight=bold); xaxis label='X value' labelattrs=(size=10pt color=black weight=bold); run;```

Keep in mind that you also need to adjust the color for the annotated text by changing the value for the TEXTCOLOR= variable (in the DATA step) from textcolor="graphdata1:contrastcolor"  to reference the new colors, as shown in this example: textcolor="navy";

The resulting graph, including the modifications, is displayed below.

## Conclusion

Using the output data set from your statistical procedure enables you to take advantage of the functionality of PROC SGPLOT to enhance your output.  This post describes just some of the customizations you can make to your output.

## References

Milliken, George. A., and Dallas E. Johnson. 2002. Analysis of Messy Data, Volume III: Analysis of Covariance. London: Chapman and Hall/CRC.

SAS Institute Inc. 2006. SAS Note 24529, "Modifying axes, colors, labels, or other elements of statistical graphs produced using ODS Graphics." Available at support.sas.com/kb/24/529.html.

The Output Delivery System (ODS) Graphics procedures provide many options to give you control over the look of your output. However, there are times when your output does not look like you thought it would.

This blog discusses how to solve some common output-related problems that we hear about in Technical Support.

All of the examples in this blog relate to creating scatter plots and bar charts from the same data set, SASHELP.CLASS. This data set, included in your SAS® installation, provides information about heights and ages for both male and female students.

## Colors in the output are not as desired

### Using the STYLEATTRS statement

The STYLEATTRS statement enables you to define attributes, such as color, for graphical elements.

In the following SGPLOT procedure example, the STYLEATTRS statement defines the colors for the marker symbols on a scatter plot as either blue or pink:

```proc sgplot data=sashelp.class; styleattrs datacolors=(blue pink); scatter x=age y=height / group=sex markerattrs=(symbol=circlefilled); run;```

However, after you submit the code, the resulting plot does not use the specified colors. Instead, you see blue and red:

When defining colors for graphical elements, the DATACOLORS= option defines the colors for filled areas, and the DATACONTRASTCOLORS= option defines the colors for marker symbols and lines.

Because the scatter plot is creating marker symbols, you have to change the STYLEATTRS statement to use the DATACONTRASTCOLORS= option instead of the DATACOLORS= option. Here is the revised code:

```proc sgplot data=sashelp.class; styleattrs datacontrastcolors=(blue pink); scatter x=age y=height / group=sex markerattrs=(symbol=circlefilled); run;```

Now, when you submit the updated code, the correct colors appear:

You can find more information about the STYLEATTRS statement in the STYLEATTRS section of the SAS® 9.4 ODS Graphics: Procedures Guide, Sixth Edition documentation.

### Using an attribute map

An attribute map enables you to associate specific values for your plot GROUP= variable with specific graphical attributes.

The attribute map is defined in a data set that includes the following:

• an ID variable that contains the name of the attribute map definition
• a VALUE variable that contains the value of the plot statement GROUP= variable
• any other variables for the attributes that you want to define

In the following example, the attribute map BARCOLORS is defined to associate the group value F with the color pink and the group value M with the color blue. Note that the FILLCOLOR variable is also used to define the colors for the bars of the bar chart.

```data attrmap; id='barcolors'; input value \$ fillcolor \$; datalines; F pink M blue ; run; proc format; value \$ genderfmt 'F'='Female' 'M'='Male'; run; proc sgplot data=sashelp.class dattrmap=attrmap; vbar age / response=height group=sex groupdisplay=cluster nooutline attrid=barcolors; format sex \$genderfmt.; run;```

However, the output shows blue and red bars, instead of using the pink and blue values that you specified:

In this case, a format is defined to display the group values as Female and Male. The attribute map associates the group values of F and M with the pink and blue bar colors that you want, so the values do not match.

You need to change the attribute map VALUE variable so that it contains the formatted value of the GROUP= variable. Here is the first part of the code again, highlighted to show where it has changed:

With the updated values, the output now displays the correct colors:

You can find more information about attribute maps in the SG Attribute Maps section of the SAS® 9.4 ODS Graphics: Procedures Guide, Sixth Edition documentation.

## Symbols in the output are not as desired

In the following PROC SGPLOT example, the STYLEATTRS statement defines the colors for the marker symbols on a scatter plot as either blue or pink. Also, the marker symbols should be either filled circles or filled squares:

```ods html style=styles.htmlblue; proc sgplot data=sashelp.class; styleattrs datacontrastcolors=(blue pink) datasymbols=(circlefilled squarefilled); scatter x=age y=height / group=sex; run;```

You submit the code using the STYLES.HTMLBLUE style. The output shows all the symbols as circles, and none are squares:

The ATTRPRIORITY ODS Graphics option determines how attributes are cycled. The default value for the ATTRPRIORITY option is defined in the style that is being used.

The STYLES.HTMLBLUE style sets the default value COLOR for the ATTRPRIORITY option. This COLOR value cycles the symbols through your specified colors before the second symbol is generated.

You want to set the ATTRPRIORITY ODS Graphics option to NONE in an ODS GRAPHICS statement. That ODS GRAPHICS statement then prevents the symbols from cycling through the colors list:

```ods graphics /attrpriority=none; ods html style=styles.htmlblue; proc sgplot data=sashelp.class; styleattrs datacontrastcolors=(blue pink) datasymbols=(circlefilled squarefilled); scatter x=age y=height / group=sex; run;```

In the updated output, squares are now seen in the correct color, pink:

You can read more about how attributes are cycled in the following blog post by Rick Wicklin:
Attrs, attrs, everywhere: The interaction between ATTRPRIORITY, CYCLEATTRS, and STYLEATTRS in ODS graphics

## Annotation is not placed in the output as desired

In this example, you want the resulting scatter plot to contain an oval around the circle that represents the tallest student:

```proc sql; create table maxheight as select height, age from sashelp.class having height=max(height); quit;   data anno; set maxheight; drawspace='datavalue'; function='oval'; x=age; y=height; width=6; height=6; linecolor='red'; run;   proc sgplot data=sashelp.class sganno=anno; scatter x=age y=height; xaxis offsetmax=0.1 offsetmin=0.1; yaxis offsetmax=0.1 offsetmin=0.1; run;```

After you submit the code, you notice that the resulting plot does not include the oval:

If you have used annotation in SAS/GRAPH® software, you might be accustomed to using the X and Y variables in the annotation data set to indicate the location of the annotation. However, ODS Statistical Graphics (SG) annotation uses X1 and Y1 variables for the location of the annotation.

Therefore, you need to change the X and Y variables in the annotation data set to X1 and Y1 instead:

```data anno; set maxheight; drawspace='datavalue'; function='oval'; x1=age; y1=height; width=6; height=6; linecolor='red'; run;```

In the next version of the scatter plot, the oval now appears:

In this example, you want to place a text label next to the circle that represents the tallest student.

```proc sql; create table maxheight as select height, age, name from sashelp.class having height=max(height); quit;   data anno; set maxheight; drawspace='datavalue'; function='label'; x1=age; y1=height; label=name; textsize=10; textcolor='red'; anchor='bottom'; run;   proc sgplot data=sashelp.class sganno=anno; scatter x=age y=height; xaxis offsetmax=0.1 offsetmin=0.1; yaxis offsetmax=0.1 offsetmin=0.1; run;```

However, after you run this code, the output does not include the text label:

Again, if you are a SAS/GRAPH user, you might assume that the LABEL function can place text on a plot. However, ODS SG annotation needs to use the TEXT function instead.

In the previous DATA step, you need to change the FUNCTION variable so that it contains the value TEXT:

```data anno; set maxheight; drawspace='datavalue'; function='text'; x1=age; y1=height; label=name; textsize=10; textcolor='red'; anchor='bottom'; run;```

After you revise the DATA step and resubmit your code, you then see that the text label appears where intended:

You can find more information about SG annotation in the SG Annotation section of the SAS® 9.4 ODS Graphics: Procedures Guide, Sixth Edition documentation.

## Summary

These are just a few examples to demonstrate some of the common output-related problems that we hear about in Technical Support. If your graphical output does not appear as you wanted, consider the options that you are using and make sure that you are using the correct option.

One of the great things about programming with SAS® software is that there are many ways to accomplish the same task. And, since SAS often adds new features that can make a task easier, it's important to stay informed.

This blog shows a few samples of graphs and explains how you can use new functionality to make the old graphs look new again. Over the past several releases, SAS has added more options and procedures for ODS Graphics. While your tried-and-true SAS/GRAPH programs still work, ODS Graphics can create modern-looking graphs with less code, while providing more output options. And, ODS Graphics is part of Base SAS, which means that all of these techniques work in SAS University Edition.

Note: All the graphs in this blog are created using the fifth maintenance release of SAS® 9.4M5 (TS1M5). Not all options are available in prior releases of SAS.

### Adding special symbols on a graph

The following graph is created with the DATA Step Graphics Interface (DSGI), which draws the horizontal bars and airplanes as well as places the text.

However, the DSGI is not supported in releases after SAS® 9.3. In SAS 9.4 and later, you can create a similar graph using the SYMBOLCHAR statement in the SGPLOT procedure. Using this statement in PROC SGPLOT references the hexadecimal value for the airplane symbol, as shown below:

To create this graph with PROC SGPLOT, submit the following code:

```data planes; input month \$ number; xval2=number + 2000; low=0; format number comma8.; cards; Jan 13399 Feb 13284 Mar 14725 Apr 15370 May 16252 Jun 15684 Jul 15313 Aug 16005 ; title1 height=14pt 'Number of Flights at Raleigh Durham International Airport'; title2 height=14pt 'By Month for 2018'; footnote1 height=12pt 'Source: Federal Aviation Administration TFMSC Report (Airport)';       proc sgplot data=planes noautolegend noborder; hbarbasic month / response=number fillattrs=(color=graydd) nooutline barwidth=0.5 baselineattrs=(thickness=0px); symbolchar name=airplane char='2708'x / hoffset=0.3 voffset=0.05; scatter x=number y=month /markerattrs=(symbol=airplane size=60px color=black); scatter x=xval2 y=month / markerchar=number markercharattrs=(size=14pt); xaxis offsetmin=0 display=none; yaxis display=(noline noticks nolabel) valueattrs=(size=14pt) offsetmin=0.025 offsetmax=0.025; run;```

For information about PROC SGPLOT, see SGPLOT Procedure in SAS® 9.4 ODS Graphics: Procedures Guide, Sixth Edition.

For more information about the SYMBOLCHAR statement, see the section "SYMBOLCHAR Statement" in the "SGPLOT Procedure" chapter of SAS® 9.4 ODS Graphics: Procedures Guide, Sixth Edition.

### Assigning colors to data values

The next example graphs show the results for a fictitious ice-cream flavor survey. Because not all the ice cream flavors are present in each survey group, macro code is used to conditionally define the PATTERN statements based on the values in the data.

You can achieve the same more easily by using attribute maps in PROC SGPLOT to associate the attributes, such as color, with data values so that the same color is always associated with the same data value. The following graph, which is similar to the one above, is created using this method:

To create this graph, submit the following code:

```/* Create the input data set ICECREAM */ data icecream; input @1 Flavor \$10. @12 Rank 1. @14 GRP \$1.; datalines; Strawberry 2 B Chocolate 1 B Vanilla 3 B Strawberry 2 A Vanilla 1 A ; run;   proc sort; by grp; run;   data attrmap; id='barcolors'; length value fillcolor linecolor \$10; input value \$ fillcolor \$; linecolor=fillcolor; datalines; Strawberry pink Chocolate CX7B3F00 Vanilla beige ; run; options nobyline; title "Ice Cream Survey for Group #byval(grp)";   proc sgplot data=icecream dattrmap=attrmap noautolegend; by grp; vbar flavor / response=rank group=flavor attrid=barcolors dataskin=pressed; run;```

I changed the colors for the bars in the PROC SGPLOT code so that the bar colors look more like the ice cream that they represent. I also added the DATASKIN= option for the bars to enhance the visual appeal of the bars in the graph.

For more information about attribute maps, see the section Using Attribute Maps to Control Visual Attributes in the SAS® 9.4 ODS Graphics: Procedures Guide, Sixth Edition.

### Combining BY-group graphs into a single page

The following graph shows two plots that are created by using PROC GPLOT with a BY statement. The graphs are then paneled side-by-side with the GREPLAY procedure.

You can use the SGPANEL procedure to create the same plots in side-by-side panels. The benefit to this method is that you need only one procedure both to create the plots and to panel them, as shown below:

To create these paneled plots, submit the following code:

```proc sgpanel data=sashelp.class; panelby sex / novarname rows=1 columns=2; scatter x=age y=height; run;```

### Placing symbols and labels on a map

The next graph uses the Annotate facility with the SAS/GRAPH GMAP and GPROJECT procedures to place a symbol and city name at the location of select cities in North Carolina.

Beginning with the fifth maintenance release of SAS 9.4M5 (TS1M5) in 64-bit Windows and 64-bit Linux operating environments, you can use the SGMAP procedure to create such maps. Using this method, you can create maps that show much more detail.

You can use PROC SGMAP with the OPENSTREETMAP, SCATTER, and TEXT statements to create a similar graph, as shown below:

To create this map, submit the following code:

```data cities; input y x city \$20.; cards; 35.6125 -77.36667 Greenville 36.21667 -81.67472 Boone 35.913064 -79.056112 Chapel Hill ; run;   data dummy; input y2 x2; datalines; 33.857977 -84.321869 36.548759 -75.460423 ;   data cities; set cities dummy; run; title1 h=10pt 'Place points on a map at city locations';   proc sgmap plotdata=cities; openstreetmap; scatter x=x y=y / markerattrs=(color=red size=10px symbol=circlefilled); scatter x=x2 y=y2 / markerattrs=(size=0px); text x=x y=y text=city / textattrs=(size=10pt) position=right; run;```

Because the OPENSTREETMAP statement is used in PROC SGMAP, more detail (for example, cities and roads) is included in the map.

The DUMMY data set adds coordinates to the points that are plotted to modify the display area of the map.

For more information about controlling the display area of the map, see the article How to Control Map Display Area with PROC SGMAP.

For more information about PROC SGMAP, see the SGMAP Procedure chapter in SAS/GRAPH® and Base SAS® 9.4: Mapping Reference.

Many of these features have been covered in more depth within other blog articles. Visit these articles to learn more!
Examples of adding special symbols in your charts using the SYMBOLCHAR statement
Using the new SGMAP procedure to create maps in Base SAS

Making great graphs even better with ODS Graphics was published on SAS Users.

The SGPLOT procedure (as well as other ODS Graphics procedures) does a great job of creating nice- looking output with very little coding. However, there are times when you want to make adjustments to the output's appearance. For those occasions, we have an ATTRS for that!

The statements in PROC SGPLOT include many options that enable you to change the attributes for parts of the plot. Each of these options ends in ATTRS, which makes them easy to find in code.

Before you can change the attributes, you need to know which part of the plot you want to change.  For example, do you want to change the color of the line, the marker symbol, the size of the label font, and so on? Once you know the part of the graph that you want to change, you can search the PROC SGPLOT documentation for an ATTRS option.

In the following PROC SGPLOT code, we have added some ATTRS options to demonstrate the types of changes you can make to a graph.

```proc sgplot data=sashelp.class; vbar age / stat=freq datalabel datalabelattrs=(size=12pt color=blue) fillattrs=(color=cx66A5A0) transparency=0.3 dataskin=matte name='bar' legendlabel='Frequency of age'; vline age / stat=percent markers markerattrs=(symbol=circlefilled color= cx01665E size=12px) lineattrs=(color=cxD05B5B thickness=3px) curvelabel='Percent Line' curvelabelattrs=(size=11pt style=italic) curvelabelloc=inside curvelabelpos=min name='vline' legendlabel='Percent of age' y2axis; refline 4 / axis=y lineattrs=(pattern=2 thickness=2px) label='Refline' labelattrs=(size=12pt) labelpos=min labelloc=inside; xaxis valueattrs=(size=10pt color=navy); yaxis labelattrs=(size=12pt weight=bold) offsetmin=0; keylegend 'bar' 'vline' / title='My legend' titleattrs=(color=blue size=14pt) valueattrs=(size=12pt) noborder; run;```

The figure below shows the graph that is produced by this PROC SGPLOT code. In the figure, some labels are added to help you identify the part of the graph that is modified using an ATTRS option. Note that this graph depicts only some of the ATTRS options that are available. For other ATTRS options, see the SAS® 9.4 ODS Graphics: Procedures Guide, Sixth Edition for the specific plot statement that you want to use.

In this figure:

• The LABELATTRS= option enables you to change the color, font family, font weight, font style, and size for the axis or reference line labels.
• The LINEATTRS= option enables you to change the color, pattern, and thickness for the plot line.
• The CURVELABELATTRS= option enables you to change the color, font family, font weight, font style, and size for the text that is added by the CURVELABEL= option.
• The DATALABELATTRS= option enables you to change the color, font family, font weight, font style, and size for the text that is added by the DATALABEL= option.
• The MARKERATTRS= option enables you to change the color, size, and symbol for the plot markers.
• The FILLATTRS= option enables you to change the color and transparency of the bar colors.
• The VALUEATTRS= option enables you to change the color, font family, font weight, font style, and size for the axis tick-value labels or legend value labels.
• The TITLEATTRS= option enables you to change the color, font family, font weight, font style, and size for the legend title.

For more information about attribute options, see the Commonly Used Attribute Options section of the SAS® 9.4 ODS Graphics: Procedures Guide, Sixth Edition.

The ATTRS options affect all of the output that is produced by that statement. This means that if you include the GROUP= option, all of the groups use the attributes that are specified in the ATTRS options. This behavior is great if you want all of the lines to use the same line pattern, but it can be a problem if you want to specify colors for each of your lines.

Beginning with SAS 9.4, the STYLEATTRS (notice the ATTRS ending) statement is part of the SPLOT (and SGPANEL) procedure to enable you to define attributes for grouped data.

For example, the following code uses the DATACONTRASTCOLORS= option to specify the colors for the marker symbols and the DATASYMBOLS= option to specify the symbols that are to be used.

```ods graphics / attrpriority=none;   proc sgplot data=sashelp.class; styleattrs datacontrastcolors=(pink blue) datasymbols=(circlefilled squarefilled); scatter x=age y=height / group=sex markerattrs=(size=10px); xaxis valueattrs=(size=12pt) labelattrs=(size=14pt); yaxis valueattrs=(size=12pt) labelattrs=(size=14pt); keylegend / valueattrs=(size=12pt) titleattrs=(size=14pt); run;```

You also might need to add the ATTRPRIORITY=NONE option in your ODS GRAPHICS statement to cycle the colors and symbols as expected. For more information about how the attributes are applied to the grouped values, see the How the Attributes Are Cycled section of the SAS® 9.4 ODS Graphics: Procedures Guide, Sixth Edition.

The attributes that are listed in the STYLEATTRS statement are associated with the group values in the order in which they appear in the data set. This behavior can cause the same value to be associated with a different color when you use the same code with another set of data.

To associate an attribute with a specific data value, you can define an attribute map. The attribute map is a data set, referenced in the DATTRMAP= option in the PROC SGPLOT statement, which includes variables that indicate to the SGPLOT procedure how to assign attributes to the group variable values.

Within the attribute map, the ID variable identifies the variables that are specific to a particular set of group values. The VALUE variable identifies the data value for the group variable that you want to associate with attributes. Note that if the variable for the GROUP= option has an associated format, the VALUE variable in the attribute map needs to contain the formatted value.

The other variables in the attribute map data set define attributes such as color, symbol, line thickness, and so on.

For example, the following code defines an attribute map to assign the color pink and the filled-circle  symbol to group value F and the color blue and the filled-square symbol to the group value M:

```data myattrmap; id='scattersymbols'; length markersymbol \$12; input value \$ markercolor \$ markersymbol \$; datalines; F pink circlefilled M blue squarefilled ;   proc sgplot data=sashelp.class dattrmap=myattrmap; scatter x=age y=height / group=sex markerattrs=(size=10px) attrid=scattersymbols; xaxis valueattrs=(size=12pt) labelattrs=(size=14pt); yaxis valueattrs=(size=12pt) labelattrs=(size=14pt); keylegend / valueattrs=(size=12pt) titleattrs=(size=14pt); run;```

Your attribute-map data set can contain multiple attribute maps, using a different value for the ID variable to distinguish each of the attribute maps. For more information about attribute maps, see the Using Attribute Maps to Control Visual Attributes section of the SAS® 9.4 ODS Graphics: Procedures Guide, Sixth Edition.

As you can see, there are many ways to assign attributes to plot elements. So, the next time you want to make a change to the visual appearance of your graph, remember that we have an ATTRS for that!

If you would like to see how to make attribute changes using a style template, read Dan Heath’s 2017 SAS Global Forum paper, Diving Deep into SAS® ODS Graphics Styles.

PROC SGPLOT: There’s an ATTRS for that was published on SAS Users.

If you are using the second maintenance release of SAS 9.3 (TS1M2) or later, you might have noticed that you have several map-related libraries that are defined for you.

• The MAPS library contains the old map data sets that have been provided with SAS/GRAPH® software for many years.  The source for these data sets was mainly freely available data or purchased data. As a result, it became difficult or impossible to provide updates to this data.
• The MAPSGFK library contains new map data sets that are licensed through GfK Geomarketing and that are provided as part of SAS/GRAPH software.
• The MAPSSAS library points to the same location as the MAPS library.

### Determining which library to use

You should use the MAPSGFK data sets to produce your maps.  There are several advantages to using the MAPSGFK data sets:

• The older MAPS library data sets contain outdated data, and this library will not be updated.
• The MAPSGFK data is updated more frequently.
• The MAPSGFK data sets standardize the variables in the data set. For example, the X and Y variables always contain the projected values, and LONG and LAT always contain the unprojected values.

Each of the data sets also contains the ID variable, as shown in the following example, to enable you to create a map without knowing about the boundaries that are contained in the data set.

```proc gmap data=mapsgfk.ireland_attr map=mapsgfk.ireland; id id; choro idname; run; quit;```

As mentioned above, the MAPSGFK library data sets contain both projected and unprojected values, which is helpful when you use annotation with maps or when you create a subset of a map.

• The MAPSGFK.PROJPARM data set contains the parameters that were used when the projected data was created for each of the data sets.  You can use these parameters with the GPROJECT procedure when you project an annotate data set. For more information about projecting an annotate data set using MAPSGFK.PROJPARM, see the sample code that appears in the section Code to Project Annotate Data with a GfK Map Data Set ("Chapter 37: GMAP Procedure") in SAS/GRAPH® 9.4: Reference, Fourth Edition.
• Many of the MAPSGFK data sets include a lower level of hierarchy  for boundaries than was available previously in the map data sets, as shown in this example:
```proc gmap data=mapsgfk.africa1 map=mapsgfk.africa1; id id; choro id / nolegend; run; quit;```

This code sample generates the following output:

You can use the GREMOVE procedure to remove internal boundaries that you don’t want as part of your map.

• In earlier releases, the names of the map data sets in the MAPS library were limited to eight characters. The MAPGSFK data sets do not have that restriction, so you can use longer names. The longer data-set names enable you to determine the map data-set content more easily.

### Determining which MAPSGFK data sets to use

To help you determine which data set to use to create your map, you can use dictionary tables in the SQL procedure to generate a list of the data sets that are contained in the MAPSGFK library, along with their associated labels.

This example illustrates how you can view the dictionary table for selected data sets (in this case, MEMNAME and MEMLABEL):

```proc sql; select memname, memlabel from dictionary.tables where libname='MAPSGFK'; quit;```

The following output is a partial display of the results:

The data sets that end in _ATTR are the attribute data sets for the map data set of the same base name. You can use the attribute data sets to obtain names and other information that is associated with variables in the map data set.

You can determine more information about the contents of the data sets using the DICTIONARY.MEMBERS dictionary table in PROC SQL.

```proc sql; select name, type, length, label from dictionary.columns where libname='MAPSGFK' and memname='NAMERICA'; quit;```

The following output shows the results:

In some cases, changing your existing PROC GMAP code to use the MAPSGFK data sets rather than the MAPS data sets might be as simple as changing the data set name in the MAP= option of the PROC GMAP statement. In other cases, this changing data sets can include changing the variables that are listed in the ID statement of PROC GMAP, including using a GREMOVE procedure step to remove a lower-level map boundary to or remove or modify a GPROJECT procedure step. You can find tips about modifying your existing PROC GMAP code to work with MAPSGFK data sets in the section Using GfK Map Data Sets with Existing Code ("Chapter 37: GMAP Procedure") in  SAS/GRAPH® 9.4: Reference, Fourth Edition.

You can find more information about the new map data sets and modifying your existing map programs to use the MAPSGFK data sets in the SAS Global Forum paper The New SAS® Map Data Sets (by Darrell Massengill) and in the SAS/GRAPH Concepts section of SAS/GRAPH® 9.4: Reference, Fourth Edition.

MAPS, MAPSGFK and MAPSSAS, Oh my! was published on SAS Users.

A coworker was recently in need of some simple graphics to include in a slide show to accompany her SAS Global Forum paper. After listening to what she wanted, I decided that I could use PROC SGPLOT to create those images for her.

The first image was a set of stacked blocks displaying the letters A, B, and C. Since blocks are drawn with only four coordinates, we can draw those using the POLYGON statement. We can use the MARKERCHAR option in the SCATTER statement to draw letters within each block.

The POLYGON statement was added to PROC SGPLOT in the first maintenance release of SAS 9.4 (TS1M1) and enables you to define the X and Y coordinates to draw a polygon. The ID required argument in the POLYGON statement identifies each set of X, Y coordinates for a particular polygon.

In a DATA step, we defined the coordinates for each of the blocks, the center of the block, and the letter to use for the marker character:

```data blocks; input x y letter \$ xcen ycen; datalines; 10 10 B 15 15 10 20 B 15 15 20 20 B 15 15 20 10 B 15 15 16 20.5 A 21 25.5 26 20.5 A 21 25.5 26 30.5 A 21 25.5 16 30.5 A 21 25.5 22 10 C 27 15 32 10 C 27 15 32 20 C 27 15 22 20 C 27 15 ; run;```

In PROC SGPLOT, we defined the POLYGON statement before the SCATTER statement so that the letters are drawn on top of the filled blocks.

```proc sgplot data=blocks noautolegend noborder; polygon x=x y=y id=letter / outline lineattrs=(thickness=6px ) fill dataskin=matte FILLATTRS=(color=cxEDE3BB) group=letter; scatter x=xcen y=ycen / markerchar=letter markercharattrs=(size=80) group=letter; yaxis display=none; xaxis display=none; run;```

You can build on this code to add tops and sides to create a three-dimensional look for your blocks.

An image of an old fashioned chalkboard with writing on it was also needed. Similar to the blocks, the chalkboard can be drawn using the POLYGON statement with coordinates for the board and a frame. We can even define coordinates to draw a piece of chalk and an eraser.

```data board; input x y part \$; datalines; 9.5 9.5 frame 9.5 40.5 frame 50.5 40.5 frame 50.5 9.5 frame 10 10 board 10 40 board 50 40 board 50 10 board 15 10.2 chalk 15 10.75 chalk 18 10.75 chalk 18 10.2 chalk 30 10.2 eraser 30 11.5 eraser 35 11.5 eraser 35 10.2 eraser ; run;```

To add the writing on the chalkboard, we can define an SG annotation data set and reference it in the PROC SGPLOT statement using the SGANNO option.

In a DATA step, specify the TEXT annotate function and use the X1 and Y1 annotate variables to define the location of the text. The DRAWSPACE variable specifies that the X1 and Y1 variables are in relation to the percentage of the wall space, placing the text in the center of the chalkboard. Other variables can be added to modify the text attributes.

```data sganno; function='text'; x1=50; y1=50; drawspace='wallpercent'; label='1 + 2 = 3'; textcolor='white'; width=100; anchor='center'; textweight='bold'; textfont='Albany AMT'; textsize=50; transparency=0.15; run;```

To define the colors for each piece of the chalkboard, we can define an attribute map and reference it in the PROC SGPLOT statement using the DATTRMAP option and in the POLYGON statement using the ATTRID option.
In a DATA step, define the ID required variable to contain the name of the attribute map. The VALUE required variable contains the value of the item to be changed, which in this case is the polygon ID value. The FILLCOLOR variable contains the name of the color to be used for each polygon.

```data attrmap; id='board'; input value \$ fillcolor \$; datalines; frame cxBFA40B board delg chalk white eraser black ; run;```

Adding the annotation and attribute map to the PROC SGPLOT step, we create the chalkboard.

```proc sgplot data=board sganno=sganno dattrmap=attrmap noautolegend noborder nosubpixel; polygon x=x y=y id=id / group=id fill outline dataskin=matte attrid=board; xaxis display=none; yaxis display=none; run;```

Finally, we created a school house. Using the techniques we have used so far, we can define all the parts of a school house to draw the building, door, windows, and add the word “school.”

```data school; input x y part \$; x1=44.25; y1=20; datalines; 40 10 building 40 60 building 45 75 building 50 60 building 50 10 building 44 10 door 44 35 door 46 35 door 46 10 door 45 75 roof 40 60 roof 39 59 roof 45 78 roof 51 59 roof 50 60 roof 41 30 windowl 41 50 windowl 43 50 windowl 43 30 windowl 47 30 windowr 47 50 windowr 49 50 windowr 49 30 windowr ; run;   data attrmap; id='school'; input value \$ fillcolor \$; datalines; building cxF52707 door cxc4a854 roof black windowl white windowr white ; run;   data panes; drawspace='datavalue'; function='line'; linecolor='black'; linethickness=5; x1=41; y1=40; x2=43; y2=40; output; x1=42; y1=50; x2=42; y2=30; output; x1=47; y1=40; x2=49; y2=40; output; x1=48; y1=50; x2=48; y2=30; output; function='text'; width=25; x1=45; y1=38; label='SCHOOL'; textcolor='black'; textsize=20; textweight='bold'; output; run;   proc sgplot data=school dattrmap=attrmap sganno=panes noborder noautolegend; polygon x=x y=y id=part / outline lineattrs=(thickness=4px color=black ) fill dataskin=matte group=part attrid=school; scatter x=x1 y=y1 / markerattrs=(color=black symbol=circlefilled); xaxis display=none; yaxis display=none; run;```

Think of all the great images you could create with PROC SGPLOT and a little imagination!

PROC SGPLOT: It isn’t just for plots anymore was published on SAS Users.