For a long time when I first started using SAS/Graph, I ignored Annotate. I read that section of the manual (yes, I'm old; back in the day, it was printed in a book, on paper) and kept asking myself "Who cares? Who would want to do that?" But one day, I actually tried to do things and found a real treasure in the annotate facility. YOU CAN USE IT TO DO ANYTHING. I think the use of annotate is greatly hindered by the aseptic and insipid examples; what is really needed is examples that really show what annotate can do with data-driven, and not hard-coded, examples. So, I thought I'd share. Attached is plot that I recently made. It is shows a graphic picture of a stratified ordinal data situation, essentially the Cochran-Mantel-Haenszel set-up. The plot is generated completely in annotate; the plot shown is one of 8 plots that are generated by the macro that draws the plot. The data are ordinal outcome data where a subject can score between 0 and 10 (collapsed to four groups (0, 1-3, 4-6, 7-10)) on item XYZ at week 24. The distribution of the outcome scores across all the subjects is shown in the upper left by the four grey rectangles in the row labeled "All". In this row, we see that 45% of the subjects scored 0, 37% scored 1-3, 13% scored 4-6, and just 4% scored 7-10. The area of the grey rectangle is proportional to the outcome proportion. But the subjects also had a baseline score in the study --- this facet of the study stratifies the subjects. The Greek word strata means "layer". Thus, our week 24 distribution is actually a mixture of distributions based on the baseline values. We can split apart (in the Greek, we can analyze) the week 24 distribution by the baseline score and we get the four rows below the "All" row. Here we see that, for those subjects who scored 0 at baseline, 81% of them scored 0 at week 24, 15% scored 1-3, 4% scored 4-6, and no one scored 7-10. But for subjects who scored 1-3 at baseline only 33% scored 0, while 54% scored 1-3, and so on. Similar details can be read for the 4-6 and 7-10 baseline categories. Note that our graphic mirrors the study set-up: the strata (layers) in the study are shown as layers in the plot. "The data display is the model." The heights of the rectangles in the baseline strata are proportion to the number of subjects in that stratum thus if you sum the areas of the grey boxes in a column vertically across that strata, that sum will be the area of the box at the top. The larger rectangles down that main diagonal show that baseline is predictive of outcome; but they don't tell the whole story. We see that subjects tended to stay steady or drop in score between baseline and week 24, as the boxes at and below the main diagonal are bigger than those above the main diagonal. But the study sought to compare two treatments: ABC and DEF. The red and blue boxes on the right side of the plot allow one to compare the distributions for the two treatments. The top row sums across all baseline levels and reveals 202 subjects assigned to ABC and 182 assigned to DEF. We see very similar outcomes between the treatments pooled across baseline levels but the strata reveal some nuances. The red group (ABC) has 76 subjects in the 0 baseline group, compared to 64 in the blue group. The baseline 0 stratum shows that the red treatment is pushed slightly to the left of the blue group, as there is more mass in the 0 and 1-3 groups for red than for blue; that difference is filled by the excess blue subjects with a 4-6 score. (As before, the blue and red bars are proportional to sample size and sum up in the columns across the rows.) Other interesting differences can be parsed from the other strata. A CMH test essentially compares the treatments within each stratum and then pools those comparisons to create the grand test. Our data display allows that to be seen in graphical form. But all this is made possible through annotate. I just took the counts and percentages and told SAS to "go here; draw this; according to these data; according to these color rules". Everything here was done with just the "bar", "move", and "label" functions and it is all data driven. There is no hard coding to get the labels or boxes in the right places. These are the kinds of examples that should be in the annotate documentation, not some lame bit of code that draws a star on top of a bar by hard coding the coordinates. Anyway, I hope everyone has a great afternoon. $0.02, Rafe
... View more