BookmarkSubscribeRSS Feed
0 Likes

Problem

My team has run into the following problem:

When using the HIGHLOW statement in SGPLOT along with the HIGHCAP = FILLEDARROW option to create arrows at the ends of bars, the following problem occurs: if a bar is shorter than the arrow cap is wide, then the arrow cap disappears. In the plots we create, we often would like to use these arrow caps conditionally to display relevant information, so to have these arrow caps disappear sometimes without any indication such as a warning or error in the log makes this feature unreliable, and we now avoid the use of HIGHCAP altogether.

 

The behavior I described is also described in the SAS documentation for HIGHCAP, "If the length of the high-low element is smaller than the cap, then the cap is not drawn.", so it appears that SAS is aware of the problem. However, as described, this behavior makes the feature completely unusable for us.

Note that in the following SAS blog, these arrows are used to conditionally represent relevant information, producing unreliable output: https://blogs.sas.com/content/graphicallyspeaking/2014/06/22/swimmer-plot/#prettyPhoto

 

Suggestions:

1. Add a warning or error that is printed to the log when a bar cap fails to print.

2. Add an option that will allow the bar cap to be printed in place of the bar when the bar is too small to accommodate the cap.

 

Example

The following sample code reproduces this problem. When I run this on Base SAS, 9.4 TS1M4, the cap appears for the bars in the range 0.4 - 9, but not for the 0.1, 0.2, and 0.3 bars. A screenshot is included at the end.

 

ods _all_ close;
ods html;
 %macro set_vars;
		subjectid = strip(put(index, best5.));
		high = index;
		low = 0;
		output;
%mend set_vars;
data ds;
	do i = 1 to 9;
		index = i;
		%set_vars;
		index = i / 10;
		%set_vars;
	end;
run;

proc sort data = ds;
	by high;
run;

proc sgplot data = ds;
	highlow y = subjectid  low = low high = high / type=bar highcap = filledarrow barwidth = 1;
run;

MissingBarCaps.PNG

2 Comments
ballardw
Super User

If the arrowhead is the critical part of the plot you might investigate VECTOR plot.

Since a vector is expected to be numeric the y values need to be numeric and to duplicate your spacing of the high-low would require playing around with values and maybe a display format But the arrowheads do appear, at least on my system, with your values:

 

proc sgplot data = ds;
	vector x=high y = index   /  
         xorigin=0 yorigin=index
         arrowdirection=out arrowheadshape=closed

  ;
run;

You can use lineattributes to get a thicker line.

I suspect if you

kmann1
Fluorite | Level 6

Hello @ballardw,

 

I experimented with VECTOR a bit, and it's certainly an improvement over HIGHLOW in reliability. I appreciate the suggestion.

One further challenge beyond the mentioned numeric y-axis: In this use case, only a subset of the bars are assigned arrows. A VECTOR statement allows for arrows on either all vectors or no vectors (whereas in HIGHLOW, a character variable can indicate which bars should have caps). We found that using 2 pairs of endpoint variables and 2 VECTOR statements, one with arrows, one without, could produce the desired result. Code is as follows.

 

ods _all_ close;
ods html;

%macro set_vars;
		subjectid = strip(put(index, best5.));
		high = index;
		low = 0;
		if rand('uniform') < 0.6 then arrow = 'Y';
			else arrow = 'N';
		output;
%mend set_vars;
data ds1;
call streaminit(1); do i = 1 to 9; index = i; %set_vars; index = i / 10; %set_vars; end; run; proc sort data = ds1; by high; run; data ds2; set ds1; y_num = _n_; select(arrow); when('Y') do; high_arrow = high; low_arrow = low; end; when('N') do; high_noarrow = high; low_noarrow = low; end; end; run; proc sgplot data = ds2; vector x=high_arrow y=y_num / xorigin=low_arrow yorigin=y_num noarrowheads lineattrs = (thickness = 6px color = cx5f8a87); vector x=high_noarrow y=y_num / xorigin=low_noarrow yorigin=y_num arrowdirection=out arrowheadshape=filled lineattrs = (thickness = 6px color = cx5f8a87); run;