2 broken axis in a single graph - that's crazy talk - how ... · the ranges option in...

12
1 2 Broken Axis in a Single Graph - That's Crazy Talk - How To Use PROC TEMPLATE, SGRENDER, and SGANNO to Make Your Graphs Look Awesome Steve Black, Agility Clinical, Inc., Carlsbad, CA ABSTRACT Sometimes SAS® has limitations that need to be worked around. These ‘rare’ circumstances also present golden opportunities to ask questions, learn new things, and experiment with options that you may have not worked with before. Such was the case with trying to create 2 broken axis in one graph. In this paper I illustrate how to utilize the Graph Template Language (GTL) and SGRENDER procedure to overcome some SAS graphing limitations. I also use annotations to help solve some labeling issues. I will go through each of the procedures used in a step by step manner to explain the options I utilized and the reasons behind them. The purpose of this paper is to explain how to create the figure and understand the syntax, but also to allow others to take these concepts and improve/build upon them. INTRODUCTION Recently while working with a pharmacokinetic (PK) analysis specialist, he requested that I create a figure similar to a mock that he had created using specialized figure software. Figure 1. Mock figure that I needed to re-create using SAS. Note the broken axis on each axis. When looking at the mock I saw lots of neat things but did not know how to best achieve this using SAS. So with a bit of playing around I came up with a few solutions but none seemed to really accomplish what I was looking for. I then did what all good programmers do when in a pinch, I emailed SAS technical support explaining what I wanted and see how they suggested I proceed. They happily sent back some code which was very helpful and got me much closer to what I needed than I had accomplished on my own. In researching the code they sent and looking into options for the procedures used I was able to create the figure just as it was suggested in the mock. In this paper I will provide several examples from simple to complex on how I accomplished this task.

Upload: others

Post on 27-Jun-2020

5 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

1

2 Broken Axis in a Single Graph - That's Crazy Talk - How To Use PROC TEMPLATE, SGRENDER, and SGANNO to Make Your Graphs Look

Awesome

Steve Black, Agility Clinical, Inc., Carlsbad, CA

ABSTRACT

Sometimes SAS® has limitations that need to be worked around. These ‘rare’ circumstances also present golden opportunities to ask questions, learn new things, and experiment with options that you may have not worked with before. Such was the case with trying to create 2 broken axis in one graph. In this paper I illustrate how to utilize the Graph Template Language (GTL) and SGRENDER procedure to overcome some SAS graphing limitations. I also use annotations to help solve some labeling issues. I will go through each of the procedures used in a step by step manner to explain the options I utilized and the reasons behind them. The purpose of this paper is to explain how to create the figure and understand the syntax, but also to allow others to take these concepts and improve/build upon them.

INTRODUCTION

Recently while working with a pharmacokinetic (PK) analysis specialist, he requested that I create a figure similar to a mock that he had created using specialized figure software.

Figure 1. Mock figure that I needed to re-create using SAS. Note the broken axis on each axis.

When looking at the mock I saw lots of neat things but did not know how to best achieve this using SAS. So with a bit of playing around I came up with a few solutions but none seemed to really accomplish what I was looking for. I then did what all good programmers do when in a pinch, I emailed SAS technical support explaining what I wanted and see how they suggested I proceed. They happily sent back some code which was very helpful and got me much closer to what I needed than I had accomplished on my own. In researching the code they sent and looking into options for the procedures used I was able to create the figure just as it was suggested in the mock. In this paper I will provide several examples from simple to complex on how I accomplished this task.

Page 2: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

2 Broken Axis in a Single Graph, continued

2

A BROKEN AXIS – A QUICK EXAMPLE

Let’s start off with a basic figure using some simple example data.

Display 1. Sample Data Used.

Using PROC SPLOT we can create a basic scatter plot and include a broken axis in the X axis. This is done by using the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger than the values specified as to allow for some spacing for data points close to the break. SAS limits the figure to only one broken axis per figure. I also change the broken axis style using the STYLEATTRS statement and specify the AXISBREAK as BRACKET. For the AXISBREAK, There are several styles to choose from, these are listed below:

Display 2. Available AXISBREAK styles.

I also set the AXISEXTENT option to DATA which turns off the wall border so that the axis lines can be clearly seen and ensures that axis lines only extend through the data range from the minimum offset to the maximum offset. The Default is FULL.

Page 3: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

2 Broken Axis in a Single Graph, continued

3

Display 3. Available AXISEXTENT options.

Here is the full code used.

proc sgplot data=sample;

styleattrs AXISBREAK = BRACKET AXISEXTENT=DATA;

scatter x=ATPTN y=aval / markerattrs=(color=blue);

yaxis label='Total' values=(0 to 3 by .25) minor;

xaxis label='Time (h) since dose' ranges=(1-5 166 - 180) values=(1 2 3 4 168 172

176 180) minor;

run;

Figure 2. Figure output using the above code.

The resulting figure is close to my mock and looks pretty good. However, when my sample data begins to have a larger difference between the max values and minimum values I begin to lose some of the visual acuity that I need for this figure. The sample data below uses the same SGPLOT code noted above to create the figure.

Page 4: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

2 Broken Axis in a Single Graph, continued

4

Figure 4. Sample code and figure with increased difference between the Y axis values.

If I switch the axis break to the Y axis and modify the RANGES and VALUES statements to: ranges=(0 - .07 1

- 3) values=(.01 .02 .03 .04 .05 1 2 3) then I get the output below. I played around with the axis

statements and it’s a real challenge to get things to look like I was hoping. I could illustrate a lot of my attempts but it safe to say a better solution is needed. SAS technical support to the rescue.

Figure 5. A not so pretty figure with very strange axis values.

Page 5: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

2 Broken Axis in a Single Graph, continued

5

USING PROC TEMPLATE TO SOLVE IT

SAS technical support provided a quick example using PROC TEMPLATE which I was able to modify to accomplish the figure requested. Using the same sample data I’ll demonstrate how to best accomplish this figure with two broken axis. It is really just creating four graphs and making it look like 1 graph.

STEP 1. ALLOW SAS TO CREATE A WORKING TEMPLATE

The first step takes the ods path statement(s) currently in use to be used and then allows SAS to create a temporary template in the work folder for the duration of the SAS session. Using this code:

ods path(prepend) work.template(update);

STEP 2. CREATE A NEW TEMPLATE USING THE GRAPHING TEMPLATE LANGUAGE (GTL)

Begin with the PROC TEMPLATE statement and then define a new STATGRAPH name, I called mine test. Then I begin the graphing section adding in the familiar AXISLINEEXTENT = DATA statement, SAS likes to change the names just slightly in GTL to make things interesting.

proc template;

define statgraph test;

begingraph / AXISLINEEXTENT=DATA;

At the end of the procedure I’ll use these closing statements, and include the other parts of code within:

endgraph;

end;

run;

The next step involves setting up the layout of the graphs. Since we are setting this figure up to be four figures we specify this in the first layout statement using the LATTICE option. I use the row/column weight option to tell SAS how much of the graph space I want each figure to use, I also provide row/column gutter statements which determine the space between the graphs.

layout lattice / rows=2 rowweights=(.5 .5) rowgutter=10

columns=2 columnweights=(.3 .7) columngutter=10;

Display 3. Example lattice layout for each of the graphs.

Page 6: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

2 Broken Axis in a Single Graph, continued

6

Now for each graph I’ll create a LAYOUT statement using the OVERLAY option, I also do not want to show the graph walls so I set the WALLDISPLAY to none. With each of these layout statements I’ll need to close them with an ENDLAYOUT statement. Within this layout statement I insert the SCATTERPLOT statement which is remarkably similar to the scatter statement in our SGPLOT code.

layout overlay / walldisplay=none

scatterplot x=ATPTN y=aval / markerattrs=(color=blue);

… X and Y AXIS CODE HERE …

endlayout;

The most complicated part of the PROC TEMPLATE style is getting the X and Y axis just as you need them. We will discuss syntax first then I’ll illustrate how I set it up. The syntax for creating the X and Y axis are XAXISOPTS and YAXISOPTS =, the options are stated within parenthesis. Some options include: DISPLAY, LINEAROPTS, and LABEL. Per each graph some X and Y axis are displayed and others are not. We control this by using the DISPLAY=NONE option for those that do not need an axis displayed. Due to multiple graphs disguised as one, I set the label statement to missing when DISPLAY does not equal ‘NONE’. I will use annotation facility to accomplish the labeling task in a subsequent step. The LINEAROPTS also contains several sub-options. These include VIEWMIN, VIEWMAX, and TICKVALUESEQUENCE. The view min and max allows me to specify which values I want displayed in each graph. This further helps in setting the figure up into 4 quadrants, this view min and max statements are similar to the values statement in SGPLOT. Tick value sequence is similar to the SGPLOT range option which allows me to specify how often I see the tick marks I use the START and END options along with the INCREMENT value to specify my tick breaks. In this next section I illustrate each graph and then show the X and Y axis codes are used.

Display 4. X and Y options for the first graph.

xaxisopts=(display=none linearopts=(viewmin=0 viewmax=4 tickvaluesequence=(start=0

end=5 increment=1)))

yaxisopts=(linearopts=(viewmin=1 viewmax=4 tickvaluesequence=(start=1 end=4

increment=1)) label=" ");

Display 5. X and Y options for the second graph.

xaxisopts=(display=none linearopts=(viewmin=168 viewmax=1344

tickvaluesequence=(start=168 end=1344 increment=168)))

Page 7: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

2 Broken Axis in a Single Graph, continued

7

yaxisopts=(display=none linearopts=(viewmin=1 viewmax=4 tickvaluesequence=(start=1

end=4 increment=1)));

Display 6. X and Y options for the third graph.

xaxisopts=(linearopts=(viewmin=0 viewmax=4 minorticks=true

tickvaluesequence=(start=0 end=5 increment=1)) label=" ")

yaxisopts=(linearopts=(viewmin=0 viewmax=0.08 tickvaluesequence=(start=0 end=.1

increment=.01)) label=" ");

Display 7. X and Y options for the fourth graph.

xaxisopts=(linearopts=(viewmin=168 viewmax=1344 minorticks=true

tickvaluesequence=(start=168 end=1344 increment=168)) label=" ")

yaxisopts=(display=none linearopts=(viewmin=0 viewmax=0.08

tickvaluesequence=(start=0 end=.1 increment=.01)));

Once completed the figure output will be nearly perfect. Due to the semi complicatedness of the X and Y options which break up the program into chucks, I’ve included the full code in the appendix for easier reference. In the next step we will create the labels by using the annotation facility.

Page 8: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

2 Broken Axis in a Single Graph, continued

8

Figure 5. Resulting figure using PROC TEMPLATE, no labels displayed yet.

STEP 3. ANNOTATE LABELS

Using some of the more recent SAS 9.4 features annotation has become much easier to accomplish. I use the SAS stored %SGANNO macro call and then within a data step I create a new dataset called _anno to hold the annotations needed. I further use the %SGTEXT macro call with the label, textsize, width, and x1/,y1 parameters specified. There are more parameters available which can be found here: http://documentation.sas.com/?cdcId=vdmmlcdc&cdcVersion=8.1&docsetId=grstatproc&docsetTarget=n1pzv4xoyp6b2kn14dvb8og0y59k.htm&locale=en

%sganno;

data _anno;

%sgtext(label='Time (h) since dose', textsize=11, width=30, x1=50,y1=2);

%sgtext(label='PK Concentration Mass (unit)', textsize=11, width=50,

x1=2,y1=50,rotate=90);

run;

In my annotations I create two text labels one for the Y axis and another for X axis. For both labels I placed them in the center of the respective axis (x1 / y1 = 50), and then use the opposite coordinates to be just inside the frame (y1 / x1 = 2). I modified the width to be just larger than the text size, otherwise the label will wrap. For both labels I set the text size to 11. Once created I will also need to place the text ‘annotate’ in the PROC TEMPLATE. I found that placing this right after the BEGINGRAPH line was acceptable.

begingraph / AXISLINEEXTENT=DATA; annotate;

Page 9: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

2 Broken Axis in a Single Graph, continued

9

STEP 4. CREATING THE FINAL OUTPUT

Acknowledging that figures can be presented in a number of different formats I’ll be demonstrating the style my current company uses. We place the figure image within an RTF document. This can be done using the following code:

ods listing close;

ods rtf file = "C:\SGPLOT Examples\f-broken.rtf" style=statistical

nobodytitle nogtitle nogfootnote;

ods graphics / noborder height=6in width=7.0in outputfmt=png;

In the code above I close the ods listing facility, and create the rtf document. I specify a style which in this case I use statistical. I also specify that I do not want to see a body title, graph title or graph footnote. I also do not want the border around the graph and I specify the height and width of my graph which is slightly larger than the default setting. I also set the output format of the figure as a png. This is needed as sometimes certain lines have a hard time being displayed with the proper widths or super scripts and other Unicode characters do not render correctly without it.

title1 j=l "Agility Clinical Inc." j=r "Database Date/Analysis:

18DEC2017";

title2 j=l "Protocol AC01-01" j=r "Page ~{thispage} of ~{lastpage}";

title3 "Figure 1";

title4 "Example of Broken Y and X axis plots";

title5 "Enrolled Subjects";

footnote1 j=l "C:\SGPLOT Examples\f-broken.sas";

In the above code I create the needed titles and footnotes using the ‘~’ as my ods escape character.

proc sgrender data=sample sganno=_anno template=test;

run;

My favorite step is the super simple PROC SGRENDER, where I specify the dataset that I want to pull in, my SGANNO dataset name, and my TEMPLATE name. Done.

ods graphics off;

ods rtf close;

ods listing;

Lastly I close up shop by turning the graphics off and closing the rtf, then opening back up the ods listing. Once completed and run the following figure will be created. There are a few limitations to using this programming style to create figures, any series/line plots will not connect the dots between images as these really are separate graphs, using the log base 10 in the Y axis is a nice way to overcome the need for the broken axis.

Page 10: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

2 Broken Axis in a Single Graph, continued

10

CONCLUSION

In this paper I demonstrated how to create a broken axis and also demonstrated how to overcome some of the limitations of the broken access statement by utilizing PROC TEMPLATE, annotation and PROC SGRENDER. The intent of this paper is to provide an example and understanding to help who face this and similar graphing challenges.

REFERENCES

SAS® Visual Data Mining and Machine Learning 8.1, ODS Graphics Procedures, %SGTEXT Macro

http://documentation.sas.com/?cdcId=vdmmlcdc&cdcVersion=8.1&docsetId=grstatproc&docsetTarget=n1pzv4xoyp6b2kn14dvb8og0y59k.htm&locale=en

SAS® 9.4 ODS Graphics: Procedures Guide, Sixth Edition

https://support.sas.com/documentation/cdl/en/grstatproc/69716/HTML/default/viewer.htm#p07m2vpyq75fgan14m6g5pphnwlr.htm

SAS® 9.4 Graph Template Language: Reference, Fifth Edition,

http://support.sas.com/documentation/cdl/en/grstatgraph/69718/HTML/default/viewer.htm#n0j696v6yqkb79n12zed3am3omcx.htm#n0147a6gcqla56n190ude5w2fgrf

CONTACT INFORMATION

Your comments and questions are valued and encouraged. Contact the author at:

Name: Steve Black Enterprise: Agility Clinical, Inc.

Page 11: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

2 Broken Axis in a Single Graph, continued

11

Work Phone: 760-258-5919 E-mail: [email protected] Web: http://www.agility-clinical.com

SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. ® indicates USA registration.

Other brand and product names are trademarks of their respective companies.

APPENDIX

ods escapechar = '~';

options nonumber nodate orientation=landscape;

data sample;

input atptn aval;

datalines;

1 .02

1 .05

2 .03

2 .01

3 .04

3 .015

4 1

4 3

4 2

168 .05

336 .02

504 .03

1344 .04

;

run;

ods path(prepend) work.template(update);

proc template;

define statgraph test;

begingraph / AXISLINEEXTENT=DATA;annotate;

layout lattice / rows=2 rowweights=(.5 .5) rowgutter=10

columns=2 columnweights=(.3 .7) columngutter=10;

layout overlay / walldisplay=none

xaxisopts=(display=none linearopts=(viewmin=0 viewmax=4

tickvaluesequence=(start=0 end=5 increment=1)))

yaxisopts=(linearopts=(viewmin=1 viewmax=4

tickvaluesequence=(start=1 end=4 increment=1)) label=" ");

scatterplot x=ATPTN y=aval / markerattrs=(color=blue);

endlayout;

layout overlay / walldisplay=none

xaxisopts=(display=none linearopts=(viewmin=168 viewmax=1344

tickvaluesequence=(start=168 end=1344 increment=168)))

yaxisopts=(display=none linearopts=(viewmin=1 viewmax=4

tickvaluesequence=(start=1 end=4 increment=1)));

scatterplot x=ATPTN y=aval / markerattrs=(color=blue);

endlayout;

layout overlay / walldisplay=none

Page 12: 2 Broken Axis In A Single Graph - That's Crazy Talk - How ... · the RANGES option in combination with the VALUES statement, note that the ranges specified need to be slightly larger

2 Broken Axis in a Single Graph, continued

12

xaxisopts=(linearopts=(viewmin=0 viewmax=4 minorticks=true

tickvaluesequence=(start=0 end=5 increment=1)) label=" ")

yaxisopts=(linearopts=(viewmin=0 viewmax=0.08

tickvaluesequence=(start=0 end=.1 increment=.01)) label=" ");

scatterplot x=ATPTN y=aval / markerattrs=(color=blue);

endlayout;

layout overlay / walldisplay=none

xaxisopts=(linearopts=(viewmin=168 viewmax=1344 minorticks=true

tickvaluesequence=(start=168 end=1344 increment=168)) label=" ")

yaxisopts=(display=none linearopts=(viewmin=0 viewmax=0.08

tickvaluesequence=(start=0 end=.1 increment=.01)));

scatterplot x=ATPTN y=aval / markerattrs=(color=blue);

endlayout;

endlayout;

endgraph;

end;

run;

%sganno;

data _anno;

%sgtext(label='Time (h) since dose', textsize=11, width=30, x1=50,y1=2);

%sgtext(label='PK Concentration Mass (unit)', textsize=11, width=50,

x1=2,y1=50,rotate=90);

run;

ods listing close;

ods rtf file = "C:\SGPLOT Examples\f-broken.rtf" style=statistical

nobodytitle nogtitle nogfootnote;

ods graphics / noborder height=6in width=7.0in outputfmt=png;

title1 j=l "Agility Clinical Inc." j=r "Database Date/Analysis: 18DEC2017";

title2 j=l "Protocol AC01-01" j=r "Page ~{thispage} of ~{lastpage}";

title3 "Figure 1";

title4 "Example of Broken Y and X axis plots";

title5 "Enrolled Subjects";

footnote1 j=l "C:\SGPLOT Examples\f-broken.sas";

proc sgrender data=sample sganno=_anno template=test;

run;

ods graphics off;

ods rtf close;

ods listing;