Data visualization with SAS programming

Pie of a Pie Chart

Accepted Solution Solved
Reply
Super Contributor
Posts: 302
Accepted Solution

Pie of a Pie Chart

[ Edited ]

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

 


Accepted Solutions
Solution
3 weeks ago
Occasional Contributor
Posts: 7

Re: Pie of a Pie Chart

Posted in reply to Scott_Mitchell

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


All Replies
Super User
Super User
Posts: 9,211

Re: Pie of a Pie Chart

Posted in reply to Scott_Mitchell

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.

Super Contributor
Posts: 302

Re: Pie of a Pie Chart

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. 

Super User
Posts: 10,615

Re: Pie of a Pie Chart

Posted in reply to Scott_Mitchell

http://robslink.com/

 

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

Solution
3 weeks ago
Occasional Contributor
Posts: 7

Re: Pie of a Pie Chart

Posted in reply to Scott_Mitchell

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;

        
Super Contributor
Posts: 302

Re: Pie of a Pie Chart

Posted in reply to JeffMeyers

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

 

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

☑ This topic is solved.

Need further help from the community? Please ask a new question.

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