This is a pie chart macro that I wrote that allows the user to "explode" out a second piechart with the "Other" categories. I don't know if this is what you are trying to do with a donut chart, but you could add a white circle to the center to make this a donut chart. The code could use refinement but could get you started.
The VAR option controls which variable sets the categories. The OTHER_THRESHOLD determines which percentage is considered "Other" (e.g. 5 will take any group <=5% and group it into the Other slice). RADIUS and OTHER_RADIUS determine the size of the circles for the pie chart and OTHER_SHIFT will move the second pie chart over left or right.
%pie(data=sashelp.cars,var=type,other_threshold=10);
%macro pie(data=,var=,other_threshold=5,radius=1,other_radius=0.5,other_shift=2,
plottype=png,plotname=_pie);
data _temp;
set &data (keep=&var rename=(&var=_var_t));
length _var_ $200.;
if ^missing(strip(vvalue(_var_t))) then _var_=strip(vvalue(_var_t));
else _var_='Missing';
run;
proc freq data=_temp noprint;
table _var_ / out=_frq;
run;
data _frq2;
set _frq;
by _var_;
if first._var_ then _indx_+1;
run;
proc sql noprint;
create table _plota as
select a.* ,b._var_
from (select _indx_,sum(percent) as percent from
(select _var_,ifn(percent lt &other_threshold,.o,_indx_) as _indx_,(percent lt &other_threshold) as other,percent from _frq2)
group by _indx_) a left join _Frq2 as b on a._indx_=b._indx_;
create table _plotb as
select _var_,_indx_,percent,percent/sum(percent) as ppt,sum(percent) as othpct from _frq2 where percent lt &other_threshold;
%local _othgrps;
select count(*) into :_othgrps from _frq where percent lt &other_threshold;
quit;
data _plota2 (drop=text xtext ytext) _texta (keep=text xtext ytext);
set _plota end=last;
retain endpoint id;
percent=percent/100;
if _n_=1 and _indx_^=.o then endpoint=0;
if _indx_=.o then do;
id=1;
text=strip(put(percent*100,12.0))||'%';
xtext=0.5*cos(0);ytext=1.1*sin(0);output _texta;
series=1;x=0;y=0;output _plota2;
series=0;
do i = (2*constant('pi'))*(-percent/2) to (2*constant('pi'))*(percent/2) by (2*constant('pi'))*(percent/50);
x=&radius*cos(i);
y=&radius*sin(i);
output _plota2;
end;
series=1;output _plota2;
endpoint=percent/2;
end;
else do;
id+1;
text=strip(put(percent*100,12.0))||'%';
xtext=0.6*cos(2*constant('pi')*(percent/2+endpoint));ytext=0.6*sin(2*constant('pi')*(percent/2+endpoint));output _texta;
series=1;x=0;y=0;output _plota2;
series=0;
do i = (2*constant('pi'))*(endpoint) to (2*constant('pi'))*(percent+endpoint) by (2*constant('pi'))*(percent/50);
x=&radius*cos(i);
y=&radius*sin(i);
output _plota2;
end;
if last then do;
series=1;output _plota2;
end;
else do;
series=1;output _plota2;
end;
endpoint=endpoint+percent;
end;
run;
%if %sysevalf(&_othgrps>0,boolean) %then %do;
data _links;
set _plota;
where _indx_=.o;
link=1;
xlink=&radius*cos(constant('pi')*percent/100);
ylink=&radius*sin(constant('pi')*percent/100);
output;
xlink=&other_shift+&other_radius*cos(2*constant('pi')/3);
ylink=&other_radius*sin(2*constant('pi')/3);
output;
link=2;
xlink=&radius*cos(constant('pi')*-percent/100);
ylink=&radius*sin(constant('pi')*-percent/100);
output;
xlink=&other_shift+&other_radius*cos(4*constant('pi')/3);
ylink=&other_radius*sin(4*constant('pi')/3);
output;
run;
data _plotb2(drop=text xtext ytext) _textb (keep=text xtext ytext);
set _plotb;
retain endpoint id;
if _n_=1 then endpoint=0;
id+1;
text=strip(put(percent,12.0))||'%';
xtext=&other_shift+0.5*&other_radius*cos(2*constant('pi')*(ppt/2+endpoint));
ytext=0.5*&other_radius*sin(2*constant('pi')*(ppt/2+endpoint));output _textb;
series=1;x=&other_shift;y=0;output _plotb2;
series=0;
do i = (2*constant('pi'))*(endpoint) to (2*constant('pi'))*(ppt+endpoint) by (2*constant('pi'))*(ppt/50);
x=&other_shift+&other_radius*cos(i);
y=&other_radius*sin(i);
output _plotb2;
end;
series=1;output _plotb2;
endpoint=endpoint+ppt;
run;
%end;
data _text;
set _texta %if %sysevalf(&_othgrps>0,boolean) %then %do; _textb %end;;
run;
data _plot;
set _plota2 (in=a) %if %sysevalf(&_othgrps>0,boolean) %then %do; _plotb2 (in=b) %end;;
retain lastid;
if a then lastid=id;
else do;
circle=2;
id=id+lastid;
end;
run;
data _plot;
merge _plot _text %if %sysevalf(&_othgrps>0,boolean) %then %do; _links %end;;
run;
ods path WORK.TEMPLAT(UPDATE) SASHELP.TMPLMST (READ);
proc template;
define statgraph _pie;
begingraph /
%if %sysevalf(&_othgrps>0,boolean) %then %do;
designheight=5in designwidth=10in
%end;
%else %do;
designheight=5in designwidth=7in
%end;;
layout lattice;
sidebar / align=right;
discretelegend 'p1' / exclude=(' ') location=inside halign=right valign=center border=false across=1 valueattrs=(size=18pt)
displayclipped=true autoitemsize=true;
endsidebar;
layout overlay / walldisplay=none xaxisopts=(display=none
%if %sysevalf(&_othgrps>0,boolean) %then %do;
linearopts=(viewmin=-1.1 viewmax=%sysevalf(&other_shift+&other_radius+0.1)))
%end;
%else %do;
linearopts=(viewmin=-1.1 viewmax=1.1))
%end;
yaxisopts=(display=none linearopts=(viewmin=-1.1 viewmax=1.1));
/*Left Pie Chart*/
polygonplot x=eval(ifn(circle^=2,x,.)) y=y id=id / outlineattrs=(color=black pattern=1) display=(fill) group=_var_ name='p1'
dataskin=gloss;
seriesplot x=eval(ifn(series=1 and circle^=2,x,.)) y=y / lineattrs=(color=black pattern=1) group=id;
DRAWOVAL X=0 Y=0 WIDTH=2 HEIGHT=2 / display=(outline) outlineattrs=(color=black) heightunit=data
drawspace=datavalue widthunit=data;
/*Right Pie Chart*/
%if %sysevalf(&_othgrps>0,boolean) %then %do;
polygonplot x=eval(ifn(circle=2,x,.)) y=y id=id / outlineattrs=(color=black pattern=1) display=(fill) group=_var_ dataskin=gloss;
seriesplot x=xlink y=ylink / group=link lineattrs=(color=black pattern=1);
DRAWOVAL X=&other_shift Y=0 WIDTH=%sysevalf(2*&other_radius) HEIGHT=%sysevalf(2*&other_radius) / display=(outline) outlineattrs=(color=black) heightunit=data
drawspace=datavalue widthunit=data;
seriesplot x=eval(ifn(series=1 and circle=2,x,.)) y=y / lineattrs=(color=black pattern=1) group=id;
%end;
/*Percentages*/
textplot x=xtext y=ytext text=text / textattrs=(size=16pt) position=center;
endlayout;
endlayout;
endgraph;
end;
run;
ods graphics / reset scale=off imagename="&plotname" imagefmt=&plottype;
proc sgrender data=_plot template=_pie;
run;
proc datasets nodetails nolist;
delete _temp _temp2 _plota _plota2 _plotb _plotb2 _links _text _texta _textb _Frq _frq2;
quit;
%mend;
... View more