BookmarkSubscribeRSS Feed
xxformat_com
Barite | Level 11

Hi,

 

Is there a way to create a donut with GTL and have several rings in this donut?

Right now I can only create it using proc gchart with SAS 9.2 functionalities/limits.

 

Best Regards,

7 REPLIES 7
ballardw
Super User

Since I don't see DONUT in GTL for 9.4 I suspect not.

 

You might investigate the Data Step Graphic Interface which allows a lot of control at the expense of programming complexity.

 

Or Annotate graphics.

 

JeffMeyers
Barite | Level 11

Are you trying to make something that looks like the following? Example Graph

xxformat_com
Barite | Level 11

Hi, I'm able to add a white circle in the middle of an existing pie but here I was looking for GTL because I'm more interesting in the subgroup functionality of donut.

 

I'm sure you can do anything with Data Step Graphic Interface but with no experience with it, and no example to adapt to my case, it sounds like spending lots of hours for just one graph.

 

Right now with proc gchart I found several limitations including

1/ getting different colors for different rings

2/ not being able to get rid of the arrow lines associated with each ring.

 

I also looked into having several pies with GTL, on the top of each other, with different diameter,

but I could not find a way to overlay several pies and change the diameter.

donut_example.JPG

 

 

JeffMeyers
Barite | Level 11

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.

_donut.png

 




%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);
xxformat_com
Barite | Level 11

9.4 is all fine for me. Thanks.

JeffMeyers
Barite | Level 11
Sounds good. Your title showed 9.3 so I was trying to code it to work in that as well.
xxformat_com
Barite | Level 11

My mistake: I just put 9.3 because GTL was introduced with 9.3. I've updated the discussion title to "SAS 9.3 or 9.4".

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
  • 7 replies
  • 1955 views
  • 1 like
  • 3 in conversation