Hello,
I've never tried to make this kind of graph (or really seen one used before), so it kind of caught my interest. I've made a rough program today to give an example of how it could be done. I used the program I made for my CIRCOS macro as a skeleton. I have a number of things to fix up, but it might get you started. I'm going to polish this program over the week I think and can post the final product later. Note that this is easier in 9.4+ because of the POLYGON plot statement, and I don't have 9.3 to actually test my program at the moment to see if it stays smooth in 9.3 using the DRAWPOLYGON option instead.
%macro donut(data=, subgroup=, var=, barbuffer=0, ringbuffer=0.05, whitespace=0.3, start=90, direction=cw, points=15,
/***2.1.1: Fill Color Options***/
color=,transparency=0,
/**Image Options**/
antialias=100000,axiscolor=black,border=0,gpath=,background=white,dpi=200,
plotname=_donut,plottype=png,
height=6in,width=6in,
fontcolor=black,svg=0,tiffdevice=TIFFP,transparent=0,
/**2.4: Document Options**/
destination=rtf,outdoc=,orientation=portrait,
/**2.5: Output Plot Data**/
out=);
/**Save current options to reset after macro runs**/
%local _mergenoby _notes _qlm _odspath _starttime _device _gsfname
_xmax _ymax _xpixels _ypixels _imagestyle _iback _listing _orientation;
%let _starttime=%sysfunc(time());
%let _notes=%sysfunc(getoption(notes));
%let _mergenoby=%sysfunc(getoption(mergenoby));
%let _qlm=%sysfunc(getoption(quotelenmax));
%let _device=%sysfunc(getoption(device));
%let _gsfname=%sysfunc(getoption(gsfname));
%let _xmax=%sysfunc(getoption(xmax));
%let _ymax=%sysfunc(getoption(ymax));
%let _xpixels=%sysfunc(getoption(xpixels));
%let _ypixels=%sysfunc(getoption(ypixels));
%let _imagestyle=%sysfunc(getoption(imagestyle));
%let _iback=%sysfunc(getoption(iback));
%let _orientation=%sysfunc(getoption(orientation));
%let _odspath=&sysodspath;
%if %sysevalf(%superq(_odspath)=,boolean) %then %let _odspath=WORK.TEMPLAT(UPDATE) SASHELP.TMPLMST (READ);
/**Turn off warnings for merging without a by and long quote lengths**/
/**Turn off notes**/
options mergenoby=NOWARN nonotes noquotelenmax;
/**See if the listing output is turned on**/
proc sql noprint;
select 1 into :_listing separated by '' from sashelp.vdest where upcase(destination)='LISTING';
quit;
/*Don't send anything to output window, results window, and set escape character*/
ods noresults escapechar='^';
ods select none;
%let _odspath=&sysodspath;
data _temp;
merge &data (keep=&subgroup rename=(&subgroup=_sg)) &data (keep=&var rename=(&var=_var));
if ^missing(_sg) and ^missing(_var);
run;
proc sort data=_temp (keep=_var) out=_levels nodupkey;
by _var;
data _levels;
set _levels;
by _var;
if first._var then _level+1;
run;
proc sort data=_temp;
by _sg;
proc freq data=_temp noprint;
by _sg;
table _var /out=_frq;
run;
proc sql noprint;
%local _sglevels _nsg;
select distinct _sg into :_sglevels separated by '|' from _temp;
%let _nsg=%sysfunc(countw(%superq(_sglevels),|));
create table _frq2 as
select a._sg,a._var,b._level,a.count,a.percent/100 as percent,count(distinct a._var) as nvar from _frq as a left join _levels as b
on a._var=b._var
group by _sg
order by _sg,_level;
quit;
data _rings;
set _frq2;
by _sg;
/**Set up constants**/
pi=constant('pi');
barbuffer=&barbuffer;
ringbuffer=&ringbuffer;
whitespace=&whitespace;
barwidth=(1-&whitespace-&ringbuffer)/&_nsg;
id=_n_;
retain radius start end;
/*Set up inner bar radius*/
if _n_=1 then radius=&whitespace;
else if first._sg then radius=radius+barwidth+(&ringbuffer)/(&_nsg-1);
/*Set up bar starting point*/
if first._sg=1 then do;
start=2*pi * (&start/360);
end=start+2*pi * percent * (1-barbuffer);
end;
else do;
start=end+2*pi*barbuffer/nvar;
end=start+2*pi * percent * (1-barbuffer);
end;
/**create the coordinates for the bar**/
/*First point*/
x=radius*cos(start);y=radius*sin(start);output;
/*Draw inner arc*/
do i = start to end by (end-start)/&points;
x=radius*cos(i);y=radius*sin(i);output;
end;
/*Draw second point*/
x=radius*cos(end);y=radius*sin(end);output;
/*Draw third point*/
x=(radius+barwidth+ringbuffer/&_nsg)*cos(end);y=(radius+barwidth+ringbuffer/&_nsg)*sin(end);output;
/*Draw outer arc*/
do i = end to start by (start-end)/&points;
x=(radius+barwidth+ringbuffer/&_nsg)*cos(i);y=(radius+barwidth+ringbuffer/&_nsg)*sin(i);output;
end;
/*Draw the fourth point*/
x=(radius+barwidth+ringbuffer/&_nsg)*cos(start);y=(radius+barwidth+ringbuffer/&_nsg)*sin(start);output;
run;
/*Make macro variables for SAS 9.3*/
proc sql noprint;
%local _npoly i j;
/*Grab number of polygons*/
select count(distinct id) into :_npoly separated by '' from _rings;
%do i = 1 %to &_npoly;
%local x_&i y_&i l_&i;
select x,y,_level into :x_&i separated by '|',:y_&i separated by '|',:l_&i separated by '|'
from _rings where id=&i;
%end;
quit;
/**Creates document to save**/
%if %sysevalf(%superq(outdoc)=,boolean)=0 %then %do;
ods escapechar='^';
/**Sets up DPI and ODS generated file**/
ods &destination
%if %qupcase(&destination)=RTF %then %do;
file="&outdoc"
image_dpi=&dpi startpage=NO
%end;
%else %if %qupcase(&destination)=HTML %then %do;
image_dpi=&dpi
%if %upcase(&sysscpl)=LINUX or %upcase(&sysscpl)=UNIX %then %do;
path="%substr(&outdoc,1,%sysfunc(find(&outdoc,/,-%sysfunc(length(&outdoc)))))"
file="%scan(&outdoc,1,/,b)"
%end;
%else %do;
path="%substr(&outdoc,1,%sysfunc(find(&outdoc,\,-%sysfunc(length(&outdoc)))))"
file="%scan(&outdoc,1,\,b)"
%end;
%if %sysevalf(%superq(gpath)=,boolean)=0 %then %do;
gpath="&gpath" (url=none)
%end;
%end;
%else %if %qupcase(&destination)=PDF %then %do;
dpi=&dpi startpage=NO bookmarkgen=off notoc
file="&outdoc"
%end;
%else %if %qupcase(&destination)=EXCEL %then %do;
file="&outdoc"
dpi=&dpi options(sheet_interval='none')
%end;
%else %if %qupcase(&destination)=POWERPOINT %then %do;
file="&outdoc"
dpi=&dpi
%end;;
%end;
%else %do;
ods listing close image_dpi=&dpi;
%end;
/*Save plot template to work directory*/
ods path WORK.TEMPLAT(UPDATE) SASHELP.TMPLMST (READ);
/*Define plot template to render*/
proc template;
define statgraph _donut;
/*Sets size of plot*/
begingraph / designheight=%superq(height) designwidth=%superq(width) SUBPIXEL=on
backgroundcolor=&background
%if %superq(transparent)=1 %then %do;
opaque=false
%end;
/**Turns the border around the plot off if border=0**/
%if %superq(border)=0 %then %do;
border=false
%if %superq(transparent)=1 %then %do;
pad=0px
%end;
%end;;
/*Sets up graph space*/
layout overlay / walldisplay=none
xaxisopts=(type=linear linearopts=(viewmin=-1.1 viewmax=1.1) display=none)
yaxisopts=(type=linear linearopts=(viewmin=-1.1 viewmax=1.1) display=none);
/**Draws the Donuts**/
/*Polygon plot only available in SAS 9.4*/
*polygonplot x=x y=y id=id / display=(fill) group=_Var
fillattrs=(transparency=%superq(transparency));
scatterplot x=x y=y / markerattrs=(size=0pt symbol=squarefilled) group=_var name='s';
discretelegend 's' / location=outside halign=right valign=bottom down=1 autoitemsize=true;
%do i = 1 %to &_npoly;
BEGINPOLYGON x=%scan(&&x_&i,1,|) y=%scan(&&y_&i,1,|) / display=(fill) transparency=%superq(transparency) drawspace=datavalue
fillattrs=graphdata%scan(&&l_&i,1,|);
%do j = 2 %to %sysfunc(countw(%superq(x_&i),|));
DRAW x=%scan(&&x_&i,&j,|) y=%scan(&&y_&i,&j,|);
%end;
ENDPOLYGON;
%end;
endlayout;
endgraph;
end;
run;
/*creates the graph. Anti-aliasing is increased due to number of plot points*/
%if %sysevalf(%superq(gpath)^=,boolean) %then %do;
ods listing gpath="%superq(gpath)";
%end;
ods results;
ods select all;
%local workdir;
%let workdir=%trim(%sysfunc(pathname(work)));
/**Names and formats the image**/
%if %sysevalf(%superq(plottype)^=,boolean) %then %do;
%if %qupcase(&plottype)=TIFF or %qupcase(&plottype)=TIF %then %do;
ods graphics / outputfmt=png;
%end;
%else %do;
ods graphics / outputfmt=&plottype;
%end;
%end;
%if %sysevalf(%superq(plotname)^=,boolean) %then %do;
ods graphics / reset=index imagename="&plotname";
%end;
/**Turns on Scalable-Vector-Graphics**/
%if &svg = 1 %then %do;
%if %qupcase(&destination) = RTF or %qupcase(&destination) = EXCEL or %qupcase(&destination) = POWERPOINT %then %do;
ods graphics / OUTPUTFMT=EMF;
%end;
%else %if %qupcase(&destination) = HTML %then %do;
ods graphics / OUTPUTFMT=SVG;
%end;
%else %do;
ods graphics / OUTPUTFMT=STATIC;
%end;
%end;
/**Sets plot options**/
options notes;
ods graphics / antialiasmax=%superq(antialias) scale=off imagename="%superq(plotname)" height=&height width=&width;
proc sgrender data=_rings template=_donut ;
run;
options nonotes;
/**Creates the TIFF file from the PNG file created earlier**/
%if %qupcase(&plottype)=TIFF or %qupcase(&plottype)=TIF %then %do;
%local _fncheck _fncheck2;
options nonotes;
%if %sysevalf(%superq(gpath)=,boolean) %then %do;
filename cirpng "./&plotname..png";
filename cirtif "./&plotname..tiff";
data _null_;
x=fexist('cirpng');
x2=fdelete('cirtif');
call symput('_fncheck',strip(put(x,12.)));
call symput('_fncheck2',strip(put(x2,12.)));
run;
%if %sysevalf(%superq(_fncheck)^=1,boolean) %then %do;
filename cirpng "./&plotname.1.png";
%end;
%end;
%else %do;
filename cirpng "%sysfunc(tranwrd(&gpath./&plotname..png,//,/))";
filename cirtif "%sysfunc(tranwrd(&gpath./&plotname..tiff,//,/))";
data _null_;
x=fexist('cirpng');
x2=fdelete('cirtif');
call symput('_fncheck',strip(put(x,12.)));
call symput('_fncheck2',strip(put(x2,12.)));
run;
%if %sysevalf(%superq(_fncheck)^=1,boolean) %then %do;
filename cirpng "%sysfunc(tranwrd(&gpath./&plotname.1.png,//,/))";
%end;
%end;
options notes;
goptions device=&tiffdevice gsfname=cirtif
xmax=&width ymax=&height
xpixels=%sysevalf(%sysfunc(compress(&width,abcdefghijklmnopqrstuvwxyz,i))*&dpi)
ypixels=%sysevalf(%sysfunc(compress(&height,abcdefghijklmnopqrstuvwxyz,i))*&dpi)
imagestyle=fit iback=cirpng;
proc gslide;
run;
quit;
data _null_;
x=fdelete('cirpng');
run;
filename cirpng clear;
filename cirtif clear;
%end;
/**Closes the ODS file**/
%if %sysevalf(%superq(outdoc)=,boolean)=0 %then %do;
ods &destination close;
%end;
/*Deletes temporary datasets*/
proc datasets nolist nodetails;
delete _temp _levels _frq _Frq2;
quit;
/**Reload previous Options**/
%if &_listing=1 %then %do;
ods Listing;
%end;
ods path &_odspath;
options mergenoby=&_mergenoby &_notes &_qlm;
%put DONUT has finished processing, runtime: %sysfunc(putn(%sysevalf(%sysfunc(TIME())-&_starttime.),mmss.));
%mend;
options nomprint;
%donut(data=sashelp.class,subgroup=sex,var=age,points=20);
... View more