O Christmas tree,
O Christmas tree,
Last year a fractal made thee!
O Christmas tree,
O Christmas tree,
A heat map can display thee!
O tree of green, adorned with lights!
A trunk of brown, the rest is white.
O Christmas tree,
O Christmas tree,
A heat map can display thee!
Last week I showed how to use a heat map to visualize values in a matrix. To simplify the discussion, I allowed the ODS graphics in SAS to automatically choose colors based on the current ODS style. However, it is often useful to specify colors in order to improve the interpretability of a heat map. For example, in a recent article about how to specify colors in a mosaic plot, I used blue colors to represent negative values, white for nearly zero values, and red for positive values.
In the SAS Graph Template Language (GTL), you can use the DISCRETEATTRMAP statement and the DISCRETEATTRVAR statement to associate a color with each value in a matrix. Suppose that you want have a matrix with a small set of values, and you want to visualize the values by using the following colors:
- A missing value (.) is represented in the heat map by the color white
- The value 0 is represented by green
- The value 1 is represented by red
- The value 2 is represented by blue
- The value 3 is represented by yellow
- The value 4 is represented by brown
To illustrate assigning colors to values, I will create a "Christmas tree matrix." (You can think of each cell of the matrix as a "pixel" in the heat map.) The following program uses two helper functions (ROW and COL), which are explained in an article about how to fill specific cells of a matrix. The matrix is defined to have 100 rows and 101 columns. The trunk of the tree is 10% of its height. Within the "leafy" portion of the tree, I'll use the SAS/IML SAMPLE function to randomly place lights of three different colors.
proc iml; /* define helper functions ROW and COL */ start row(x); /* return matrix m such that m[i,j] = i */ return( repeat( T(1:nrow(x)), 1, ncol(x) )); finish; start col(x); /* return matrix m such that m[i,j] = j */ return( repeat(1:ncol(x), nrow(x)) ); finish; /* parameters for the tree dimensions */ h = 100; w = h+1; b = int(h/10); M = j(w, h, .); /* initialize h x w matrix to missing */ x = col(M); /* column numbers */ y = w + 1 - row(M); /* reverse Y axis */ /* define the leafy portion of the tree */ TreeIdx = loc(y>b & y<=2*x & y<=-2*x+2*h); /* a triangle */ M[TreeIdx] = 0; /* place lights randomly within tree */ N = int(0.12*ncol(TreeIdx)); /* use 12% of tree area */ call randseed(1225); do i = 1 to 3; /* 3 colors */ idx = sample(TreeIdx, N, "WOR"); /* sample without replacement */ M[idx] = i; end; /* define the trunk */ width = int(b/2); TrunkIdx = loc( y<= b & abs(x-nrow(M)/2)<width ); M[TrunkIdx] = 4; /* write matrix in "long form" to SAS data set */ Row = row(M); /* row index */ Col = col(M); /* col index */ create Tree var {"Row" "Col" "M"}; append; close Tree; quit; |
As in last week's article, I'll use a GTL template to define the heat map. However, this time I'll associate specific colors to data values. The following template defines a green tree with a brown trunk that is decorated with red, blue, and yellow lights:
proc template; define statgraph HeatmapTree; dynamic _X _Y _Z; begingraph; discreteattrmap name="christmastree"; value '.' / fillattrs=(color=WHITE); /* background color */ value '0' / fillattrs=(color=GREEN); /* tree color */ value '1' / fillattrs=(color=RED); /* ornament color 1 */ value '2' / fillattrs=(color=BLUE); /* ornament color 2 */ value '3' / fillattrs=(color=YELLOW); /* ornament color 3 */ value '4' / fillattrs=(color=BROWN); /* tree trunk color */ enddiscreteattrmap; discreteattrvar attrvar=Alias var=_Z attrmap="christmastree"; layout overlay / xaxisopts=(type=discrete display=none) yaxisopts=(type=discrete display=none reverse=true); heatmapparm x=_X y=_Y colorgroup=Alias / xbinaxis=false ybinaxis=false primary=true; endlayout; endgraph; end; run; ods graphics / width=500 height=800; proc sgrender data=Tree template=HeatmapTree; dynamic _X="Col" _Y="Row" _Z="M"; run; |
The resulting heat map is shown at the beginning of this article.
You might not have many occasion where you need to use SAS to draw Christmas trees, but the same technique enables you to assign colors to any heat map that contains a small number of values. For matrices with a larger number of values, use the RANGEATTRMAP and RANGEATTRVAR statements to specify colors for a gradient color ramp, as shown in Chris Hemedinger's blog post.
This is my last post until after the Winter Break. I am taking a 10-day hiatus from blogging to recharge my mental batteries. If you've enjoyed reading any of my 100+ blog posts in 2013, please subscribe through an RSS feed and tell your friends. I look forward to learning and sharing more SAS programming techniques in 2014.
12 Comments
Does this code have requirements besides IML, ie SAS 9.4?
The HEATMAPPARM and DISCRETEATTRS statements were added in SAS 9.3. At the moment I can't think of anything that needs 9.4.
Hi Rick,
Thanks for the blog posts and enjoy your holiday. I enjoy sharing your blog and look forward to reading your informative posts in the new year...
Cheers,
Michelle
The below program is more better, "Happy Christmas" will replace the default "SAS System"
title "Happy Christmas ";
ods graphics / width=500 height=800;
proc sgrender data=Tree template=HeatmapTree;
dynamic _X="Col" _Y="Row" _Z="M";
run;
Welcome Rick Wicklin, FD
I am a master's student in mathematical statistics ..
I have a problem in the formation model to calculate the Spearman coefficient of permutations (manual means without the use of Spearman coefficient of built-in device)
I arrived for this model .. And stood in the programming Spearman coefficient ..
DM 'LOG; CLEAR; OUTPUT; CLEAR;';
% let n = 3;
data perm & n (drop = i);
array a {& n};
do i = 1 to &n; a [i] = i;
end;
do i = 1 to fact (& n);
call allperm (i, of a [*]);
output;
end;
run;
proc print data = perm &n;
proc iml;
use perm &n;
read all var {a1 a2 a3} into b;
print b;
run;
quit;
I hope to help you ..
Thanks in advance for your cooperation ..
Every year, you are fine ..
You can ask questions like this at the SAS Support Community. For statistical questions use the community for Statistical Procedures. There is also a community for SAS/IML programming questions.
Pingback: Hot heat maps
Pingback: Visualize a matrix in SAS by using a discrete heat map - The DO Loop
Pingback: A Christmas tree from Pascal’s triangle - The DO Loop
How can I write an ID variable to the resulting tree matrix. so that the first matrix gets ID=1 and the second ID=2 in row-major order?
I would like to use this ID variable for further graphing using it as a group variable?
I thought about using the any ALL function but I cannot figure it out.
I don't know what you mean by "the first matrix" and "the second [matrix]". If you are trying to perform some sort of animation (like blinking lights), then set
ID = j(nrow(M), 1, 1); /* for first tree */
create Tree var {"Row" "Col" "M" "ID"}; append;
then create the new M and submit
ID = j(nrow(M), 1, 2); /* for second tree */
create Tree var {"Row" "Col" "M" "ID"}; append;
etc. You can do this in a loop to save typing.
Pingback: Math-ing around the Christmas tree: Can the SVD de-noise an image? - The DO Loop