BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
SASuserlot
Barite | Level 11

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;
1 ACCEPTED SOLUTION

Accepted Solutions
Ksharp
Super User

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

View solution in original post

11 REPLIES 11
Ksharp
Super User

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

SASuserlot
Barite | Level 11

Screenshot 2026-02-24 025230.pngThank you @Ksharp . 

Yes. Some thing like this. for every visit. 

 

 

Ksharp
Super User

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

 

Ksharp
Super User

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

SASuserlot
Barite | Level 11
Thank you very much. I Really appreciate your help. Got a question, when I do why the " double score" bar coming first. Is there any way I can order the regular score first and double score second. Also the extra line graph is it becaz the avg value retained ( if it's the case I can adjust it manually by making it missing)
Ksharp
Super User

"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;

 

SASuserlot
Barite | Level 11

Thank you for the solution.  Apologies, Error on my part that the treatments were assigned in reverse. Got fixed. thanks again.

SASuserlot
Barite | Level 11

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

Ksharp
Super User

" 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

Ksharp
Super User

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

SASuserlot
Barite | Level 11

Thank you very much. 

Catch up on SAS Innovate 2026

Nearly 200 sessions are now available on demand with the SAS Innovate Digital Pass.

Explore 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
  • 1328 views
  • 8 likes
  • 2 in conversation