I am looking to generate a bargraph and line graphs with two axis using proc template. However I am having difficulty to get further. I really appreciate your help.
1. my barchart should starts from the x-axis, instead of line at Y=0
2. want my scatter and Series plot should start from (0,0). I tried to control but not successful
3. I want to duplicate the Score at each Month (Start from Month 1 to Month 12). and generate the same graph where we have two bars at each months. Example, at Month 1 I have score 3, I need score 3 and score 6 bars..
Capture.PNG
/* Create the monthly_score dataset */
data monthly_score;
length trtpn 8 visit $10 score 8 avg 8;
call streaminit(123);
do trtpn = 1 to 2;
do month = 1 to 12;
/* Create visit name */
visit = cats('Month', month);
score = floor(rand('uniform') * 6);
avg = .;
if month = 6 then do;
if trtpn = 1 then avg = 3.5;
else avg = 3.5 + (rand('uniform') - 0.5) * 0.4;
end;
else if month = 12 then do;
if trtpn = 1 then avg = 6.8;
else avg = 6.8 + (rand('uniform') - 0.5) * 0.4;
end;
if avg ne . then avg = round(avg, 0.1);
output;
end;
end;
drop month;
run;
/* Create dummy record for BL with avg=0 but score missing */
data dummy;
visit = "BL";
avg = 0;
trtpn = 1;
score = .;
run;
/* Create dataset with counts for trtpn=1 */
data monthly_score_trt1;
set monthly_score dummy;
where trtpn = 1;
run;
proc sort data = monthly_score_trt1;
by trtpn visit;
run;
/* Create a format to display month names */
proc format;
value visitfmt
0 = " "
1 = "Month1"
2 = "Month2"
3 = "Month3"
4 = "Month4"
5 = "Month5"
6 = "Month6"
7 = "Month7"
8 = "Month8"
9 = "Month9"
10 = "Month10"
11 = "Month11"
12 = "Month12";
run;
/* Create dataset with numeric visit variable */
data bar_data;
set monthly_score_trt1;
by trtpn visit;
if visit = "BL" then visit_num = 0;
else visit_num = input(scan(visit, -1, 'Month'), 8.);
bar_count = score;
if visit = "BL" then bar_count = .;
keep trtpn visit visit_num score avg bar_count;
run;
proc sort data=bar_data;
by visit_num;
run;
ods path(prepend) work.templat(update);
/* Create template with dual y-axes */
proc template;
define statgraph bargraph_trt1_dual;
begingraph;
entrytitle "Bar Graph by Visit for School Group 1";
entrytitle "with Counts Displayed on Top";
layout overlay /
xaxisopts=(
label="Visit"
type=linear
linearopts=(
viewmin=1 viewmax=12
tickvaluelist=(1 2 3 4 5 6 7 8 9 10 11 12)
tickvalueformat=visitfmt.
TICKVALUEFITPOLICY=rotate
TICKVALUEROTATION=DIAGONAL
TICKVALUEPRIORITY=true)
)
yaxisopts=(
label="Score Values"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=1)
)
)
y2axisopts=(
label="Average Score"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=.5)
)
);
barchart x=visit_num y=score /
fillattrs=(color=CX4A7DB4 transparency=0.2)
outlineattrs=(color=CX4A7DB4 thickness=1)
barwidth=0.6
name="bars"
legendlabel="Score Values"
includemissinggroup=false
yaxis=y
;
scatterplot x=visit_num y=score /
markerattrs=(symbol=square size=0)
datalabel=bar_count
datalabelposition=top
datalabelattrs=(
size=10pt
weight=bold
color=black
)
yaxis=y;
scatterplot x=visit_num y=avg /
markerattrs=(
symbol=circlefilled
size=8
color=CXFF7F0E
)
name="avg"
legendlabel="Average Score (Month 6 & 12)"
yaxis=y2;
seriesplot x=visit_num y=avg /
lineattrs=(
pattern=shortdash
color=CXFF7F0E
thickness=2
)
name="series"
legendlabel="Average Trend"
yaxis=y2;
/* Reference line to mark start of months */
*referenceline x=0.5 /
lineattrs=(pattern=solid color=lightgray thickness=1)
curvelabel="Baseline"
curvelabelposition=min;
/* Legend */
discretelegend "bars" "avg" "series" /
location=outside
halign=center
border=true
autoalign=(bottom);
endlayout;
endgraph;
end;
run;
/* Render the graph with dual axes */
proc sgrender data=bar_data template=bargraph_trt1_dual;
format score 4.1 avg 4.1 visit_num visitfmt.;
label score="Score Value"
avg="Average Score"
visit="Visit"
bar_count="Count";
run;
If you need to display these two bar with two different color:
/* Create the monthly_score dataset */
data monthly_score;
length trtpn 8 visit $10 score 8 avg 8;
call streaminit(123);
do trtpn = 1 to 2;
do month = 1 to 12;
/* Create visit name */
visit = cats('Month', month);
score = floor(rand('uniform') * 6);
avg = .;
if month = 6 then do;
if trtpn = 1 then avg = 3.5;
else avg = 3.5 + (rand('uniform') - 0.5) * 0.4;
end;
else if month = 12 then do;
if trtpn = 1 then avg = 6.8;
else avg = 6.8 + (rand('uniform') - 0.5) * 0.4;
end;
if avg ne . then avg = round(avg, 0.1);
output;
end;
end;
drop month;
run;
/* Create dummy record for BL with avg=0 but score missing */
data dummy;
visit = "BL";
avg = 0;
trtpn = 1;
score = .;
run;
/* Create dataset with counts for trtpn=1 */
data monthly_score_trt1;
set monthly_score dummy;
if trtpn=2 then call missing(avg);
run;
proc sort data = monthly_score_trt1;
by trtpn visit;
run;
/* Create a format to display month names */
proc format;
value visitfmt
0 = " "
1 = "Month1"
2 = "Month2"
3 = "Month3"
4 = "Month4"
5 = "Month5"
6 = "Month6"
7 = "Month7"
8 = "Month8"
9 = "Month9"
10 = "Month10"
11 = "Month11"
12 = "Month12";
run;
/* Create dataset with numeric visit variable */
data bar_data;
set monthly_score_trt1;
by trtpn visit;
if visit = "BL" then visit_num = 0;
else visit_num = input(scan(visit, -1, 'Month'), 8.);
bar_count = score;
if visit = "BL" then bar_count = .;
keep trtpn visit visit_num score avg bar_count;
run;
proc sort data=bar_data;
by trtpn visit_num;
run;
ods path(prepend) work.templat(update);
/* Create template with dual y-axes */
proc template;
define statgraph bargraph_trt1_dual;
begingraph;
entrytitle "Bar Graph by Visit for School Group 1";
entrytitle "with Counts Displayed on Top";
layout overlay /
xaxisopts=( OFFSETMIN=0
label="Visit"
type=linear
linearopts=(
viewmin=1 viewmax=12
tickvaluelist=(1 2 3 4 5 6 7 8 9 10 11 12)
tickvalueformat=visitfmt.
TICKVALUEFITPOLICY=rotate
TICKVALUEROTATION=DIAGONAL
TICKVALUEPRIORITY=true)
)
yaxisopts=( OFFSETMIN=0
label="Score Values"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=1)
)
)
y2axisopts=( OFFSETMIN=0
label="Average Score"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=.5)
)
);
barchart x=visit_num y=score /
/* fillattrs=(color=CX4A7DB4 transparency=0.2)*/
outlineattrs=(color=CX4A7DB4 thickness=1)
barwidth=0.6
name="bars"
legendlabel="Score Values"
includemissinggroup=false
yaxis=y
BARLABEL=on
GROUP=trtpn
GROUPDISPLAY=cluster
OUTLINEATTRS=(thickness=0)
;
/* scatterplot x=visit_num y=score /*/
/* markerattrs=(symbol=square size=0)*/
/* datalabel=bar_count*/
/* datalabelposition=top*/
/* datalabelattrs=(*/
/* size=10pt */
/* weight=bold */
/* color=black*/
/* )*/
/* yaxis=y;*/
scatterplot x=visit_num y=avg /
markerattrs=(
symbol=circlefilled
size=8
color=CXFF7F0E
)
name="avg"
legendlabel="Average Score (Month 6 & 12)"
yaxis=y2;
seriesplot x=visit_num y=avg /
lineattrs=(
pattern=shortdash
color=CXFF7F0E
thickness=2
)
name="series"
legendlabel="Average Trend"
yaxis=y2;
/* Reference line to mark start of months */
*referenceline x=0.5 /
lineattrs=(pattern=solid color=lightgray thickness=1)
curvelabel="Baseline"
curvelabelposition=min;
/* Legend */
discretelegend "bars" "avg" "series" /
location=outside
halign=center
border=true
autoalign=(bottom);
endlayout;
endgraph;
end;
run;
/* Render the graph with dual axes */
proc sgrender data=bar_data template=bargraph_trt1_dual;
format score 4.0 avg 4.1 visit_num visitfmt.;
label score="Score Value"
avg="Average Score"
visit="Visit"
bar_count="Count";
run;
屏幕截图 2025-09-09 175017.png
You need to use option offsetmin=0 .
And the third question I don't understand , you want to display two bars one by one ?
layout overlay /
xaxisopts=( OFFSETMIN=0
label="Visit"
type=linear
linearopts=(
viewmin=1 viewmax=12
tickvaluelist=(1 2 3 4 5 6 7 8 9 10 11 12)
tickvalueformat=visitfmt.
TICKVALUEFITPOLICY=rotate
TICKVALUEROTATION=DIAGONAL
TICKVALUEPRIORITY=true)
)
yaxisopts=( OFFSETMIN=0
label="Score Values"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=1)
)
)
y2axisopts=( OFFSETMIN=0
label="Average Score"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=.5)
)
);
屏幕截图 2025-09-09 175017.png
Here is an example:
/* Create the monthly_score dataset */
data monthly_score;
length trtpn 8 visit $10 score 8 avg 8;
call streaminit(123);
do trtpn = 1 to 2;
do month = 1 to 12;
/* Create visit name */
visit = cats('Month', month);
score = floor(rand('uniform') * 6);
avg = .;
if month = 6 then do;
if trtpn = 1 then avg = 3.5;
else avg = 3.5 + (rand('uniform') - 0.5) * 0.4;
end;
else if month = 12 then do;
if trtpn = 1 then avg = 6.8;
else avg = 6.8 + (rand('uniform') - 0.5) * 0.4;
end;
if avg ne . then avg = round(avg, 0.1);
output;
end;
end;
drop month;
run;
/* Create dummy record for BL with avg=0 but score missing */
data dummy;
visit = "BL";
avg = 0;
trtpn = 1;
score = .;
run;
/* Create dataset with counts for trtpn=1 */
data monthly_score_trt1;
set monthly_score dummy;
run;
proc sort data = monthly_score_trt1;
by trtpn visit;
run;
/* Create a format to display month names */
proc format;
value visitfmt
0 = " "
1 = "Month1"
2 = "Month2"
3 = "Month3"
4 = "Month4"
5 = "Month5"
6 = "Month6"
7 = "Month7"
8 = "Month8"
9 = "Month9"
10 = "Month10"
11 = "Month11"
12 = "Month12";
run;
/* Create dataset with numeric visit variable */
data bar_data;
set monthly_score_trt1;
by trtpn visit;
if visit = "BL" then visit_num = 0;
else visit_num = input(scan(visit, -1, 'Month'), 8.);
bar_count = score;
if visit = "BL" then bar_count = .;
keep trtpn visit visit_num score avg bar_count;
run;
proc sort data=bar_data;
by trtpn visit_num;
run;
ods path(prepend) work.templat(update);
/* Create template with dual y-axes */
proc template;
define statgraph bargraph_trt1_dual;
begingraph;
entrytitle "Bar Graph by Visit for School Group 1";
entrytitle "with Counts Displayed on Top";
layout overlay /
xaxisopts=( OFFSETMIN=0
label="Visit"
type=linear
linearopts=(
viewmin=1 viewmax=12
tickvaluelist=(1 2 3 4 5 6 7 8 9 10 11 12)
tickvalueformat=visitfmt.
TICKVALUEFITPOLICY=rotate
TICKVALUEROTATION=DIAGONAL
TICKVALUEPRIORITY=true)
)
yaxisopts=( OFFSETMIN=0
label="Score Values"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=1)
)
)
y2axisopts=( OFFSETMIN=0
label="Average Score"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=.5)
)
);
barchart x=visit_num y=score /
fillattrs=(color=CX4A7DB4 transparency=0.2)
outlineattrs=(color=CX4A7DB4 thickness=1)
barwidth=0.6
name="bars"
legendlabel="Score Values"
includemissinggroup=false
yaxis=y
BARLABEL=on
GROUP=trtpn
GROUPDISPLAY=cluster
;
/* scatterplot x=visit_num y=score /*/
/* markerattrs=(symbol=square size=0)*/
/* datalabel=bar_count*/
/* datalabelposition=top*/
/* datalabelattrs=(*/
/* size=10pt */
/* weight=bold */
/* color=black*/
/* )*/
/* yaxis=y;*/
scatterplot x=visit_num y=avg /
markerattrs=(
symbol=circlefilled
size=8
color=CXFF7F0E
)
name="avg"
legendlabel="Average Score (Month 6 & 12)"
yaxis=y2;
seriesplot x=visit_num y=avg /
lineattrs=(
pattern=shortdash
color=CXFF7F0E
thickness=2
)
name="series"
legendlabel="Average Trend"
yaxis=y2;
/* Reference line to mark start of months */
*referenceline x=0.5 /
lineattrs=(pattern=solid color=lightgray thickness=1)
curvelabel="Baseline"
curvelabelposition=min;
/* Legend */
discretelegend "bars" "avg" "series" /
location=outside
halign=center
border=true
autoalign=(bottom);
endlayout;
endgraph;
end;
run;
/* Render the graph with dual axes */
proc sgrender data=bar_data template=bargraph_trt1_dual;
format score 4.0 avg 4.1 visit_num visitfmt.;
label score="Score Value"
avg="Average Score"
visit="Visit"
bar_count="Count";
run;
屏幕截图 2025-09-09 175017.png
If you need to display these two bar with two different color:
/* Create the monthly_score dataset */
data monthly_score;
length trtpn 8 visit $10 score 8 avg 8;
call streaminit(123);
do trtpn = 1 to 2;
do month = 1 to 12;
/* Create visit name */
visit = cats('Month', month);
score = floor(rand('uniform') * 6);
avg = .;
if month = 6 then do;
if trtpn = 1 then avg = 3.5;
else avg = 3.5 + (rand('uniform') - 0.5) * 0.4;
end;
else if month = 12 then do;
if trtpn = 1 then avg = 6.8;
else avg = 6.8 + (rand('uniform') - 0.5) * 0.4;
end;
if avg ne . then avg = round(avg, 0.1);
output;
end;
end;
drop month;
run;
/* Create dummy record for BL with avg=0 but score missing */
data dummy;
visit = "BL";
avg = 0;
trtpn = 1;
score = .;
run;
/* Create dataset with counts for trtpn=1 */
data monthly_score_trt1;
set monthly_score dummy;
if trtpn=2 then call missing(avg);
run;
proc sort data = monthly_score_trt1;
by trtpn visit;
run;
/* Create a format to display month names */
proc format;
value visitfmt
0 = " "
1 = "Month1"
2 = "Month2"
3 = "Month3"
4 = "Month4"
5 = "Month5"
6 = "Month6"
7 = "Month7"
8 = "Month8"
9 = "Month9"
10 = "Month10"
11 = "Month11"
12 = "Month12";
run;
/* Create dataset with numeric visit variable */
data bar_data;
set monthly_score_trt1;
by trtpn visit;
if visit = "BL" then visit_num = 0;
else visit_num = input(scan(visit, -1, 'Month'), 8.);
bar_count = score;
if visit = "BL" then bar_count = .;
keep trtpn visit visit_num score avg bar_count;
run;
proc sort data=bar_data;
by trtpn visit_num;
run;
ods path(prepend) work.templat(update);
/* Create template with dual y-axes */
proc template;
define statgraph bargraph_trt1_dual;
begingraph;
entrytitle "Bar Graph by Visit for School Group 1";
entrytitle "with Counts Displayed on Top";
layout overlay /
xaxisopts=( OFFSETMIN=0
label="Visit"
type=linear
linearopts=(
viewmin=1 viewmax=12
tickvaluelist=(1 2 3 4 5 6 7 8 9 10 11 12)
tickvalueformat=visitfmt.
TICKVALUEFITPOLICY=rotate
TICKVALUEROTATION=DIAGONAL
TICKVALUEPRIORITY=true)
)
yaxisopts=( OFFSETMIN=0
label="Score Values"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=1)
)
)
y2axisopts=( OFFSETMIN=0
label="Average Score"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=.5)
)
);
barchart x=visit_num y=score /
/* fillattrs=(color=CX4A7DB4 transparency=0.2)*/
outlineattrs=(color=CX4A7DB4 thickness=1)
barwidth=0.6
name="bars"
legendlabel="Score Values"
includemissinggroup=false
yaxis=y
BARLABEL=on
GROUP=trtpn
GROUPDISPLAY=cluster
OUTLINEATTRS=(thickness=0)
;
/* scatterplot x=visit_num y=score /*/
/* markerattrs=(symbol=square size=0)*/
/* datalabel=bar_count*/
/* datalabelposition=top*/
/* datalabelattrs=(*/
/* size=10pt */
/* weight=bold */
/* color=black*/
/* )*/
/* yaxis=y;*/
scatterplot x=visit_num y=avg /
markerattrs=(
symbol=circlefilled
size=8
color=CXFF7F0E
)
name="avg"
legendlabel="Average Score (Month 6 & 12)"
yaxis=y2;
seriesplot x=visit_num y=avg /
lineattrs=(
pattern=shortdash
color=CXFF7F0E
thickness=2
)
name="series"
legendlabel="Average Trend"
yaxis=y2;
/* Reference line to mark start of months */
*referenceline x=0.5 /
lineattrs=(pattern=solid color=lightgray thickness=1)
curvelabel="Baseline"
curvelabelposition=min;
/* Legend */
discretelegend "bars" "avg" "series" /
location=outside
halign=center
border=true
autoalign=(bottom);
endlayout;
endgraph;
end;
run;
/* Render the graph with dual axes */
proc sgrender data=bar_data template=bargraph_trt1_dual;
format score 4.0 avg 4.1 visit_num visitfmt.;
label score="Score Value"
avg="Average Score"
visit="Visit"
bar_count="Count";
run;
屏幕截图 2025-09-09 175017.png
"why the " double score" bar coming first"
I don't understand what " double score" is . Do you want to display 2 before 1 ? Just reorder it .
proc sort data=bar_data;
by descending trtpn visit_num;
run;
"Also the extra line graph is it becaz the avg value retained"
Yes. I already set it missing in my code.
data monthly_score_trt1;
set monthly_score dummy;
if trtpn=2 then call missing(avg);
run;
Thank you for the solution. Apologies, Error on my part that the treatments were assigned in reverse. Got fixed. thanks again.
Hi @Ksharp ,
Is there a way I can add the back group after Month 3 in the graph?. I was able to get the background, but don't know how to start from a specific value on- x-axis
" I can add the back group after Month 3"
Do you mean background ?
Here is the code you can start with.
/* Create the monthly_score dataset */
data monthly_score;
length trtpn 8 visit $10 score 8 avg 8;
call streaminit(123);
do trtpn = 1 to 2;
do month = 1 to 12;
/* Create visit name */
visit = cats('Month', month);
score = floor(rand('uniform') * 6);
avg = .;
if month = 6 then do;
if trtpn = 1 then avg = 3.5;
else avg = 3.5 + (rand('uniform') - 0.5) * 0.4;
end;
else if month = 12 then do;
if trtpn = 1 then avg = 6.8;
else avg = 6.8 + (rand('uniform') - 0.5) * 0.4;
end;
if avg ne . then avg = round(avg, 0.1);
output;
end;
end;
drop month;
run;
/* Create dummy record for BL with avg=0 but score missing */
data dummy;
visit = "BL";
avg = 0;
trtpn = 1;
score = .;
run;
/* Create dataset with counts for trtpn=1 */
data monthly_score_trt1;
set monthly_score dummy;
if trtpn=2 then call missing(avg);
run;
proc sort data = monthly_score_trt1;
by trtpn visit;
run;
/* Create a format to display month names */
proc format;
value visitfmt
0 = " "
1 = "Month1"
2 = "Month2"
3 = "Month3"
4 = "Month4"
5 = "Month5"
6 = "Month6"
7 = "Month7"
8 = "Month8"
9 = "Month9"
10 = "Month10"
11 = "Month11"
12 = "Month12";
run;
/* Create dataset with numeric visit variable */
data bar_data;
set monthly_score_trt1;
by trtpn visit;
if visit = "BL" then visit_num = 0;
else visit_num = input(scan(visit, -1, 'Month'), 8.);
bar_count = score;
if visit = "BL" then bar_count = .;
max=ifn(trtpn=2 and visit_num>3,7,.); /*7 is the max value in Y axis*/
keep trtpn visit visit_num score avg bar_count max;
run;
proc sort data=bar_data;
by trtpn visit_num;
run;
ods path(prepend) work.templat(update);
/* Create template with dual y-axes */
proc template;
define statgraph bargraph_trt1_dual;
begingraph;
entrytitle "Bar Graph by Visit for School Group 1";
entrytitle "with Counts Displayed on Top";
layout overlay /
xaxisopts=( OFFSETMIN=0
label="Visit"
type=linear
linearopts=(
viewmin=1 viewmax=12
tickvaluelist=(1 2 3 4 5 6 7 8 9 10 11 12)
tickvalueformat=visitfmt.
TICKVALUEFITPOLICY=rotate
TICKVALUEROTATION=DIAGONAL
TICKVALUEPRIORITY=true)
)
yaxisopts=( OFFSETMIN=0 OFFSETMAX=0
label="Score Values"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=1)
)
)
y2axisopts=( OFFSETMIN=0 OFFSETMAX=0
label="Average Score"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=.5)
)
);
barchart x=visit_num y=max /
barwidth=1
name="other_bars"
yaxis=y
OUTLINEATTRS=(thickness=0)
DATATRANSPARENCY=0.6
;
barchart x=visit_num y=score /
/* fillattrs=(color=CX4A7DB4 transparency=0.2)*/
outlineattrs=(color=CX4A7DB4 thickness=1)
barwidth=0.6
name="bars"
legendlabel="Score Values"
includemissinggroup=false
yaxis=y
BARLABEL=on
GROUP=trtpn
GROUPDISPLAY=cluster
OUTLINEATTRS=(thickness=0)
;
/* scatterplot x=visit_num y=score /*/
/* markerattrs=(symbol=square size=0)*/
/* datalabel=bar_count*/
/* datalabelposition=top*/
/* datalabelattrs=(*/
/* size=10pt */
/* weight=bold */
/* color=black*/
/* )*/
/* yaxis=y;*/
scatterplot x=visit_num y=avg /
markerattrs=(
symbol=circlefilled
size=8
color=CXFF7F0E
)
name="avg"
legendlabel="Average Score (Month 6 & 12)"
yaxis=y2;
seriesplot x=visit_num y=avg /
lineattrs=(
pattern=shortdash
color=CXFF7F0E
thickness=2
)
name="series"
legendlabel="Average Trend"
yaxis=y2;
/* Reference line to mark start of months */
*referenceline x=0.5 /
lineattrs=(pattern=solid color=lightgray thickness=1)
curvelabel="Baseline"
curvelabelposition=min;
/* Legend */
discretelegend "bars" "avg" "series" /
location=outside
halign=center
border=true
autoalign=(bottom);
endlayout;
endgraph;
end;
run;
/* Render the graph with dual axes */
proc sgrender data=bar_data template=bargraph_trt1_dual;
format score 4.0 avg 4.1 visit_num visitfmt.;
label score="Score Value"
avg="Average Score"
visit="Visit"
bar_count="Count";
run;
屏幕截图 2026-03-07 155045.png
An alternative way is using REFLINE statement.
/* Create template with dual y-axes */
proc template;
define statgraph bargraph_trt1_dual;
begingraph;
entrytitle "Bar Graph by Visit for School Group 1";
entrytitle "with Counts Displayed on Top";
layout overlay /
xaxisopts=( OFFSETMIN=0
label="Visit"
type=linear
linearopts=(
viewmin=1 viewmax=12
tickvaluelist=(1 2 3 4 5 6 7 8 9 10 11 12)
tickvalueformat=visitfmt.
TICKVALUEFITPOLICY=rotate
TICKVALUEROTATION=DIAGONAL
TICKVALUEPRIORITY=true)
)
yaxisopts=( OFFSETMIN=0 OFFSETMAX=0
label="Score Values"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=1)
)
)
y2axisopts=( OFFSETMIN=0 OFFSETMAX=0
label="Average Score"
linearopts=(
viewmin=0 viewmax=7
tickvaluesequence=(start=0 end=7 increment=.5)
)
);
REFERENCELINE X=4/lineattrs=(thickness=40) DATATRANSPARENCY=0.8 ;
REFERENCELINE X=5/lineattrs=(thickness=40) DATATRANSPARENCY=0.8 ;
REFERENCELINE X=6/lineattrs=(thickness=40) DATATRANSPARENCY=0.8 ;
REFERENCELINE X=7/lineattrs=(thickness=40) DATATRANSPARENCY=0.8 ;
REFERENCELINE X=8/lineattrs=(thickness=40) DATATRANSPARENCY=0.8 ;
REFERENCELINE X=9/lineattrs=(thickness=40) DATATRANSPARENCY=0.8 ;
REFERENCELINE X=10/lineattrs=(thickness=40) DATATRANSPARENCY=0.8 ;
REFERENCELINE X=11/lineattrs=(thickness=40) DATATRANSPARENCY=0.8 ;
REFERENCELINE X=12/lineattrs=(thickness=40) DATATRANSPARENCY=0.8 ;
barchart x=visit_num y=score /
/* fillattrs=(color=CX4A7DB4 transparency=0.2)*/
outlineattrs=(color=CX4A7DB4 thickness=1)
barwidth=0.6
name="bars"
legendlabel="Score Values"
includemissinggroup=false
yaxis=y
BARLABEL=on
GROUP=trtpn
GROUPDISPLAY=cluster
OUTLINEATTRS=(thickness=0)
;
/* scatterplot x=visit_num y=score /*/
/* markerattrs=(symbol=square size=0)*/
/* datalabel=bar_count*/
/* datalabelposition=top*/
/* datalabelattrs=(*/
/* size=10pt */
/* weight=bold */
/* color=black*/
/* )*/
/* yaxis=y;*/
scatterplot x=visit_num y=avg /
markerattrs=(
symbol=circlefilled
size=8
color=CXFF7F0E
)
name="avg"
legendlabel="Average Score (Month 6 & 12)"
yaxis=y2;
seriesplot x=visit_num y=avg /
lineattrs=(
pattern=shortdash
color=CXFF7F0E
thickness=2
)
name="series"
legendlabel="Average Trend"
yaxis=y2;
/* Reference line to mark start of months */
*referenceline x=0.5 /
lineattrs=(pattern=solid color=lightgray thickness=1)
curvelabel="Baseline"
curvelabelposition=min;
/* Legend */
discretelegend "bars" "avg" "series" /
location=outside
halign=center
border=true
autoalign=(bottom);
endlayout;
endgraph;
end;
run;
屏幕截图 2026-03-07 170214.png
Thank you very much.
It's your turn to help shape SAS Innovate 2027. Share your expertise and inspire the SAS community.
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.