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?
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;
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;
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;
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:
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;
Looks a little silly as-is, but if you fiddle with the options, I would bet you can get something good.
Is there a reason you're writing separate SERIES statements instead of writing a single SERIES statement with the GROUP= option?
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;
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.
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;
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;
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.
Ready to level-up your skills? Choose your own adventure.