Graphics Programming

Data visualization using SAS programming, including ODS Graphics and SAS/GRAPH. Charts, plots, maps, and more!
BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
BruceBrad
Lapis Lazuli | Level 10

I want to position some curvelabels at the end of my time-series lines. I'm trying different combinations of CURVELABELLOC and CURVELABELPOS.

For some reason, it keeps on putting the label for the top series on top of the figure. The first figure is with

OUTSIDE and MAX (same results with AUTO). The second is with INSIDE and MAX. (If I drop the top series, I some of the labels on the top and rotated!).

How can I force it to put all labels down the RHS of the figure?

 

BruceBrad_0-1740183180037.png

 

BruceBrad_1-1740183449334.png

 

1 ACCEPTED SOLUTION

Accepted Solutions
Ksharp
Super User

You want this ?

 

* Curvelabel;
data dat;
input date var1 var2;
cards;
1 10 20
2 15 25
3 11 .
4 18 .
;
run;
data dat2;
set dat end=last;
retain retainvar1 retainvar2;
if not missing(var1) then retainvar1=var1;
if not missing(var2) then retainvar2=var2;
if last;
keep date retainvar1 retainvar2;
run;
%sganno
data sganno;
 set dat2;
 %SGTEXT(LABEL='var1',WIDTH=80, X1SPACE="GRAPHPERCENT",Y1SPACE="DATAVALUE" ,X1=96,Y1=retainvar1,TEXTCOLOR="blue")
 %SGTEXT(LABEL='var2',WIDTH=80, X1SPACE="GRAPHPERCENT",Y1SPACE="DATAVALUE" ,X1=96,Y1=retainvar2,TEXTCOLOR="red")
run;

proc sgplot data=dat sganno=sganno ;
series x=date y=var1 /markers markerattrs=(symbol=circlefilled color=blue) lineattrs=(color=blue)
 CURVELABELLOC=outside curvelabel curvelabelattrs=(color=white);
series x=date y=var2 /markers markerattrs=(symbol=circlefilled color=red) lineattrs=(color=red) 
 CURVELABELLOC=outside curvelabel curvelabelattrs=(color=white);
run;

Ksharp_0-1740362749709.png

 

View solution in original post

11 REPLIES 11
BruceBrad
Lapis Lazuli | Level 10
One more point: This behaviour is probably driven by the fact that some of the series finish earlier than the others. I've also tried INSIDE and END, which sort of works for positioning, but the labels then overprint other series datapoints.
Ksharp
Super User
Post your data and code to replicate your problem.
So could help to address where is problem.
BruceBrad
Lapis Lazuli | Level 10

My code has lots of lines, but this simple code shows the problem. My preference would be to have all the labels on the outside at right.

data dat;
input date var1 var2;
cards;
1 10 20
2 10 20
3 10 .
4 10 .
;
run;
Title "Outside puts one series at top (I would prefer all the labels at right)";
proc sgplot;
series x=date y=var1 /curvelabel curvelabelloc=outside;
series x=date y=var2 /curvelabel curvelabelloc=outside;
run;
title "Inside, in this case, works OK";
title2 "Though the top label would be neater if it were to the right";
proc sgplot;
series x=date y=var1 /curvelabel curvelabelloc=inside curvelabelpos=max;
series x=date y=var2 /curvelabel curvelabelloc=inside curvelabelpos=max;
run;
title "However, if the lines are close together, one label gets put at the bottom";
proc sgplot;
series x=date y=var1 /curvelabel curvelabelloc=inside curvelabelpos=max;
series x=date y=var2 /curvelabel curvelabelloc=inside curvelabelpos=max;
yaxis values=(1 to 100);
run;
BruceBrad
Lapis Lazuli | Level 10

Here is a work-around. I create two dummy cases at the end. But this wouldn't work if I had data point markers.

* Curvelabel;
data dat;
input date var1 var2;
cards;
1 10 20
2 15 25
3 11 .
4 18 .
;
run;
data dat2;
retain retainvar1 retainvar2;
drop retainvar1 retainvar2;
set dat end=lastobs;
if not(missing(var1)) then retainvar1 = var1;
if not(missing(var2)) then retainvar2 = var2;
output;
nextdate = lag(date)+1;
nextdate2 = lag(date)+2;
drop nextdate nextdate2;
if lastobs then do;
  date = nextdate;
  var1 = .;
  var2 = .;
  output;
  date = nextdate2;
  var1 = retainvar1;
  var2 = retainvar2;
  output;
end;
run;

Title "Adding two dummy observations at end works if I don't have data point markers";
proc sgplot data=dat2;
series x=date y=var1 /curvelabel curvelabelloc=outside break;
series x=date y=var2 /curvelabel curvelabelloc=outside break;
run;
title;
proc sgplot data=dat2;
series x=date y=var1 /curvelabel curvelabelloc=inside curvelabelpos=max break;
series x=date y=var2 /curvelabel curvelabelloc=inside curvelabelpos=max break;
run;

proc sgplot data=dat2;
series x=date y=var1 /curvelabel curvelabelloc=inside curvelabelpos=max break;
series x=date y=var2 /curvelabel curvelabelloc=inside curvelabelpos=max break;
yaxis values=(1 to 100);
run;
run;
quickbluefish
Barite | Level 11

Agree this is odd - especially the ones that show up on the bottom when the lines are close together.  At least for the ones that place the label on top, I think the algorithm is just trying to put it in the least ambiguous position possible, even if it doesn't look great.  It's more obvious when you have a lot of lines, some of which (like in your plot) only extend part way down the x-axis:

quickbluefish_1-1740359584938.png

You could play around with something like this instead -- labeling with the TEXT statement instead of the CURVELABEL options:

data test;
do grp=1 to 10;
	ymean=rand('erlang',2)*5;
	nwks=25;
	if ranuni(0)<0.4 then nwks=rand('integer',5,25);
	x=.; y=.;
	do wk=1 to nwks;
		yval=ymean+rand('normal')*10;
		if wk=nwks then do;
			put 'hello?';
			x=wk;
			y=yval;
		end;
		output;
	end;
end;
run;

proc sgplot data=test noautolegend;
series x=wk y=yval / group=grp;
text x=x y=y text=grp / group=grp textattrs=(size=12pt) position=right backfill backlight;
scatter x=x y=y / group=grp markerattrs=(size=12pt);
run;

quickbluefish_2-1740360460753.png

Looks a little silly as-is, but if you fiddle with the options, I would bet you can get something good.

quickbluefish
Barite | Level 11

Is there a reason you're writing separate SERIES statements instead of writing a single SERIES statement with the GROUP= option?  

BruceBrad
Lapis Lazuli | Level 10
Mainly just habit. This allows me to customise the appearance of each line manually. Would reshaping the data and using groups force a line-up of the labels?
Ksharp
Super User

You want this ?

 

* Curvelabel;
data dat;
input date var1 var2;
cards;
1 10 20
2 15 25
3 11 .
4 18 .
;
run;
data dat2;
set dat end=last;
retain retainvar1 retainvar2;
if not missing(var1) then retainvar1=var1;
if not missing(var2) then retainvar2=var2;
if last;
keep date retainvar1 retainvar2;
run;
%sganno
data sganno;
 set dat2;
 %SGTEXT(LABEL='var1',WIDTH=80, X1SPACE="GRAPHPERCENT",Y1SPACE="DATAVALUE" ,X1=96,Y1=retainvar1,TEXTCOLOR="blue")
 %SGTEXT(LABEL='var2',WIDTH=80, X1SPACE="GRAPHPERCENT",Y1SPACE="DATAVALUE" ,X1=96,Y1=retainvar2,TEXTCOLOR="red")
run;

proc sgplot data=dat sganno=sganno ;
series x=date y=var1 /markers markerattrs=(symbol=circlefilled color=blue) lineattrs=(color=blue)
 CURVELABELLOC=outside curvelabel curvelabelattrs=(color=white);
series x=date y=var2 /markers markerattrs=(symbol=circlefilled color=red) lineattrs=(color=red) 
 CURVELABELLOC=outside curvelabel curvelabelattrs=(color=white);
run;

Ksharp_0-1740362749709.png

 

BruceBrad
Lapis Lazuli | Level 10

Thanks everyone for all the suggestions. I'll have to learn how to do annotations. SAS is obviously looking at which axis is closest to the max point for each line and putting the label on that axis. Unfortunately this is often pretty ugly.

One simple enhancement for SAS would be to add an OUTSIDERIGHT choice to the CURVELABELLOC option to allow users to force output to the right hand side.

A very simple work-around (which is actually workable for my graph today) is to change the y axis to have more white space at top and bottom - then the right axis will always be closer.

ballardw
Super User

I maybe END instead of max

 

data dat;
input date var1 var2;
cards;
1 10 20
2 15 25
3 11 .
4 18 .
;
run;
data dat2;
retain retainvar1 retainvar2;
drop retainvar1 retainvar2;
set dat end=lastobs;
if not(missing(var1)) then retainvar1 = var1;
if not(missing(var2)) then retainvar2 = var2;
output;
nextdate = lag(date)+1;
nextdate2 = lag(date)+2;
drop nextdate nextdate2;
if lastobs then do;
  date = nextdate;
  var1 = .;
  var2 = .;
  output;
  date = nextdate2;
  var1 = retainvar1;
  var2 = retainvar2;
  output;
end;
run;

title "Curvelabelpos=END with outside";
proc sgplot data=dat2;
series x=date y=var1 /curvelabel curvelabelloc=outside curvelabelpos=end break;
series x=date y=var2 /curvelabel curvelabelloc=outside curvelabelpos=end break;
run;

title "Curvelabelpos=END with inside";
proc sgplot data=dat2;
series x=date y=var1 /curvelabel curvelabelloc=inside curvelabelpos=end break;
series x=date y=var2 /curvelabel curvelabelloc=inside curvelabelpos=end break;
run;
title;
Tom
Super User Tom
Super User

That looks promising.  Can you make the dots disappear?

It might be easier to make them disappear if they were generated by separate statements.  That will also make the data manipulation easier.

Like this:

data dat3;
retain retainvar1 retainvar2;
set dat end=lastobs;
if not(missing(var1)) then retainvar1 = var1;
if not(missing(var2)) then retainvar2 = var2;
output;
if lastobs then do;
  var1 = .;
  var2 = .;
  var1e = retainvar1;
  var2e = retainvar2;
  output;
end;
drop retainvar1 retainvar2;
run;

title "Curvelabelpos=END with outside";
proc sgplot data=dat3;
series x=date y=var1 / ;
series x=date y=var2 / ;
series x=date y=var1e /curvelabel curvelabelloc=outside curvelabelpos=end ;
series x=date y=var2e /curvelabel curvelabelloc=outside curvelabelpos=end ;
run;

sas-innovate-white.png

Our biggest data and AI event of the year.

Don’t miss the livestream kicking off May 7. It’s free. It’s easy. And it’s the best seat in the house.

Join us virtually with our complimentary SAS Innovate Digital Pass. Watch live or on-demand in multiple languages, with translations available to help you get the most out of every session.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 11 replies
  • 1199 views
  • 5 likes
  • 5 in conversation