I am always puzzled by the widespread popularity of continuous gradient color coding.

It is impossible to match the color in a graphic element (e.g., heat map cell) to that precise color on a gradient legend.

It is impossible to get a reasonably precise estimate in the difference in response value for any pair of color-coded cells in a heat map. At most, you might conjecture that their colors are different, but How Much is unknowable.

If hypothetically, the viewer could magically match the color in a cell to that color on the legend, the precise value is still unknowable because the gradient legend cannot fit a label for each of the legend colors.

(If you want to see examples of gradient legends that are more useful and/or that use purpose-specific distinguishable subgradients, I recommend Chapter 6 in Visual Data Insights Using SAS® ODS Graphics: A Guide to Communication-Effective Data Visualization.)

My Number 1 guideline for Data Visualization is: Visuals for Quick, Easy Inference, and Precise Numbers for Accurate Inference. Visuals are for Impression that is Immediate, Easy, and Memorable (“Sticky”). Precise Numbers are for Knowledge that is Clear, Certain, and Correct (“The Answer”).

ODS Graphics makes delivering precise numbers easy in several ways: data labels, axis tables, and, for heat maps, the TEXT statement.

When using the TEXT statement to annotate heat map cells, there are two ways to assure that the text is readable despite the cell color fill that is in its background. The BACKLIGHT option with value 1 puts a white halo around each character. The BACKFILL option (preferably with FILL color white for maximum readability) puts a white rectangle beneath each text string. The OUTLINE option creates a box around it (never harmful—its thin, IF default, line never subtracts much area from the surrounding color fill in a heat map cell). The PAD option is best set to 1pt to minimize the subtraction of area from the surrounding color fill.

For color coding, color distinguishability is optimized by maximizing the color mass: thick text, thick lines, big markers, big color swatches for a discrete legend, and sufficient area in a heat map cell.

In my experience, BACKFILL has always been a better choice than BACKLIGHT. For light background colors BACKLIGHT is satisfactory, but for dark backgrounds it, for me, can be visually irritating. I would use it in desperation only for very small cell sizes where backfill might be infeasible due to leaving insufficient distinguishable color fill in the cells.

Below is the code for my two recommended variations from the blog’s example.

I always use a uniform text size for all elements of a graph, unless space constraints dictate an exception for the troubled part(s) of the graph.

You can create an ODS style to impose uniform text characteristics (font or family, height or size, weight (bold or normal), etc. That can solve the problem mentioned below for text size of the gradient legend’s values.

%let TextSize = 7pt; /* Adequate with ODS graphics default dimensions for images.
Adjust it as desired. */

ods graphics / reset=all scale=off imagename=
'AnnotateWithBackLightForMaximumCellColorFill_DeliverTheNumbersNoGuessingNeeded';
title1 height=&TextSize "Heat Map of Correlation Matrix";
title2 height=&TextSize
"Read the Precise Correlation Values in the Cells";
title3 height=&TextSize
"Visually Approximately Compare Them Using the Color Coding and Legend";
proc sgplot data=CorrList aspect=1 rattrmap=CorrRangeAttrMap
noautolegend; /* suppress the legend triggered by the TEXT statement */
heatmapparm x=Var1 y=Var2 colorresponse=Corr / rattrID=CorrID
outline outlineattrs=(color=grey);
text x=Var1 y=Var2 text=Corr /
textattrs=(size=&TextSize)
backlight=1; /* brightest halo (white) */
yaxis reverse display=(nolabel) valueattrs=(size=&TextSize);
xaxis display=(nolabel) valueattrs=(size=&TextSize)
fitpolicy=stagger; /* prevent tilted axis values */
format Corr 5.2; /* trim it from four decimal positions */
gradlegend / position=bottom
/* ODS Graphics provides no direct control on legend value size */
title='Correlation' titleattrs=(size=&TextSize);
run;

imagename=
'AnnotateWithBackFillForMaximumReadability_DeliverTheNumbersNoGuessingNeeded';
text x=Var1 y=Var2 text=Corr /
textattrs=(size=&TextSize)
backfill pad=1pt fillattrs=(color=white) outline;

LeRoy Bessler, PhD, is a data artist, the world’s longest serving advisor to fellow SAS users on best practices for graphic design and use of color, and author of Visual Data Insights Using SAS ODS Graphics: A Guide to Communication-Effective Data Visualization. Since 1981 he has worked to get the best from SAS graphics software.