/*----------------------------------------------------------------*
 | FOURFOLD.SAS   IML modules for four-fold                       |
 |                display of 2x2xK tables                         |
 | Usage:                                                         |
 |   proc iml;                                                    |
 |   %include fourfold;                                           |
 |   run fourfold( dim, table, vnames, lnames );                  |
 | where:                                                         |
 |   dim      table dimensions: {2 2 k}                           |
 |   table    two- or three-way contingency table,                |
 |            of size (2k)x2                                      |
 |   vnames   variable names, 1x3 character matrix.               |
 |            vnames[,1]=column variable                          |
 |            vnames[,2]=row variable                             |
 |            vnames[,3]=panel variable                           |
 |   lnames   category names, 3 x k character matrix.             |
 |            vnames[1,]=col categories,                          |
 |            lnames[2,]=row categories,                          |
 |            lnames[3,]=panel categories.                        |
 |global input variables:                                         |
 |   std      ='MARG' standardizes each 2x2 table to equal        |
 |            margins, keeping the odds ratio fixed.              |
 |            ='MAX' standardizes each table to a maximum         |
 |            cell frequency of 100.                              |
 |            ='MAXALL' standardizes all tables to                |
 |            max(f[i,j,k])=100.                                  |
 |   down     number of panels down each page                     |
 |   across   number of panels across each page                   |
 |   sangle   angle for side labels (0|90)                        |
 *----------------------------------------------------------------*
 *  Author:  Michael Friendly              *
 * Created:  9 May 1991 19:12:12      Copyright (c) 1992          *
 * Revised:  12 Nov 1993 14:59:39                                 *
 * Version:  1.5                                                  *
 *----------------------------------------------------------------*/
goptions gunit=pct;
start fourfold(dim, table, vnames, lnames)
  global (std, down, across, name, sangle );
  if type(std  )  ^='C' then std='MARG';
  if type(down)   ^='N' then down=2;
  if type(across) ^='N' then across=1;
  if type(name)   ^='C' then name='FFOLD';
  if type(sangle) ^='N' then sangle=0;

  /*-- Establish viewports --*/
  np = max(down,across);
  pd = np - (across||down);
  size = int(100 / np);
  do i = 1 to across;
     px = size # ((i-1) // i) + (pd[1] # size/2);
     do j = 1 to down;
        py = 100 - (size # (j//(j-1)) + (pd[2] # size/2));
        ports = ports // shape( (px||py), 1);
     end;
  end;
  nport=nrow(ports);

  run odds(dim, table, lnames, odds);
  if ncol(dim)<3 then k=1;     * number of panels;
                 else k = dim[3];
  page = 0;                    * number of pages;
  do i=1 to k;
     r = 2#i;                  * row index, this table;
     t=table[((r-1):r),];      * current 2x2 table;

     /* construct top label for this panel */
     title='';
     if k > 1 then do;
        if vnames[,3] = " " then title = lnames[3,i];
        else title=trim(vnames[,3])+': '+lnames[3,i];
        end;

     /* standardize table to fit 100x100 square */
     run stdize(fit, t, table);

     print title;
     print fit[c=(lnames[1,]) r=(lnames[2,]) f=8.2] ' '
             t[c=(lnames[1,]) r=(lnames[2,]) f=8.0] ;

     /*-- start new page if needed --*/
     if mod(i,nport)=1 then do;
        call gstart;
        page = page+1;                * count pages;
        gname =trim(name)+char(page,1,0);
        call gopen(gname);            * name uniquely;
        end;

     /*-- set viewport --*/
     ip = 1 + mod(i-1,nport);         * viewport number;
     port = ports[ip,];               * coordinates;
     call gport(port);

     /*-- draw this panel, display if end-of page --*/
     call gpie2x2(fit, t, lnames, vnames, title,
                  np, odds[i]);
     if mod(i,nport)=0 | i=k then call gshow;
   end;
   call gclose;
finish;

start stdize(fit, t, table) global(std);
  /*-- standardize table to equal margins --*/
  if std='MARG' then do;
     config = {1 2};
     newtab = {50 50 , 50 50 };
     call ipf(fit,status,{2 2},newtab,config,t);
  end;

  /*-- standardize to largest cell in EACH table --*/
  else if std='MAX' then do;
     n = t[+,+];
     fit = (t/n)#200 ;
     fit = fit# 100/max(fit);
  end;

  /*-- standardize to largest cell in ALL tables --*/
  else if std='MAXALL' then do;
  fit = t # 100 / max(table);
  end;
  else fit = t;   /* raw counts */
finish;

start gpie2x2(tab,freq,lnames,vnames,title,np,d)
      global(sangle);
  /*-- Draw one fourfold display --*/
  t = shape(tab,1,4);     * vector of scaled frequencies;
  r = 5 * sqrt(t);        * radii of each quarter circle;

  /*-- set graphic window, font, and text height */
  call gwindow({-16 -16 120 120});
  call gset('FONT','DUPLEX');
  ht = 2.0 # max(np,2);
  call gset('HEIGHT',ht);

  /*-- set shading patterns for each quadrant */
  /* cell:[1,1] [1,2] [2,1] [2,2] */
  angle1 = { 90    0   180   270 };
  angle2 = {180   90   270     0 };
  shade  = {'L1' 'X1'  'X1'  'L1',
            'X1' 'L1'  'L1'  'X1'}[1+(d>0),];

  /*-- draw quarter circles, with color and shading */
  do i = 1 to 4;
     pat = shade[,i];
     if pat='X1' then color='BLUE';
                 else color='RED';
     call gpie(50,50, r[i], angle1[i], angle2[i],
               color, 3, pat);
     end;

  /*-- draw frame and axes --*/
  call gxaxis({0 50},100,11,1,1);
  call gyaxis({50 0},100,11,1,1);
  call ggrid({0 100}, {0 100});

  /*-- set label coordinates, angles --*/
  lx = { 50, -.5, 50, 101};
  ly = { 99, 50, -1,  50};
  ang= {  0,  0,  0,   0};
  /*-- label justification position --*/
       /*  ab  lt  bl   rt  */
  posn = {  2,  4,  8,   6};
  if vnames[,1] = " " then vl1 = '';
     else vl1= trim(vnames[,1])+': ';
  vl2='';

  /*-- are side labels rotated? --*/
  if sangle=90 then do;
     ang[{2 4}] = sangle;
     posn[ {2 4}] = {2 8};
     if vnames[,2] ^= " "
        then vl2= trim(vnames[,2])+': ';
     end;
  labels = (vl2 + lnames[2,1])//
           (vl1 + lnames[1,1])//
           (vl2 + lnames[2,2])//
           (vl1 + lnames[1,2]);
  run justify(lx,ly,labels,ang,posn,ht,xnew,ynew,len);

  /*-- write actual frequencies in the corners --*/
  cells = char(shape(freq,4,1),4,0);
  lx = {  5, 95,  5, 95};
  ly = { 94, 94,  4,  4};
  ang= {  0,  0,  0,  0};
  posn={  6,  4,  6,  4};
  run justify(lx,ly,cells,ang,posn,ht,xnew,ynew,len);

  /*-- write panel title centered above */
  if length(title)>1 then do;
     ht=1.25#ht;
     call gstrlen(len,title,ht);
     call gscript((50-len/2),112,title,,,ht);
  end;
finish;

start justify(x, y, labels, ang, pos, ht, xnew, ynew, len);
  /* Justify strings a la Annotate POSITION variable.
     x, y, labels, ang and pos are equal-length vectors.
     Returns justified coordinates (xnew, ynew)
  */

  n = nrow(x);
  call gstrlen(len,labels);
  xnew = x; ynew =  y;

  /* x and y offset factors for each position   */
  /*       1   2   3   4   5   6   7   8    9   */
  off1 = {-1 -.5   0  -1 -.5   0  -1   -.5   0  };
  off2 = { 1   1   1   0   0   0  -1.6 -1.6 -1.6};

  do i=1 to n;
     if ang[i]=0 then do;
        xnew[i] = x[i] + (off1[pos[i]]#len[i]);
        ynew[i] = y[i] + (off2[pos[i]]#ht);
     end;
     else if ang[i]=90 then do;
        ynew[i] = y[i] + (off1[pos[i]]#len[i]);
        xnew[i] = x[i] - (off2[pos[i]]#ht);
     end;
  call gscript(xnew[i], ynew[i], labels[i], ang[i]);
  end;
  len = round(len,.01);
finish;

start odds(dim, table, lnames, odds);
  /*-- Calculate odds ratios for 2x2xK table --*/
  k = dim[3];
  do i=1 to k;
     r = 2#i;
     t=table[((r-1):r),];
     odds = odds // ( t[1,1]#t[2,2] / (t[1,2]#t[2,1]) );
     end;
  rl = lnames[3,];
  odds = odds || log(odds);

  title= 'Odds (' + trim(lnames[1,1]) + '|' + trim(lnames[2,1])
         + ') / ('+ trim(lnames[1,1]) + '|' + trim(lnames[2,2]) + ')';
  reset noname;
  print title;
  print odds[r=rl c={'Odds Ratio' 'Log Odds'} format=10.4];
  reset name;

  odds = odds[,2];      *-- return log odds ratios;
finish;