BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.

Good Evening Fellow SAS Boffins,

 

I am in the process of replacing a number of manually updated graphs in a Word document with SAS (9.4m4) alternatives.  I have hit a dead end when it comes to replicating a Pie of a Pie Chart similar to the image below.  

 

I am not married to the concept of using SAS/GRAPH and ODS if there is an AMO or VA alternative I would be keen to explore the idea.

 

Any help would be greatly appreciated.

 

Pie of a Pie Chart.gif

 

1 ACCEPTED SOLUTION

Accepted Solutions
JeffMeyers
Barite | Level 11

Hello, I found this topic while I was hanging out at PharmaSUG and thought it looked like a fun challenge. I made an example of doing this with the CARS dataset within the SASHELP library.

 

The program gets the frequencies (I get rid of hybrid due to it's very small size in the example).  I make the pie slices with the Polygon plot, the outlines with series plot, and the percentages with TEXT plot.  Note I have to do a great bit of trigonometry to make this work where I assume the left circle has a radius of 1.  If you need to change the size of this circle then some changes to code would need to happen.  I force any percentages smaller than 8% into a separate group, and then make the second pie chart based on them.  I have a macro variable in a %let to determine the distance between the circles.  It should autosize the second circle based on this macro variable.  There are many tweaks I could do to make it look better, but I think it's a fun start.

pieofpie.png

 

 

proc freq data=sashelp.cars noprint;
    table type / out=frq;
    where type ^='Hybrid';
run;

proc sql;
    create table plota as
        select type,sum(percent) as percent from
        (select ifc(percent lt 8,' ',type) as type,(percent lt 8) as other,percent from frq)
        group by type;
    create table plotb as   
        select type,percent,percent/sum(percent) as ppt,sum(percent) as othpct from frq where percent lt 8;
quit;

%let dist=2;
data plota2 (drop=text xtext ytext) texta (keep=text xtext ytext);
    set plota end=last;
    retain endpoint id;
    percent=percent/100;
    if type=' ' then do;
        id=1;
        text=strip(put(percent*100,12.0))||'%';
        xtext=0.5*cos(0);ytext=0.5*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=1*cos(i);
            y=1*sin(i);
            output plota2;
        end;
        series=1;x=x*(&dist/cos(percent*constant('pi')));y=y*(&dist/cos(percent*constant('pi')));output plota2;
        endpoint=percent/2;
    end;
    else do;
        id=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=1*cos(i);
            y=1*sin(i);
            output plota2;
        end;
        if last then do;
            series=1;x=x*(&dist/cos(percent*constant('pi')));y=y*(&dist/cos(percent*constant('pi')));output plota2;
        end;
        else do;
            series=1;output plota2;        
        end;
        endpoint=endpoint+percent;
    end;
run;

data plotb2(drop=text xtext ytext) textb (keep=text xtext ytext);
    set plotb;
    retain endpoint id;
    if _n_=1 then do;
        endpoint=0;
        call symput('radius',strip(put(2*&dist*tan(othpct/100*constant('pi')),best12.)));
    end;
    id+1;
    text=strip(put(percent,12.0))||'%';
    xtext=&dist+0.5*(&dist*tan(othpct/100*constant('pi')))*cos(2*constant('pi')*(ppt/2+endpoint));
    ytext=0.5*(&dist*tan(othpct/100*constant('pi')))*sin(2*constant('pi')*(ppt/2+endpoint));output textb;
    series=1;x=&dist;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=&dist+(&dist*tan(othpct/100*constant('pi')))*cos(i);
        y=(&dist*tan(othpct/100*constant('pi')))*sin(i);
        output plotb2;
    end;
    series=1;output plotb2;
    endpoint=endpoint+ppt;
run;

data text;
    set texta textb;
run;
data plot;
    set plota2 (in=a) plotb2 (in=b);
    retain lastid;
    if a then lastid=id;
    else if b then do;
        circle=2;
        id=id+lastid;
    end;
run;

data plot;
    merge plot text;
    run;
ods path WORK.TEMPLAT(UPDATE) SASHELP.TMPLMST (READ);
proc template;
    define statgraph pieofpie;
    begingraph / designheight=5in designwidth=10in;
    
    layout overlay / xaxisopts=(display=none linearopts=(viewmin=-1.1 viewmax=4)) 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=type name='p1';
        seriesplot x=eval(ifn(series=1 and circle^=2,x,.)) y=y / lineattrs=(color=black pattern=1) group=id;
        polygonplot x=eval(ifn(circle=2,x,.)) y=y id=id / outlineattrs=(color=black pattern=1) display=(fill) group=type name='p2';
        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;
        DRAWOVAL X=&dist Y=0 WIDTH=%sysevalf(&radius) HEIGHT=%sysevalf(&radius) / display=(outline) outlineattrs=(color=black) heightunit=data 
            drawspace=datavalue widthunit=data;
            
        textplot x=xtext y=ytext text=text / textattrs=(size=16pt) position=center;
        discretelegend 'p1'  / exclude=(' ') location=inside halign=right valign=center border=false across=1 valueattrs=(size=18pt)
            displayclipped=true;
    endlayout;
    
    endgraph;
    end;
run;
ods graphics / reset scale=off imagename='pieofpie'; 
proc sgrender data=plot template=pieofpie;
run;

        

View solution in original post

5 REPLIES 5
RW9
Diamond | Level 26 RW9
Diamond | Level 26

Well, I don't know myself, have a look through this blog, its my goto to find anything graph orientated, and has examples:

http://blogs.sas.com/content/graphicallyspeaking/

 

It may be that you need to create two graphs side by side, only the right one with a legend:

https://blogs.sas.com/content/graphicallyspeaking/2012/03/26/let-them-eat-pie/

Something like that with two pie charts.

Scott_Mitchell
Quartz | Level 8

Thanks @RW9.  I have spent a fair chunk of the day going through that blog (and others).  It is a great resource.

 

I was also considering the concept of having 2 pie graphs side by side, but I doubt the stakeholders will buy into it.  They are pretty committed to the existing look and feel, but may have to make some concessions.

 

Thank you again. 

Ksharp
Super User

http://robslink.com/

 

@ Robert Allison  might make it happen via SAS/GRAPH .

 

UPDATED:

I know it is too old. 

I just want to post my code (PROC SGPLOT) for somebody who need it.

proc format;
value $fmt(default=80)
'Very Heavy (> 25)','Moderate (6-15)','Light (1-5)'=' other'; /*add a white blank before 'other' is to make 'other' appeared at first place in PERCENT_MAIN dataset*/
run;

data have;
 set sashelp.heart(keep=Smoking_Status rename=(Smoking_Status=type) where=(type is not missing));
 category=put(type,$fmt.);
run;
proc freq data=have noprint order=internal;
table category/out=percent_main;      /*get main pie percent*/
table category*Type/out=percent_all;  /*get sub pie percent*/
run;
/*process main pie*/
data main_temp;
set percent_main;
PERCENT=0.01*PERCENT;
if _n_=1 then percent_cum=PERCENT/2;  /*when category='other'*/
 else percent_cum+PERCENT;
keep category percent_cum PERCENT;
format PERCENT percent8.2;
run;
data main_pie;
 set main_temp;
 pi=constant('pi');
 lag_percent_cum=lag(percent_cum);

 id=_n_;x=0;y=0;output;
 if _n_=1 then do;  /*when category='other'*/
   do theta=-2*pi*percent_cum to 2*pi*percent_cum by 0.001;
     x=cos(theta);y=sin(theta); output;
   end;
 end;
 else do;
    do theta=2*pi*lag_percent_cum to 2*pi*percent_cum by 0.001;
     x=cos(theta);y=sin(theta); output;
   end;
 end;
 keep category id x y PERCENT;
run;
/*Keep line position*/
data line_pos(rename=(id=line_id x=line_x y=line_y));
 set main_pie(where=(id=1 and x ne 0)) end=last;
 if _n_=1 then do;output;x=2;y=-0.5;output;end;
 if last  then do;id=id+1;output;x=2;y=0.5;output;end;
 keep id x y;
run;


/*process sub pie*/
proc sql;
create table sub_temp as
select type,0.01*PERCENT as PERCENT format=percent8.2,PERCENT/sum(PERCENT) as new_percent
 from  percent_all
  where strip(category)='other';
quit;
data sub_temp;
set sub_temp(rename=(type=category));
percent_cum+new_percent;
keep category percent_cum PERCENT;
run;
data sub_pie;
 set sub_temp;
 pi=constant('pi');
 lag_percent_cum=coalesce(lag(percent_cum),0);

 id=10000+_n_;x=2;y=0;output;
 do theta=2*pi*lag_percent_cum to 2*pi*percent_cum by 0.001;
     x=2+0.5*cos(theta);y=0.5*sin(theta); output;
 end;
 keep category id x y PERCENT;
run;



/*Plot pie of pie chart*/
data pie;
 length category $ 200;
 set main_pie sub_pie line_pos;
run;
ods graphics/noborder ANTIALIAS ANTIALIASMAX=10000000 width=800px height=600px;
proc sgplot data=pie aspect=0.6;
series x=line_x y=line_y/group=line_id lineattrs=(color=black);
polygon id=id x=x y=y/group=category label=PERCENT labelattrs=(color=black size=12) fill nooutline dataskin=sheen
 LABELLOC=INSIDEBBOX  LABELPOS=CENTER name='x';
xaxis display=none;
yaxis display=none;
keylegend 'x'/location=inside position=se across=1 exclude=('other' ' ')  autoitemsize VALUEATTRS=(size=10);
run;

Ksharp_0-1709626140211.png

 

JeffMeyers
Barite | Level 11

Hello, I found this topic while I was hanging out at PharmaSUG and thought it looked like a fun challenge. I made an example of doing this with the CARS dataset within the SASHELP library.

 

The program gets the frequencies (I get rid of hybrid due to it's very small size in the example).  I make the pie slices with the Polygon plot, the outlines with series plot, and the percentages with TEXT plot.  Note I have to do a great bit of trigonometry to make this work where I assume the left circle has a radius of 1.  If you need to change the size of this circle then some changes to code would need to happen.  I force any percentages smaller than 8% into a separate group, and then make the second pie chart based on them.  I have a macro variable in a %let to determine the distance between the circles.  It should autosize the second circle based on this macro variable.  There are many tweaks I could do to make it look better, but I think it's a fun start.

pieofpie.png

 

 

proc freq data=sashelp.cars noprint;
    table type / out=frq;
    where type ^='Hybrid';
run;

proc sql;
    create table plota as
        select type,sum(percent) as percent from
        (select ifc(percent lt 8,' ',type) as type,(percent lt 8) as other,percent from frq)
        group by type;
    create table plotb as   
        select type,percent,percent/sum(percent) as ppt,sum(percent) as othpct from frq where percent lt 8;
quit;

%let dist=2;
data plota2 (drop=text xtext ytext) texta (keep=text xtext ytext);
    set plota end=last;
    retain endpoint id;
    percent=percent/100;
    if type=' ' then do;
        id=1;
        text=strip(put(percent*100,12.0))||'%';
        xtext=0.5*cos(0);ytext=0.5*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=1*cos(i);
            y=1*sin(i);
            output plota2;
        end;
        series=1;x=x*(&dist/cos(percent*constant('pi')));y=y*(&dist/cos(percent*constant('pi')));output plota2;
        endpoint=percent/2;
    end;
    else do;
        id=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=1*cos(i);
            y=1*sin(i);
            output plota2;
        end;
        if last then do;
            series=1;x=x*(&dist/cos(percent*constant('pi')));y=y*(&dist/cos(percent*constant('pi')));output plota2;
        end;
        else do;
            series=1;output plota2;        
        end;
        endpoint=endpoint+percent;
    end;
run;

data plotb2(drop=text xtext ytext) textb (keep=text xtext ytext);
    set plotb;
    retain endpoint id;
    if _n_=1 then do;
        endpoint=0;
        call symput('radius',strip(put(2*&dist*tan(othpct/100*constant('pi')),best12.)));
    end;
    id+1;
    text=strip(put(percent,12.0))||'%';
    xtext=&dist+0.5*(&dist*tan(othpct/100*constant('pi')))*cos(2*constant('pi')*(ppt/2+endpoint));
    ytext=0.5*(&dist*tan(othpct/100*constant('pi')))*sin(2*constant('pi')*(ppt/2+endpoint));output textb;
    series=1;x=&dist;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=&dist+(&dist*tan(othpct/100*constant('pi')))*cos(i);
        y=(&dist*tan(othpct/100*constant('pi')))*sin(i);
        output plotb2;
    end;
    series=1;output plotb2;
    endpoint=endpoint+ppt;
run;

data text;
    set texta textb;
run;
data plot;
    set plota2 (in=a) plotb2 (in=b);
    retain lastid;
    if a then lastid=id;
    else if b then do;
        circle=2;
        id=id+lastid;
    end;
run;

data plot;
    merge plot text;
    run;
ods path WORK.TEMPLAT(UPDATE) SASHELP.TMPLMST (READ);
proc template;
    define statgraph pieofpie;
    begingraph / designheight=5in designwidth=10in;
    
    layout overlay / xaxisopts=(display=none linearopts=(viewmin=-1.1 viewmax=4)) 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=type name='p1';
        seriesplot x=eval(ifn(series=1 and circle^=2,x,.)) y=y / lineattrs=(color=black pattern=1) group=id;
        polygonplot x=eval(ifn(circle=2,x,.)) y=y id=id / outlineattrs=(color=black pattern=1) display=(fill) group=type name='p2';
        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;
        DRAWOVAL X=&dist Y=0 WIDTH=%sysevalf(&radius) HEIGHT=%sysevalf(&radius) / display=(outline) outlineattrs=(color=black) heightunit=data 
            drawspace=datavalue widthunit=data;
            
        textplot x=xtext y=ytext text=text / textattrs=(size=16pt) position=center;
        discretelegend 'p1'  / exclude=(' ') location=inside halign=right valign=center border=false across=1 valueattrs=(size=18pt)
            displayclipped=true;
    endlayout;
    
    endgraph;
    end;
run;
ods graphics / reset scale=off imagename='pieofpie'; 
proc sgrender data=plot template=pieofpie;
run;

        
Scott_Mitchell
Quartz | Level 8

@JeffMeyers that is a truly outstanding solution.  I can't wait to try it.

 

Thanks also to @Ksharp and @RW9 for their valuable input.

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 5 replies
  • 2045 views
  • 2 likes
  • 4 in conversation