Service Packs for the SAS9 Platform." />
This sample uses the GCHART procedure in conjunction with the Annotate facility in the SAS/GRAPH software to create a series of bar chart indicators, and then uses the GREPLAY procedure to arrange the indicators into a dashboard. For a sample of an individual indicator, see Create a bar chart indicator for a dashboard.
This sample builds on sample Create a bar chart indicator for a dashboard and places several of these charts on the same page in an arrangement that could be considered a minimal dashboard. It uses the same %DO_CHART macro that was used in the bar chart indicator sample. The %DO_CHART macro is called separately for each graph with a unique GRSEG name. It then uses the GREPLAY procedure to draw the graphs into a custom template in order to put the graphs on a single page in a specific order and position.
The custom GREPLAY template is defined by specifying four X and Y coordinates for the rectangular area that will hold each graph. The units of the X and Y coordinates are the percentage of the total length of the axis where the coordinate is located, with 0,0 being at the bottom-left corner. Each rectangular area is assigned an ID number. Here is the layout of the custom template:
Note: The layout shown is not an exact representation of the areas. Spaces have been added around the borders of the areas to make the layout more apparent.
Here are the coordinates that place the Revenue chart near the top-left corner of the dashboard in area 1:
1/llx = 0 lly = 50 ulx = 0 uly = 73 urx =20 ury = 73 lrx =20 lry = 50
where LLX
is the lower-left X coordinate, LLY
is the lower-left Y coordinate, and so on.
The GREPLAY procedure allows you to overlap the rectangular areas. In this sample, area 0 is defined as covering the entire dashboard. The GSLIDE procedure is used to place the title near the top-right corner, and the Annotate facility is used to create a custom legend. Here are the coordinates:
0/llx = 0 lly = 0 ulx = 0 uly =100 urx =100 ury =100 lrx =100 lry = 0
Here is the GREPLAY procedure TREPLAY action statement that draws the appropriate charts into the desired areas of the custom GREPLAY template:
treplay 16:plot16 17:plot17 0:titles 1:plot1 2:plot2 3:plot3 4:plot4 5:plot5 6:plot6 7:plot7 8:plot8 9:plot9 10:plot10 11:plot11 12:plot12 13:plot13 14:plot14 15:plot15
Note: For convenience and code readability, spacing is used in the TREPLAY action statement to make the layout correspond to the actual location of the charts in relation to one another.
Notice that PLOT16 is replayed into area 16.
For each chart on the dashboard, this sample adds a drill-down link to a detailed table of the data for the chart. To keep this sample self-contained, the drill-down tables are placed on the same output page just below the dashboard, and each table is identified using an HTML anchor as follows:
ods html anchor="&anchor_name";
However, in your case, these drill-down links would likely be to separate detail tables or charts on a separate page.
In this sample, the anchor names are created when the bar charts are created as follows:
htmlvar= 'title='|| quote( 'Quarter: '|| trim(left(quarter)) ||'0D'x|| 'Target: '|| trim(left(put(target,&value_format))) ||'0D'x|| 'Actual: '|| trim(left(put(actual,&value_format))) ||'0D'x|| 'Actual as Percent of Target: '|| trim(left(put(percent_of_target,percent6.0))) ) || ' '|| 'href="#'||trim(left("&grseg_name"))||'"';
To keep the naming simple and consistent, the GRSEG names are used as the anchor names. While you are viewing the dashboard, if you move your mouse pointer over each bar, the data value for the bar is displayed in the HTML chart tip. If you click a bar, your browser scrolls down to the HTML anchor to show you the table of the data for the bar.
For additional information about creating dashboard applications with SAS/GRAPH software, see SAS/GRAPH Dashboard Samples.
These sample files and code examples are provided by SAS Institute Inc. "as is" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and fitness for a particular purpose. Recipients acknowledge and agree that SAS Institute shall not be liable for any damages whatsoever arising out of their use of this material. In addition, SAS Institute will provide no support for the materials contained herein.
/* Specify the name for the output file. */
%let name=barChartDash;
/* Set output options. */
filename odsout '.';
goptions reset=all;
goptions device=gif;
%let title1=Sales Dashboard;
%let title2=(All currency in US $);
%let title3=19dec2004;
/* Set colors for indicator features. */
%let gray=gray;
%let crefgray=graycc;
%let backcolor=grayee;
/* Set colors and patterns for the bars. */
%let green=cxc2e699;
%let pink=cxfa9fb5;
%let red=cxff0000;
pattern1 v=s c=&green;
pattern2 v=s c=&red;
pattern3 v=s c=&pink;
/* Define fonts for indicator and dashboard titles and text. */
%let ftitle='swissb';
%let ftext='swissl';
/* Create a user-defined format that prints 200000 as $200k. */
proc format;
picture kdollar low-high='0009k ' (prefix='$' mult=.001);
run;
/* Define the location of the HTML page that supplies drill-down details
for the indicator. If you don't have Internet access, you must put
the target HTML file where your browser can access it, then change the
following URL to point to your location. */
%let hardcoded_drilldown=http://support.sas.com/rnd/datavisualization/dashboards/generic_drilldown.htm;
/**************************************************************************/
/* The do_chart macro creates an individual bar chart indicator with a
target value marker.
The do_chart macro accepts the following list of parameters:
data_name = name of the data set that contains indicator values
pltname = name of GRSEG to store the graph
*/
%macro do_chart(data_name, pltname);
%local data_name pltname;
/* Extract values from the data set into macro variables. */
proc sql noprint;
select unique title_text into :title_text from &data_name;
select unique value_format into :value_format from &data_name;
select unique y_max into :y_max from &data_name;
select unique y_by into :y_by from &data_name;
quit;
/* Trim blank spaces from the macro variable values. */
%let title_text=%trim(&title_text);
%let value_format=%trim(&value_format);
%let y_max=%trim(&y_max);
%let y_by=%trim(&y_by);
data temp_data; set &data_name;
format actual &value_format;
/* Evaluate the actual value as a percent of the target value and assign colors. */
length evaluation $12;
percent_of_target=actual/target;
if (percent_of_target < poor_pct) then evaluation='Poor';
else if (percent_of_target < good_pct) then evaluation='Satisfactory';
else evaluation='Good';
/* Set the 'tool tip' for the chart to provide a link to an
HTML page with drill-down details. */
length htmlvar $200;
htmlvar='title='||quote(
'Quarter: '|| trim(left(quarter)) ||'0D'x||
'Target: '|| trim(left(put(target,&value_format))) ||'0D'x||
'Actual: '|| trim(left(put(actual,&value_format))) ||'0D'x||
'Actual as Percent of Target: '|| trim(left(put(percent_of_target,percent6.0)))
) || ' '||
'href="'||"&hardcoded_drilldown"||'"';
run;
/* Annotate a custom target marker for each bar. */
data target_anno; set temp_data;
length function $ 8 style $ 20 color $ 12;
hsys='3'; when='a'; position='5';
/* Draw the triangular pointer (using character 'A' of the MARKER
software font provided with SAS/GRAPH software). */
function='label'; style='marker'; text='A';
if (evaluation eq 'Good') then color="&green";
else if (evaluation eq 'Poor') then color="&red";
else if (evaluation eq 'Satisfactory') then color="&pink";
else color='white';
xsys='2'; ysys='2'; midpoint=quarter; y=target; size=.01;
output;
xsys='7'; x=9; size=4;
output;
/* Draw an outline around the triangle using the MARKERE font
(empty/outline of the triangle marker). */
style='markere'; color="&gray";
xsys='2'; ysys='2'; midpoint=quarter; y=target; size=.01;
output;
/* Reposition the cursor and draw a line from the point of the
triangle, across the bar. */
xsys='7'; x=9; size=4;
output;
hsys='3'; position='5'; xsys='2'; ysys='2'; hsys='3'; position='5'; size=1; color="&gray";
function='move'; midpoint=quarter; y=target;
output;
function='draw'; xsys='7'; x=6;
output;
function='draw'; xsys='7'; x=-12;
output;
run;
/* Add dummy data values to guarantee that all three possible
colors are accounted for, so that poor is always red,
satisfactory is always pink, and good is always green. Otherwise
the colors will be assigned in alphabetic order, and if the particular
graph is missing any of the three categories then that will affect which
color goes to which category. */
data levels_guarantee;
length evaluation $12;
quarter='Q1'; evaluation="Poor";
output;
quarter='Q1'; evaluation="Satisfactory";
output;
quarter='Q1'; evaluation="Good";
output;
run;
data temp_data;
set temp_data levels_guarantee;
run;
/* Draw the chart and save it with the specified name so it can later be
replayed into the desired location of a dashboard. */
goptions gunit=pct htitle=10 ftitle=&ftitle htext=8.0 ftext=&ftext;
goptions xpixels=275 ypixels=200;
axis1 label=none order=(0 to &y_max by &y_by) minor=none major=(h=2) offset=(0,0);
axis2 label=none offset=(8,10);
title "&title_text";
proc gchart data=temp_data anno=target_anno;
vbar quarter / discrete
type=sum
sumvar=actual
subgroup=evaluation
raxis=axis1
maxis=axis2
autoref cref=&crefgray clipref
coutline=&gray
width=7
space=7
nolegend
html=htmlvar
des=""
name="&pltname";
run;
quit;
%mend do_chart;
/**************************************************************************/
/* Delete all GRSEGs in the current session to ensure that indicators use
the expected names. If a name is already in use, then an attempt to create
a new GRSEG using that name it will add a number to the name. In that case,
the subsequent GREPLAY will be placing the wrong GRSEGs into the dashboard.
Note: The macro code just checks whether there are any gsegs to delete. If
it tried to delete specific entries and none existed, then you would get an
error message: "ERROR: Member-name GSEG is unknown." */
%macro delcat(catname);
%if %sysfunc(cexist(&catname)) %then %do;
proc greplay nofs igout=&catname;
delete _all_;
run;
%end;
quit;
%mend delcat;
%delcat(work.gseg);
/*******************************************************************************/
/* Create a separate data set for each chart. */
data data1;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Market Share Q1 .23 .24 percentn6.0 .65 .90 .30 .10
Market Share Q2 .20 .25 percentn6.0 .65 .90 .30 .10
Market Share Q3 .19 .26 percentn6.0 .65 .90 .30 .10
Market Share Q4 .17 .27 percentn6.0 .65 .90 .30 .10
;
run;
data data2;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Order Size Q1 405 400 comma7.0 .50 .75 500 100
Order Size Q2 421 410 comma7.0 .50 .75 500 100
Order Size Q3 435 420 comma7.0 .50 .75 500 100
Order Size Q4 449 430 comma7.0 .50 .75 500 100
;
run;
data data3;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
New Customers Q1 346 300 comma7.0 .50 .85 500 100
New Customers Q2 430 350 comma7.0 .50 .85 500 100
New Customers Q3 447 400 comma7.0 .50 .85 500 100
New Customers Q4 468 450 comma7.0 .50 .85 500 100
;
run;
data data4;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
On Time Delivery Q1 .83 .92 percentn6.0 .60 .90 1.0 .25
On Time Delivery Q2 .73 .93 percentn6.0 .60 .90 1.0 .25
On Time Delivery Q3 .65 .94 percentn6.0 .60 .90 1.0 .25
On Time Delivery Q4 .68 .95 percentn6.0 .60 .90 1.0 .25
;
run;
data data5;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Satisfaction Q1 3.2 3.3 comma5.1 .60 .90 5 1
Satisfaction Q2 3.0 3.3 comma5.1 .60 .90 5 1
Satisfaction Q3 2.8 3.3 comma5.1 .60 .90 5 1
Satisfaction Q4 2.7 3.3 comma5.1 .60 .90 5 1
;
run;
data data6;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Chardonnay Q1 68634 69000 kdollar. .60 .90 125000 25000
Chardonnay Q2 64025 76820 kdollar. .60 .90 125000 25000
Chardonnay Q3 104063 84640 kdollar. .60 .90 125000 25000
Chardonnay Q4 107610 98900 kdollar. .60 .90 125000 25000
;
run;
data data7;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Cabernet Q1 28430 27000 kdollar. .60 .90 125000 25000
Cabernet Q2 30228 30060 kdollar. .60 .90 125000 25000
Cabernet Q3 35053 33120 kdollar. .60 .90 125000 25000
Cabernet Q4 38728 38700 kdollar. .60 .90 125000 25000
;
run;
data data8;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Merlot Q1 25440 25500 kdollar. .60 .90 125000 25000
Merlot Q2 24977 28390 kdollar. .60 .90 125000 25000
Merlot Q3 28955 31280 kdollar. .60 .90 125000 25000
Merlot Q4 28865 36550 kdollar. .60 .90 125000 25000
;
run;
data data9;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Sauvignon Blanc Q1 17677 15000 kdollar. .60 .90 125000 25000
Sauvignon Blanc Q2 35763 16700 kdollar. .60 .90 125000 25000
Sauvignon Blanc Q3 13790 18400 kdollar. .60 .90 125000 25000
Sauvignon Blanc Q4 12398 21500 kdollar. .60 .90 125000 25000
;
run;
data data10;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Zinfandel Q1 13876 13500 kdollar. .60 .90 125000 25000
Zinfandel Q2 10164 15030 kdollar. .60 .90 125000 25000
Zinfandel Q3 17876 16560 kdollar. .60 .90 125000 25000
Zinfandel Q4 18644 19350 kdollar. .60 .90 125000 25000
;
run;
data data11;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
North America Q1 78963 79500 kdollar. .60 .90 125000 25000
North America Q2 78138 86840 kdollar. .60 .90 125000 25000
North America Q3 91176 93840 kdollar. .60 .90 125000 25000
North America Q4 91441 107500 kdollar. .60 .90 125000 25000
;
run;
data data12;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Asia Q1 28877 25500 kdollar. .60 .90 125000 25000
Asia Q2 37472 29225 kdollar. .60 .90 125000 25000
Asia Q3 48641 33120 kdollar. .60 .90 125000 25000
Asia Q4 52944 39775 kdollar. .60 .90 125000 25000
;
run;
data data13;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Europe Q1 30811 30000 kdollar. .60 .90 125000 25000
Europe Q2 33032 33400 kdollar. .60 .90 125000 25000
Europe Q3 39948 36800 kdollar. .60 .90 125000 25000
Europe Q4 41253 43000 kdollar. .60 .90 125000 25000
;
run;
data data14;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Middle East Q1 12365 13500 kdollar. .60 .90 125000 25000
Middle East Q2 13081 15030 kdollar. .60 .90 125000 25000
Middle East Q3 15767 16560 kdollar. .60 .90 125000 25000
Middle East Q4 15592 19350 kdollar. .60 .90 125000 25000
;
run;
data data15;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
South America Q1 3041 1500 kdollar. .60 .90 125000 25000
South America Q2 3435 2505 kdollar. .60 .90 125000 25000
South America Q3 4206 3680 kdollar. .60 .90 125000 25000
South America Q4 5035 5375 kdollar. .60 .90 125000 25000
;
run;
data data16;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Revenue Q1 154057 150000 kdollar. .60 .90 300000 100000
Revenue Q2 165158 167000 kdollar. .60 .90 300000 100000
Revenue Q3 199738 184000 kdollar. .60 .90 300000 100000
Revenue Q4 206264 215000 kdollar. .60 .90 300000 100000
;
run;
data data17;
length value_format $15;
input title_text $ 1-20 quarter $ 22-23 actual target value_format poor_pct good_pct y_max y_by;
datalines;
Profit Q1 31999 37500 kdollar. .60 .80 60000 20000
Profit Q2 36749 41750 kdollar. .60 .80 60000 20000
Profit Q3 42431 46000 kdollar. .60 .80 60000 20000
Profit Q4 46685 53750 kdollar. .60 .80 60000 20000
;
run;
/* Run the do_chart macro for each chart, passing in the name of
the data set and the name of the GRSEG to store the output in. */
goptions nodisplay;
%do_chart(data1,plot1);
%do_chart(data2,plot2);
%do_chart(data3,plot3);
%do_chart(data4,plot4);
%do_chart(data5,plot5);
%do_chart(data6,plot6);
%do_chart(data7,plot7);
%do_chart(data8,plot8);
%do_chart(data9,plot9);
%do_chart(data10,plot10);
%do_chart(data11,plot11);
%do_chart(data12,plot12);
%do_chart(data13,plot13);
%do_chart(data14,plot14);
%do_chart(data15,plot15);
%do_chart(data16,plot16);
%do_chart(data17,plot17);
/**************************************************************************/
/* Create an overall title for the entire dashboard and also an overall
legend using the GSLIDE procedure. This slide is stored in a GRSEG named
titles that is replayed into a dashboard template area that overlaps the
entire dashboard output area. The text on the slide is placed in the top
right corner, where no charts are located. */
data titlanno;
length function color $ 8 style $ 20 position $ 1 text $ 50;
retain xsys ysys '3' hsys '3' when 'a';
function='label';
position='5';
color='black';
/* Annotated Title Text */
x=75;
size=5;
style="&ftitle";
y=96; text="&title1";
output;
size=3.5;
style="&ftext";
y=y-6; text="&title2";
output;
y=y-5; text="&title3";
output;
/* Custom annotated legend */
style="marker"; text='U'; size=3;
x=45;
y=82; color="&red";
output;
y=y+4; color="&pink";
output;
y=y+4; color="&green";
output;
y=y+4; color="&gray"; size=2.5; text='A';
output;
style="markere"; text='U'; size=3;
x=45;
y=82; color="&gray";
output;
y=y+4; color="&gray";
output;
y=y+4; color="&gray";
output;
style="&ftext"; size=2.2; color="black"; position='6';
x=46.5;
y=82.3; text="Poor";
output;
y=y+4; text="Satisfactory";
output;
y=y+4; text="Good";
output;
y=y+4; text="Target";
output;
run;
/* Draw the title and legend on a blank slide and save the output in a
GRSEG named titlanno. */
goptions xpixels=900 ypixels=640;
title;
footnote;
proc gslide des="" name="titles" anno=titlanno;
run;
/**************************************************************************/
/* Create the dashboard. */
goptions display;
goptions xpixels=900 ypixels=640;
goptions cback=&backcolor;
ODS LISTING CLOSE;
ODS HTML path=odsout body="&name..htm"
(title="Sales Dashboard (DM Review Contest - Scenario 3)")
style=minimal;
goptions border;
%let greout=white;
proc greplay tc=tempcat nofs igout=work.gseg;
/* Define the areas of a custom dashboard template. */
tdef dashbrd des='Dashboard'
/* Entire dashboard area (used for overall title/legend) */
0/llx = 0 lly = 0
ulx = 0 uly =100
urx =100 ury =100
lrx =100 lry = 0
/* First 5-chart row (various metrics) */
1/llx = 0 lly = 50
ulx = 0 uly = 73
urx =20 ury = 73
lrx =20 lry = 50
2/llx =20 lly = 50
ulx =20 uly = 73
urx =40 ury = 73
lrx =40 lry = 50
3/llx =40 lly = 50
ulx =40 uly = 73
urx =60 ury = 73
lrx =60 lry = 50
4/llx =60 lly = 50
ulx =60 uly = 73
urx =80 ury = 73
lrx =80 lry = 50
5/llx =80 lly = 50
ulx =80 uly = 73
urx =100 ury = 73
lrx =100 lry = 50
/* Second 5-chart row (types of wine) */
6/llx = 0 lly = 25
ulx = 0 uly = 48
urx =20 ury = 48
lrx =20 lry = 25
7/llx =20 lly = 25
ulx =20 uly = 48
urx =40 ury = 48
lrx =40 lry = 25
8/llx =40 lly = 25
ulx =40 uly = 48
urx =60 ury = 48
lrx =60 lry = 25
9/llx =60 lly = 25
ulx =60 uly = 48
urx =80 ury = 48
lrx =80 lry = 25
10/llx =80 lly = 25
ulx =80 uly = 48
urx =100 ury = 48
lrx =100 lry = 25
/* Third 5-chart row (geographic locations) */
11/llx = 0 lly = 0
ulx = 0 uly = 23
urx =20 ury = 23
lrx =20 lry = 0
12/llx =20 lly = 0
ulx =20 uly = 23
urx =40 ury = 23
lrx =40 lry = 0
13/llx =40 lly = 0
ulx =40 uly = 23
urx =60 ury = 23
lrx =60 lry = 0
14/llx =60 lly = 0
ulx =60 uly = 23
urx =80 ury = 23
lrx =80 lry = 0
15/llx =80 lly = 0
ulx =80 uly = 23
urx =100 ury = 23
lrx =100 lry = 0
/* Two charts in top left (revenue and profit) */
16/llx = 0 lly = 76
ulx = 0 uly = 99
urx =20 ury = 99
lrx =20 lry = 76
17/llx =20 lly = 76
ulx =20 uly = 99
urx =40 ury = 99
lrx =40 lry = 76
;
/* Replay the individual indicators into the appropriate areas in the custom
dashboard template. */
template = dashbrd;
treplay
16:plot16 17:plot17 0:titles
1:plot1 2:plot2 3:plot3 4:plot4 5:plot5
6:plot6 7:plot7 8:plot8 9:plot9 10:plot10
11:plot11 12:plot12 13:plot13 14:plot14 15:plot15
des=""
name="&name";
run;
quit;
ODS HTML CLOSE;
ODS LISTING;
These sample files and code examples are provided by SAS Institute Inc. "as is" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and fitness for a particular purpose. Recipients acknowledge and agree that SAS Institute shall not be liable for any damages whatsoever arising out of their use of this material. In addition, SAS Institute will provide no support for the materials contained herein.
Type: | Sample |
Date Modified: | 2009-01-07 14:35:43 |
Date Created: | 2006-12-22 03:03:27 |
Product Family | Product | Host | SAS Release | |
Starting | Ending | |||
SAS System | SAS/GRAPH | All | 9.1 TS1M3 | 9.1 TS1M3 |