Venn diagram

Reply
Occasional Contributor
Posts: 16

Venn diagram

Hi,

I would like to draw a Venn diagram to illustrate the following situation

group A n=694

group B n=132

group n=410

group D n=6561

A and B n=74

A and C n=70

A and D n=146

Data and expected Venn diagram are attached. How can I do it by SAS (with numbers in the circles) and if it can be proportional (D is too big)? I saw some macros/codes from Global Forum... but not successful apply them.

Thank you

Attachment
Super User
Posts: 11,134

Re: Venn diagram

/*+-------------------------------------------------------+  
   |          SUGI 30 Sample                               |
   |          SAS MAPPING: Tips and tricks                 |
   |                                                       |
   |    NAME: vennmap.sas                                  |
   |   TITLE: Create a venn diagram with 3 overlapping     |
   |          circles.                                     |
   | PRODUCT: GRAPH                                        |
   |                                                       |
   |   NOTES: Last Update 24Feb05  ADM                     |
   +-------------------------------------------------------+*/

%let name=vennmap;
filename odsout '.';

goptions reset=global;

/*********************************************************************/   
/* The information below defines the how the Venn diagram will look  */
/* and how it will be labeled.  There are 3 overlapping circles.  The*/
/* top circle is #1.  The lower left circle is #2.  The lower right  */
/* circle is #3.  Overlap areas are named with all the circle numbers*/
/* that are included in the overlap.                                 */
/*********************************************************************/
/* The only thing that needs to be changed for your data are the     */
/* macro Variables below.                                            */
/*********************************************************************/ 

/* For our example - the following survey results:                  */
/*   39 people liked Product A                  #1                  */
/*   43 people liked Product B                  #2                  */
/*   56 people liked Product C                  #3                  */
/*   7 people liked Product A and B             #4                  */
/*   10 people liked Product A and C            #5                  */
/*   16 people liked Product B and C            #6                  */
/*   4 people liked Product A, B and C          #7                  */
/*   6 people didn't like any of the products   #8                  */

/* Text on graph for each circle  - Set your own values here*/
%let text1=Product A;  /* top circle */
%let text2=Product B;  /* lower left circle */ 
%let text3=Product C;  /* lower right circle */

/* Values to appear on each area  - Set your own values here */
/* This could be calculated with a datastep and call symput. */
%let value1=26;       /*#1 - #7 - (#4 - #7) - (#5 - #7)*/
%let value2=24;       /*#2 - #7 - (#4 - #7) - (#6 - #7)*/
%let value3=34;       /*#3 - #7 - (#5 - #7) - (#6 - #7)*/
%let value1_2=3;      /*#4 - #7*/
%let value2_3=12;     /*#6 - #7*/   
%let value1_3=6;      /*#5 - #7*/
%let value1_2_3=4;    /*#7*/

/* colors to use - probably don't need to change */
%let color1=cx5A5AE7;
%let color2=cxE75A5A;
%let color3=cx5AE75A;

/* ID variable value for each area - probably don't need to change */
%let label1=1 Only;
%let label2=2 Only;
%let label3=3 Only;
%let label1_2=1&2 Intersect;
%let label2_3=2&3 Intersect;
%let label1_3=1&3 Intersect;
%let label1_2_3=1&2&3 Intersect;

/* Circle centers.  These are based on a radius of 25 and circles */
/* overlap at the center of the next circle.  Don't change these  */
/* unless you have a need.                                        */
/* Triangle calc:  Sqrt((radius*radius)-((radius*radius)/4))      */
%let radius=25;
%let ypad=10;
%let x_center1=50;  /* 2 x radius;  just to give space on sides*/
%let y_center1=25+10+21.650635; /* radius + ypad + triangle calc*/
%let x_center2=50-(25/2);       /* x_center1 - half radius */
%let y_center2=25+10;           /* radius + ypad*/
%let x_center3=50+(25/2);       /* x_center1 + half radius */
%let y_center3=&y_center2;    

/* position of outside labels */
%let x_label1=27;
%let y_label1=73;
%let x_label2=13;
%let y_label2=25;
%let x_label3=87;
%let y_label3=25;

/********************************************************************/   
/* gen_respdata:  Macro to generate the response dataset.           */
/********************************************************************/   
%macro gen_respdata(respdata);
  /* Set ID and VALUE on each section of diagram */
  data &respdata;
    length area_id $30 value $30;
    area_id="&label1"; value="&value1"; output;
    area_id="&label2"; value="&value2"; output;
    area_id="&label3"; value="&value3"; output;
    area_id="&label1_2"; value="&value1_2"; output;
    area_id="&label2_3"; value="&value2_3"; output;
    area_id="&label1_3"; value="&value1_3"; output;
    area_id="&label1_2_3"; value="&value1_2_3"; output;
   run;

   /* Add tool tips and drill down */
   data &respdata;
     set &respdata;
     length htmlvar $500;
     htmlvar='alt='||quote(
                'ID:      '|| trim(left(area_id)) ||'0D'x||
                'Value:   '|| trim(left(value))
                 )  ||' '||
             'href="somewebpage.html"';
   run;
%mend gen_respdata;


/*********************************************************************/ 
/* gen_colors:  Macro to generate the overlapping colors based on the*/
/* colors of the 3 circles.  Just averages the colors.               */ 
/*********************************************************************/   
%macro gen_colors();
   data _null_;
     /* First get the rgb hex values of the 3 defined colors */
     red1=  input(substr("&color1",3,2),hex2.);
     green1=input(substr("&color1",5,2),hex2.);
     blue1= input(substr("&color1",7,2),hex2.);
     red2=  input(substr("&color2",3,2),hex2.);
     green2=input(substr("&color2",5,2),hex2.);
     blue2= input(substr("&color2",7,2),hex2.);
     red3=  input(substr("&color3",3,2),hex2.);
     green3=input(substr("&color3",5,2),hex2.);
     blue3= input(substr("&color3",7,2),hex2.);

     /* A factor to 'darken' the combined area colors.
     values 0->1 (1=no darkening, 0=totally darkened) */
     darken=.9;

     /* Calculate color for 1_2 intersection, and */
     /* store in macro variable */
     red=   ((red1+red2)/2)*darken;
     green= ((green1+green2)/2)*darken;
     blue=  ((blue1+blue2)/2)*darken;
     color1_2="cx"||put(red,hex2.)||put(green,hex2.)||put(blue,hex2.);
     call symput('color1_2',trim(left(color1_2)));

     /* Calculate color for 2_3 intersection, and */
     /* store in macro variable */
     red=   ((red2+red3)/2)*darken;
     green= ((green2+green3)/2)*darken;
     blue=  ((blue2+blue3)/2)*darken;
     color2_3="cx"||put(red,hex2.)||put(green,hex2.)||put(blue,hex2.);
     call symput('color2_3',trim(left(color2_3)));

     /* Calculate color for 1_3 intersection, and */
     /* store in macro variable */
     red=   ((red1+red3)/2)*darken;
     green= ((green1+green3)/2)*darken;
     blue=  ((blue1+blue3)/2)*darken;
     color1_3="cx"||put(red,hex2.)||put(green,hex2.)||put(blue,hex2.);
     call symput('color1_3',trim(left(color1_3)));

     /* Calculate color for 1_2_3 intersection, and
     /* store in macro variable */
     red=   ((red1+red2+red3)/3)*darken*darken;
     green= ((green1+green2+green3)/3)*darken*darken;
     blue=  ((blue1+blue2+blue3)/3)*darken*darken;
     color1_2_3="cx"||put(red,hex2.)||put(green,hex2.)||
                put(blue,hex2.);
     call symput('color1_2_3',trim(left(color1_2_3)));

   run;
%mend gen_colors;

/********************************************************************/ 
/* arc_calc:  Macro to generate arcs of various polygons            */
/* used by the venn_map macro.                                      */
/********************************************************************/ 
%macro arc_calc(center_x,center_y,start_angle,end_angle,direction);
   do degrees=&start_angle to &end_angle by &direction;
     radians=degrees*(atan(1)/45);
     x= &center_x + (radius * cos(radians));
     y= &center_y + (radius * sin(radians));
     output;
   end;
%mend arc_calc;

/********************************************************************/   
/* venn_map:  Generates the map dataset for the Venn diagram.       */
/********************************************************************/  
%macro venn_map(mapdata);
   /* Calculate the 3 arcs in each section of the diagram.  There */
   /* will be 7 areas in the graph.                               */
   data &mapdata;
      length area_id $30;
      radius=25;

      /* ----- Area 1 (top-most, area 1 only) ----- */
      area_id = "&label1";
        /* args are: center_x, center_y, start_angle, */
        /* end_angle, direction */
        %arc_calc(&x_center1, &y_center1, 0, 180, 1);
        /* bottom/left arc of area 1 */
        %arc_calc(&x_center2, &y_center2, 120, 60, -1);
        /* bottom/right arc of area 1 */
        %arc_calc(&x_center3, &y_center3, 120, 60, -1);

      /* ----- Area 2 (Left-most, area 2 only) ----- */
      area_id="&label2";
        /* big/left arc of area 2 */
        %arc_calc(&x_center2, &y_center2, 300, 120, -1);
        /* little/right/top arc of area 2 */
        %arc_calc(&x_center1, &y_center1, 180, 240, 1);
        /* little/right/bottom arc of area 2 */
        %arc_calc(&x_center3, &y_center3, 180, 240, 1);

      /* ----- Area 3 (Right-most, area 3 only) ----- */
      area_id="&label3";
        /* big/right arc of area 3 */
        %arc_calc(&x_center3, &y_center3, 60, -120, -1);
        /* little/left lower arc of area 3 */
        %arc_calc(&x_center2, &y_center2, -60, 0, 1);
        /* little/left/top arc of area 3 */
        %arc_calc(&x_center1, &y_center1, -60, 0, 1);

      /* ----- Area 1&2 Intersection ----- */
      area_id="&label1_2";
        /* lower/left arc */
        %arc_calc(&x_center1, &y_center1, 180, 240, 1);
        /* lower/right arc */
        %arc_calc(&x_center3, &y_center3, 180, 120, -1);
        /* topmost arc */
        %arc_calc(&x_center2, &y_center2, 60, 120, 1);

      /* ----- Area 2&3 Intersection ----- */
      area_id="&label2_3";
        /* lower/left arc */
        %arc_calc(&x_center3, &y_center3, 180, 240, 1);
        /* lower/right arc */
        %arc_calc(&x_center2, &y_center2, -60, 0, 1);
        /* topmost arc */
        %arc_calc(&x_center1, &y_center1, 300, 240, -1);

      /* ----- Area 1&3 Intersection ----- */
      area_id="&label1_3";
        /* topmost arc */
        %arc_calc(&x_center3, &y_center3, 60, 120, 1);
        /* lower/left arc */
        %arc_calc(&x_center2, &y_center2, 60, 0, -1);
        /* lower/right arc */
        %arc_calc(&x_center1, &y_center1, 300, 360, 1);

      /* ----- Area 1&2&3 Intersection (center) ----- */
      area_id="&label1_2_3";
        /* upper/right arc */
        %arc_calc(&x_center2, &y_center2, 0, 60, 1);
        /* upper/left arc */
        %arc_calc(&x_center3, &y_center3, 120, 180, 1);
        /* lower/bottom arc */
        %arc_calc(&x_center1, &y_center1, 240, 300, 1);
run;
%mend venn_map;


/********************************************************************/ 
/* gen_anno:  Generate a centroid for each polygon and place value  */
/* at the centroid.  Also, place labels outside.                    */
/********************************************************************/ 
%macro gen_anno(anno_values,anno_labels,mapdata,respdata);
    proc sql;
        /* Get the average x/y center coordinates of each piece */
        create table centroid as
           select unique area_id, avg(x) as x, avg(y) as y
           from &mapdata group by area_id;
        /* Merge the centers with the 'response' data */
        create table &respdata as
           select unique &respdata..*, centroid.x, centroid.y
           from &respdata left join centroid
           on &respdata..area_id = centroid.area_id;
    quit;
    run;
    data &anno_values;
       set &respdata;
       length text $20 style $12 function $12 color $12;
       xsys='2'; ysys='2'; hsys='3'; when='A';
       function='label'; position='5'; style='"arial/bo"';
       size=4; color='white';
       text=trim(left(value));
    run;

   /* Place labels outside circles */
   data &anno_labels;
      length text $20 style $12 function $12 color $12;
      xsys='2'; ysys='2'; hsys='3'; when='A';
      function='label'; style='"arial/bo"'; size=4; color='black';
      text="&text1"; position='4'; x=&x_label1;y=&y_label1; output;
      text="&text2"; position='4'; x=&x_label2;y=&y_label2; output;
      text="&text3"; position='6'; x=&x_label3;y=&y_label3; output;
   run;
%mend;

%macro do_map(mapdata, respdata, anno_labels, anno_values);
  %*GOPTIONS DEVICE=gif;

%* ODS LISTING CLOSE;
%* ODS HTML path=odsout body="&name..htm"
  style=minimal
  ;

  goptions htitle=6pct htext=4pct ftitle="arial/bo" ftext="arial";
  goptions cback=white border;

  title "Product Taste Test - Venn Diagram";
  title2 "Consumers who liked each product";

  /* Use the 'midpoints=' gmap option to guarantee the colors will
     be assigned in the desired order (otherwise they will be
     assigned alphabetically by &labeln values. */
  pattern1 v=s c=&color1;
  pattern2 v=s c=&color2;
  pattern3 v=s c=&color3;
  pattern4 v=s c=&color1_2;
  pattern5 v=s c=&color2_3;
  pattern6 v=s c=&color1_3;
  pattern7 v=s c=&color1_2_3;

  proc gmap map=&mapdata data=&respdata anno=&anno_labels all;
    id area_id;
    choro area_id /
          nolegend
          /* This forces the colors to be in the same order of the */
          /* pattern statement.                                    */
          midpoints=
              "&label1"
              "&label2"
              "&label3"
              "&label1_2"
              "&label2_3"
              "&label1_3"
              "&label1_2_3"
          coutline=black
          woutline=2
          anno=&anno_values
          html=htmlvar
          des=""
          name="&name";
  run;


  quit;
  %* ODS HTML CLOSE;
  %* ODS LISTING;
%mend;


/********************************************************************/ 
/* Do the real work here.                                           */
/********************************************************************/ 
%gen_respdata(respdata);  /* create/prepare response dataset */
%gen_colors();            /* set up colors */
%venn_map(mapdata);       /* create map dataset */
%gen_anno(anno_values, anno_labels, mapdata, respdata);/*annotate data*/
%do_map(mapdata, respdata, anno_labels, anno_values); /* draw map */

PROC Star
Posts: 7,433

Re: Venn diagram

You might also want to take a look at the macro provided in the following paper: http://support.sas.com/resources/papers/proceedings13/243-2013.pdf

Occasional Contributor
Posts: 16

Re: Venn diagram

Thank you!

Respected Advisor
Posts: 4,817

Re: Venn diagram

In case you wonder what a diagram respecting the circle and intersection areas would look like, here it is...

Venn diagram.png

Done with FCMP, MDS and SGANNO (i.e. a lot of overkill !)

PG

PG
Ask a Question
Discussion stats
  • 4 replies
  • 1064 views
  • 7 likes
  • 4 in conversation