Difference between revisions of "WG5 P02 SAS Box Plot Example Script"

From PHUSE Wiki
Jump to: navigation, search
(Created page with "/******************************************************************************* *** PROGRAM: WPCT-F.07.01-example.sas *** *** DESCRIPTION: Example of Figure 7.1 from Me...")
 
Line 1: Line 1:
/*******************************************************************************
+
  /*******************************************************************************
***  PROGRAM:    WPCT-F.07.01-example.sas
+
  ***  PROGRAM:    WPCT-F.07.01-resolved.sas
***
+
  ***
***  DESCRIPTION: Example of Figure 7.1 from Measures of Central Tendency white paper
+
  ***  DESCRIPTION: Example of Figure 7.1 from Measures of Central Tendency white paper
***
+
  ***
***  INSTRUCTIONS FOR USER:
+
  ***  INSTRUCTIONS FOR USER:
***          If you cannot directly access the github XPORT files (below) from you environment,  
+
  ***          If you cannot directly access the github XPORT files (below) from you environment,  
***          then store them locally and modify the initial data-access code accordingly
+
  ***          then store them locally and modify the initial data-access code accordingly
***
+
  ***
***  INPUT:  Test data (sourced from github)
+
  ***  INPUT:  Test data (sourced from github)
***          https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/adsl.xpt
+
  ***          https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/adsl.xpt
***          https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/advs.xpt
+
  ***          https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/advs.xpt
***
+
  ***
***  OUTPUT:
+
  ***  OUTPUT:
***
+
  ***
***  AUTHOR: CSS/PhUSE Standard Scripts Working Group
+
  ***  AUTHOR: CSS/PhUSE Standard Scripts Working Group
***          http://www.phusewiki.org/wiki/index.php?title=WG5_Project_02
+
  ***          http://www.phusewiki.org/wiki/index.php?title=WG5_Project_02
***
+
  ***
*******************************************************************************/
+
  *******************************************************************************/
  
/* ACCESS CSS/PhUSE test data from github */
+
  /* ACCESS CSS/PhUSE test data from github */
  filename source url "https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/adsl.xpt";
+
    filename source url "https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/adsl.xpt";
    *--- alternative: access local copy of test data ---*;
+
      *--- alternative: access local copy of test data ---*;
    * filename source '<YOUR-PATH-AS-NEEDED> adsl.xpt' ;
+
      * filename source '<YOUR-PATH-AS-NEEDED> adsl.xpt' ;
  libname source xport access=READONLY;
+
    libname source xport access=READONLY;
  
    data css_adsl;
+
      data css_adsl;
      set source.adsl;
+
        set source.adsl;
    run;
+
      run;
    filename source clear;
+
      filename source clear;
    libname source clear;
+
      libname source clear;
  
  filename source url "https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/advs.xpt";
+
    filename source url "https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/advs.xpt";
    *--- alternative: access local copy of test data ---*;
+
      *--- alternative: access local copy of test data ---*;
    * filename source '<YOUR-PATH-AS-NEEDED> advs.xpt' ;
+
      * filename source '<YOUR-PATH-AS-NEEDED> advs.xpt' ;
  libname source xport access=READONLY;
+
    libname source xport access=READONLY;
  
    data css_advs;
+
      data css_advs;
      set source.advs;
+
        set source.advs;
      where (paramcd in ('DIABP') and atptn in (815));
+
        where (paramcd in ('DIABP') and atptn in (815));
    run;
+
      run;
    filename source clear;
+
      filename source clear;
    libname source clear;
+
      libname source clear;
  
*--- SHORTEN treatment names for display ---*;
+
  *--- SHORTEN treatment names for display ---*;
  data adsl_sub (rename=(trt01p_short=trt01p));
+
    data adsl_sub (rename=(trt01p_short=trt01p));
    set css_adsl;
+
      set css_adsl;
  
    length trt01p_short $6;
+
      length trt01p_short $6;
    select (trt01p);
+
      select (trt01p);
      when ('Placebo') trt01p_short = 'P';
+
        when ('Placebo') trt01p_short = 'P';
      when ('Xanomeline High Dose') trt01p_short = 'X-high';
+
        when ('Xanomeline High Dose') trt01p_short = 'X-high';
      when ('Xanomeline Low Dose') trt01p_short = 'X-low';
+
        when ('Xanomeline Low Dose') trt01p_short = 'X-low';
      otherwise trt01p_short = 'UNEXPECTED';
+
        otherwise trt01p_short = 'UNEXPECTED';
    end;
+
      end;
  
    drop trt01p;
+
      drop trt01p;
  run;
+
    run;
  
  data advs_sub (rename=(trtp_short=trtp));
+
    data advs_sub (rename=(trtp_short=trtp));
    set css_advs;
+
      set css_advs;
  
    length trtp_short $6;
+
      length trtp_short $6;
    select (trtp);
+
      select (trtp);
      when ('Placebo') trtp_short = 'P';
+
        when ('Placebo') trtp_short = 'P';
      when ('Xanomeline High Dose') trtp_short = 'X-high';
+
        when ('Xanomeline High Dose') trtp_short = 'X-high';
      when ('Xanomeline Low Dose') trtp_short = 'X-low';
+
        when ('Xanomeline Low Dose') trtp_short = 'X-low';
      otherwise trtp_short = 'UNEXPECTED';
+
        otherwise trtp_short = 'UNEXPECTED';
    end;
+
      end;
  
    drop trtp;
+
      drop trtp;
  run;
+
    run;
  
*--- Restrict analysis to SAFETY POP and ANALYSIS RECORDS (&a_fl) ---*;
+
  *--- Restrict analysis to SAFETY POP and ANALYSIS RECORDS (&a_fl) ---*;
  data css_asldata;
+
    data css_asldata;
    set adsl_sub (keep=STUDYID USUBJID SAFFL TRT01P TRT01PN);
+
      set adsl_sub (keep=STUDYID USUBJID SAFFL TRT01P TRT01PN);
    where SAFFL = 'Y';
+
      where SAFFL = 'Y';
  run;
+
    run;
  
  data css_anadata;
+
    data css_anadata;
    set advs_sub (keep=STUDYID USUBJID SAFFL ANL01FL TRTP TRTPN PARAM PARAMCD  
+
      set advs_sub (keep=STUDYID USUBJID SAFFL ANL01FL TRTP TRTPN PARAM PARAMCD  
                      AVAL ANRLO ANRHI AVISIT AVISITN ATPT ATPTN);
+
                        AVAL ANRLO ANRHI AVISIT AVISITN ATPT ATPTN);
    where SAFFL = 'Y' and ANL01FL = 'Y';
+
      where SAFFL = 'Y' and ANL01FL = 'Y';
  run;
+
    run;
  
*--- Calculate summary statistics, and merge onto measurement data for use as "block" variables ---*;
+
  *--- Calculate summary statistics, and merge onto measurement data for use as "block" variables ---*;
  proc sort data=css_anadata;
+
    proc sort data=css_anadata;
    by avisitn trtpn;
+
      by avisitn trtpn;
  run;
+
    run;
  
  proc summary data=css_anadata noprint;
+
    proc summary data=css_anadata noprint;
    by avisitn trtpn;
+
      by avisitn trtpn;
    var AVAL;
+
      var AVAL;
    output out=css_stats (drop=_type_) n=n mean=mean std=std median=median min=min max=max q1=q1 q3=q3;
+
      output out=css_stats (drop=_type_) n=n mean=mean std=std median=median min=min max=max q1=q1 q3=q3;
  run;
+
    run;
  
  data css_plot (rename=(avisit=_PHASE_));
+
    data css_plot (rename=(avisit=_PHASE_));
    merge css_anadata (in=in_paramcd) css_stats (in=in_stats);
+
      merge css_anadata (in=in_paramcd) css_stats (in=in_stats);
    by avisitn trtpn;
+
      by avisitn trtpn;
    label n = 'n' mean = 'Mean' std = 'Std Dev' min = 'Min' q1 = 'Q1' median = 'Median' q3 = 'Q3' max = 'Max';
+
      label n = 'n' mean = 'Mean' std = 'Std Dev' min = 'Min' q1 = 'Q1' median = 'Median' q3 = 'Q3' max = 'Max';
  run;
+
    run;
  
*--- PROC SHEWHART reads "phases" (visits) from a special _PHASE_ variable ---*;
+
  *--- PROC SHEWHART reads "phases" (visits) from a special _PHASE_ variable ---*;
  data css_plot_tp;
+
    data css_plot_tp;
    set css_plot end=NoMore;
+
      set css_plot end=NoMore;
    by avisitn trtpn;
+
      by avisitn trtpn;
    keep AVAL timept trtpn trtp ANRLO ANRHI max q3 median q1 min std mean n _PHASE_;
+
      keep AVAL timept trtpn trtp ANRLO ANRHI max q3 median q1 min std mean n _PHASE_;
    retain timept 0;
+
      retain timept 0;
  
    if first.avisitn then timept = floor(timept + 1);
+
      if first.avisitn then timept = floor(timept + 1);
    if first.trtpn then timept + (1/(1+3));
+
      if first.trtpn then timept + (1/(1+3));
  
    *--- Create BOXPLOT_TIMEPT_RANGES, to limit number of boxes per plot page to &MAX_BOXES_PER_PAGE ---*;
+
      *--- Create BOXPLOT_TIMEPT_RANGES, to limit number of boxes per plot page to &MAX_BOXES_PER_PAGE ---*;
    length boxplot_timept_ranges $200;
+
      length boxplot_timept_ranges $200;
    retain max_boxes_per_page 20 boxes_on_page 0 last_timept_end 0 boxplot_timept_ranges ' ';
+
      retain max_boxes_per_page 20 boxes_on_page 0 last_timept_end 0 boxplot_timept_ranges ' ';
  
    if last.avisitn then do;
+
      if last.avisitn then do;
      boxes_on_page = boxes_on_page + 3;
+
        boxes_on_page = boxes_on_page + 3;
      if boxes_on_page + 3 > max_boxes_per_page then do;
+
        if boxes_on_page + 3 > max_boxes_per_page then do;
  
        *--- Current visit is enough for this plot, no more boxes, next visit will be too much ---*;
+
          *--- Current visit is enough for this plot, no more boxes, next visit will be too much ---*;
        boxplot_timept_ranges = strip(boxplot_timept_ranges) !!strip(compbl( put(last_timept_end,8.-L) !!' <= timept < '!! put(ceil(timept),8.-L) )) !!'|';
+
          boxplot_timept_ranges = strip(boxplot_timept_ranges) !!strip(compbl( put(last_timept_end,8.-L) !!' <= timept < '!! put(ceil(timept),8.-L) )) !!'|';
        boxes_on_page = 0;
+
          boxes_on_page = 0;
         last_timept_end = timept;
+
          last_timept_end = timept;
 +
         end;
 +
        else if NoMore then do;
 +
          boxplot_timept_ranges = strip(boxplot_timept_ranges) !!strip(compbl( put(last_timept_end,8.-L) !!' <= timept < ' !!put(timept,8.-L) ))!!'|';
 +
        end;
 
       end;
 
       end;
      else if NoMore then do;
 
        boxplot_timept_ranges = strip(boxplot_timept_ranges) !!strip(compbl( put(last_timept_end,8.-L) !!' <= timept < ' !!put(timept,8.-L) ))!!'|';
 
      end;
 
    end;
 
  
    if NoMore then call symput('boxplot_timept_ranges', strip(boxplot_timept_ranges));
+
      if NoMore then call symput('boxplot_timept_ranges', strip(boxplot_timept_ranges));
  run;
+
    run;
  
*--- ANNOTATE normal range outliers on box plot ---*;
+
  *--- ANNOTATE normal range outliers on box plot ---*;
  data css_annotate (keep=function hsys text size color x xsys y ysys);
+
    data css_annotate (keep=function hsys text size color x xsys y ysys);
    set css_plot_tp;
+
      set css_plot_tp;
    function = 'SYMBOL';
+
      function = 'SYMBOL';
    * On the horizontal axis (typically TimePT), introduce user-selected jitter *;
+
      * On the horizontal axis (typically TimePT), introduce user-selected jitter *;
    x = TIMEPT;
+
      x = TIMEPT;
    y = AVAL;
+
      y = AVAL;
    * jitter size is based on ±8% of spacing used for PROC SHEWHART boxplot., *;
+
      * jitter size is based on ±8% of spacing used for PROC SHEWHART boxplot., *;
    jitter = 0.08 / (1+3);
+
      jitter = 0.08 / (1+3);
    jitter = ranuni(29507) * jitter;
+
      jitter = ranuni(29507) * jitter;
    if ranuni(41385) > 0.5 then jitter = -1 * jitter;
+
      if ranuni(41385) > 0.5 then jitter = -1 * jitter;
    x = x + jitter;
+
      x = x + jitter;
    * SIZE variable is percentage of graphic space *;
+
      * SIZE variable is percentage of graphic space *;
    hsys = '3';
+
      hsys = '3';
    size = 1;
+
      size = 1;
    * X and Y variables contain data values *;
+
      * X and Y variables contain data values *;
    xsys = '2';
+
      xsys = '2';
    ysys = '2';
+
      ysys = '2';
    * ANNOTATE non-missing obs with valid LOW and HIGH limits *;
+
      * ANNOTATE non-missing obs with valid LOW and HIGH limits *;
    if (n(y, ANRLO) = 2 and y < ANRLO) or (n(ANRHI, y) = 2 and ANRHI < y);
+
      if (n(y, ANRLO) = 2 and y < ANRLO) or (n(ANRHI, y) = 2 and ANRHI < y);
    * Draw a filled DOT with user-selected COLOR *;
+
      * Draw a filled DOT with user-selected COLOR *;
    length color $5 text $6;
+
      length color $5 text $6;
    color = 'RED';
+
      color = 'RED';
    text = 'DOT';
+
      text = 'DOT';
    OUTPUT;
+
      OUTPUT;
  run;
+
    run;
  
*--- Graphics Settings - Default HSIZE and VSIZE are suitable for A4 and letter ---*;
+
  *--- Graphics Settings - Default HSIZE and VSIZE are suitable for A4 and letter ---*;
  options orientation=landscape;
+
    options orientation=landscape;
  goptions reset=all hsize=11.5in vsize=7.5in;
+
    goptions reset=all hsize=11.5in vsize=7.5in;
  title justify=left height=1.2    'Box Plot - Diastolic Blood Pressure (mmHg) by Visit, Analysis Timepoint: AFTER LYING DOWN FOR 5 MINUTES';
+
    title justify=left height=1.2    'Box Plot - Diastolic Blood Pressure (mmHg) by Visit, Analysis Timepoint: AFTER LYING DOWN FOR 5 MINUTES';
  footnote1 justify=left height=1.0 'Box plot type=schematic, the box shows median, interquartile range (IQR, edge of the bar), min and max';
+
    footnote1 justify=left height=1.0 'Box plot type=schematic, the box shows median, interquartile range (IQR, edge of the bar), min and max';
  footnote2 justify=left height=1.0 'within 1.5 IQR below 25% and above 75% (ends of the whisker). Values outside the 1.5 IQR below 25% and';
+
    footnote2 justify=left height=1.0 'within 1.5 IQR below 25% and above 75% (ends of the whisker). Values outside the 1.5 IQR below 25% and';
  footnote3 justify=left height=1.0 'above 75% are shown as outliers. Means plotted as different symbols by treatments.';
+
    footnote3 justify=left height=1.0 'above 75% are shown as outliers. Means plotted as different symbols by treatments.';
  axis1 value=none label=none major=none minor=none;
+
    axis1 value=none label=none major=none minor=none;
  axis2 order=(40 to 110 by 10);
+
    axis2 order=(40 to 110 by 10);
  
*--- OPEN PDF output destination ---*;
+
  *--- OPEN PDF output destination ---*;
  ods pdf file='WPCT-F.07.01_Box_plot_DIABP_by_visit_for_timepoint_815.pdf';
+
    ods pdf file='WPCT-F.07.01_Box_plot_DIABP_by_visit_for_timepoint_815.pdf';
  
*--- CREATE PDF box plot ---*;
+
  *--- CREATE PDF box plot ---*;
  proc shewhart data=css_plot_tp (where=( 0 <= timept < 7 ));
+
    proc shewhart data=css_plot_tp (where=( 0 <= timept < 7 ));
    boxchart AVAL * timept (max q3 median q1 min std mean n trtp) = trtp /  
+
      boxchart AVAL * timept (max q3 median q1 min std mean n trtp) = trtp /  
      annotate = css_annotate (where=( 0 <= x < 7 ))  
+
        annotate = css_annotate (where=( 0 <= x < 7 ))  
      boxstyle = schematic  
+
        boxstyle = schematic  
      notches  
+
        notches  
      stddeviations  
+
        stddeviations  
      nolegend  
+
        nolegend  
      ltmargin = 5  
+
        ltmargin = 5  
      blockpos = 3  
+
        blockpos = 3  
      blocklabelpos = left  
+
        blocklabelpos = left  
      blocklabtype=scaled  
+
        blocklabtype=scaled  
      blockrep
+
        blockrep
      haxis=axis1  
+
        haxis=axis1  
      vaxis=axis2  
+
        vaxis=axis2  
      vref=60 90  
+
        vref=60 90  
      cvref=RED  
+
        cvref=RED  
      idsymbol=square  
+
        idsymbol=square  
      idcolor=black  
+
        idcolor=black  
      nolimits  
+
        nolimits  
      readphase = all  
+
        readphase = all  
      phaseref  
+
        phaseref  
      phaselabtype=scaled  
+
        phaselabtype=scaled  
      phaselegend;
+
        phaselegend;
  
    label AVAL = "Diastolic Blood Pressure (mmHg)"  
+
      label AVAL = "Diastolic Blood Pressure (mmHg)"  
          timept = 'Visit'  
+
            timept = 'Visit'  
          trtp = 'Treatment'  
+
            trtp = 'Treatment'  
          n = 'n'  
+
            n = 'n'  
          mean = 'Mean'  
+
            mean = 'Mean'  
          std = 'Std'  
+
            std = 'Std'  
          median = 'Median'  
+
            median = 'Median'  
          min = 'Min'  
+
            min = 'Min'  
          max = 'Max'  
+
            max = 'Max'  
          q1 = 'Q1'  
+
            q1 = 'Q1'  
          q3 = 'Q3';
+
            q3 = 'Q3';
  
    format mean 5.1 std 6.2;
+
      format mean 5.1 std 6.2;
  run;
+
    run;
  
*--- CLOSE PDF output destination ---*;
+
  *--- CLOSE PDF output destination ---*;
  ods pdf close;
+
    ods pdf close;

Revision as of 03:20, 15 August 2015

 /*******************************************************************************
 ***  PROGRAM:     WPCT-F.07.01-resolved.sas
 ***
 ***  DESCRIPTION: Example of Figure 7.1 from Measures of Central Tendency white paper
 ***
 ***  INSTRUCTIONS FOR USER:
 ***          If you cannot directly access the github XPORT files (below) from you environment, 
 ***          then store them locally and modify the initial data-access code accordingly
 ***
 ***  INPUT:  Test data (sourced from github)
 ***          https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/adsl.xpt
 ***          https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/advs.xpt
 ***
 ***  OUTPUT:
 ***
 ***  AUTHOR: CSS/PhUSE Standard Scripts Working Group
 ***          http://www.phusewiki.org/wiki/index.php?title=WG5_Project_02
 ***
 *******************************************************************************/
 /* ACCESS CSS/PhUSE test data from github */
   filename source url "https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/adsl.xpt";
     *--- alternative: access local copy of test data ---*;
     * filename source '<YOUR-PATH-AS-NEEDED> adsl.xpt' ;
   libname source xport access=READONLY;
     data css_adsl;
       set source.adsl;
     run;
     filename source clear;
     libname source clear;
   filename source url "https://raw.github.com/phuse-org/phuse-scripts/master/scriptathon2014/data/advs.xpt";
     *--- alternative: access local copy of test data ---*;
     * filename source '<YOUR-PATH-AS-NEEDED> advs.xpt' ;
   libname source xport access=READONLY;
     data css_advs;
       set source.advs;
       where (paramcd in ('DIABP') and atptn in (815));
     run;
     filename source clear;
     libname source clear;
 *--- SHORTEN treatment names for display ---*;
   data adsl_sub (rename=(trt01p_short=trt01p));
     set css_adsl;
     length trt01p_short $6;
     select (trt01p);
       when ('Placebo') trt01p_short = 'P';
       when ('Xanomeline High Dose') trt01p_short = 'X-high';
       when ('Xanomeline Low Dose') trt01p_short = 'X-low';
       otherwise trt01p_short = 'UNEXPECTED';
     end;
     drop trt01p;
   run;
   data advs_sub (rename=(trtp_short=trtp));
     set css_advs;
     length trtp_short $6;
     select (trtp);
       when ('Placebo') trtp_short = 'P';
       when ('Xanomeline High Dose') trtp_short = 'X-high';
       when ('Xanomeline Low Dose') trtp_short = 'X-low';
       otherwise trtp_short = 'UNEXPECTED';
     end;
     drop trtp;
   run;
 *--- Restrict analysis to SAFETY POP and ANALYSIS RECORDS (&a_fl) ---*;
   data css_asldata;
     set adsl_sub (keep=STUDYID USUBJID SAFFL TRT01P TRT01PN);
     where SAFFL = 'Y';
   run;
   data css_anadata;
     set advs_sub (keep=STUDYID USUBJID SAFFL ANL01FL TRTP TRTPN PARAM PARAMCD 
                        AVAL ANRLO ANRHI AVISIT AVISITN ATPT ATPTN);
     where SAFFL = 'Y' and ANL01FL = 'Y';
   run;
 *--- Calculate summary statistics, and merge onto measurement data for use as "block" variables ---*;
   proc sort data=css_anadata;
     by avisitn trtpn;
   run;
   proc summary data=css_anadata noprint;
     by avisitn trtpn;
     var AVAL;
     output out=css_stats (drop=_type_) n=n mean=mean std=std median=median min=min max=max q1=q1 q3=q3;
   run;
   data css_plot (rename=(avisit=_PHASE_));
     merge css_anadata (in=in_paramcd) css_stats (in=in_stats);
     by avisitn trtpn;
     label n = 'n' mean = 'Mean' std = 'Std Dev' min = 'Min' q1 = 'Q1' median = 'Median' q3 = 'Q3' max = 'Max';
   run;
 *--- PROC SHEWHART reads "phases" (visits) from a special _PHASE_ variable ---*;
   data css_plot_tp;
     set css_plot end=NoMore;
     by avisitn trtpn;
     keep AVAL timept trtpn trtp ANRLO ANRHI max q3 median q1 min std mean n _PHASE_;
     retain timept 0;
     if first.avisitn then timept = floor(timept + 1);
     if first.trtpn then timept + (1/(1+3));
     *--- Create BOXPLOT_TIMEPT_RANGES, to limit number of boxes per plot page to &MAX_BOXES_PER_PAGE ---*;
     length boxplot_timept_ranges $200;
     retain max_boxes_per_page 20 boxes_on_page 0 last_timept_end 0 boxplot_timept_ranges ' ';
     if last.avisitn then do;
       boxes_on_page = boxes_on_page + 3;
       if boxes_on_page + 3 > max_boxes_per_page then do;
         *--- Current visit is enough for this plot, no more boxes, next visit will be too much ---*;
         boxplot_timept_ranges = strip(boxplot_timept_ranges) !!strip(compbl( put(last_timept_end,8.-L) !!' <= timept < '!! put(ceil(timept),8.-L) )) !!'|';
         boxes_on_page = 0;
         last_timept_end = timept;
       end;
       else if NoMore then do;
         boxplot_timept_ranges = strip(boxplot_timept_ranges) !!strip(compbl( put(last_timept_end,8.-L) !!' <= timept < ' !!put(timept,8.-L) ))!!'|';
       end;
     end;
     if NoMore then call symput('boxplot_timept_ranges', strip(boxplot_timept_ranges));
   run;
 *--- ANNOTATE normal range outliers on box plot ---*;
   data css_annotate (keep=function hsys text size color x xsys y ysys);
     set css_plot_tp;
     function = 'SYMBOL';
     * On the horizontal axis (typically TimePT), introduce user-selected jitter *;
     x = TIMEPT;
     y = AVAL;
     * jitter size is based on ±8% of spacing used for PROC SHEWHART boxplot., *;
     jitter = 0.08 / (1+3);
     jitter = ranuni(29507) * jitter;
     if ranuni(41385) > 0.5 then jitter = -1 * jitter;
     x = x + jitter;
     * SIZE variable is percentage of graphic space *;
     hsys = '3';
     size = 1;
     * X and Y variables contain data values *;
     xsys = '2';
     ysys = '2';
     * ANNOTATE non-missing obs with valid LOW and HIGH limits *;
     if (n(y, ANRLO) = 2 and y < ANRLO) or (n(ANRHI, y) = 2 and ANRHI < y);
     * Draw a filled DOT with user-selected COLOR *;
     length color $5 text $6;
     color = 'RED';
     text = 'DOT';
     OUTPUT;
   run;
 *--- Graphics Settings - Default HSIZE and VSIZE are suitable for A4 and letter ---*;
   options orientation=landscape;
   goptions reset=all hsize=11.5in vsize=7.5in;
   title justify=left height=1.2     'Box Plot - Diastolic Blood Pressure (mmHg) by Visit, Analysis Timepoint: AFTER LYING DOWN FOR 5 MINUTES';
   footnote1 justify=left height=1.0 'Box plot type=schematic, the box shows median, interquartile range (IQR, edge of the bar), min and max';
   footnote2 justify=left height=1.0 'within 1.5 IQR below 25% and above 75% (ends of the whisker). Values outside the 1.5 IQR below 25% and';
   footnote3 justify=left height=1.0 'above 75% are shown as outliers. Means plotted as different symbols by treatments.';
   axis1 value=none label=none major=none minor=none;
   axis2 order=(40 to 110 by 10);
 *--- OPEN PDF output destination ---*;
   ods pdf file='WPCT-F.07.01_Box_plot_DIABP_by_visit_for_timepoint_815.pdf';
 *--- CREATE PDF box plot ---*;
   proc shewhart data=css_plot_tp (where=( 0 <= timept < 7 ));
     boxchart AVAL * timept (max q3 median q1 min std mean n trtp) = trtp / 
       annotate = css_annotate (where=( 0 <= x < 7 )) 
       boxstyle = schematic 
       notches 
       stddeviations 
       nolegend 
       ltmargin = 5 
       blockpos = 3 
       blocklabelpos = left 
       blocklabtype=scaled 
       blockrep
       haxis=axis1 
       vaxis=axis2 
       vref=60 90 
       cvref=RED 
       idsymbol=square 
       idcolor=black 
       nolimits 
       readphase = all 
       phaseref 
       phaselabtype=scaled 
       phaselegend;
     label AVAL = "Diastolic Blood Pressure (mmHg)" 
           timept = 'Visit' 
           trtp = 'Treatment' 
           n = 'n' 
           mean = 'Mean' 
           std = 'Std' 
           median = 'Median' 
           min = 'Min' 
           max = 'Max' 
           q1 = 'Q1' 
           q3 = 'Q3';
     format mean 5.1 std 6.2;
   run;
 *--- CLOSE PDF output destination ---*;
   ods pdf close;