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

Hi, I am trying to have the bar labels (total_obs2) be displayed at the base of each bar in this barline graph. Below is my sas code.

 

data have;
  input Unit $ Total_Obs2 Compliance_Rate;
  datalines;
a 139 85.5
b 419 90.2
c 636 92.7
d 4854 88.3
e 3807 84.6
f 1903 93.8
;
run;

/* Define the modified graph template */
proc template;
  define statgraph barline_labels;
    begingraph;
      entrytitle "HAVE - Compliance Rate and Total Observations";
      layout overlay/
        xaxisopts=(display=(tickvalues))
        yaxisopts=(label="# Observations" griddisplay=on offsetmin=0
          linearopts=(viewmin=0 viewmax=500
          thresholdmin=0 thresholdmax=1));
        barchartparm x=Unit y=Total_Obs2 /
          fillattrs=(transparency=0.4 color=Royalblue)
          segmentlabeltype=auto;
        seriesplot x=Unit y=Compliance_Rate / yaxis=y2 name="series" 
          lineattrs=(color=green thickness=3)
          markerattrs=(symbol=circlefilled size=100)
          datalabel=Compliance_Rate  DATALABELPOSITION=bottom ;
        discretelegend "series";
      endlayout;
    endgraph;
  end;
run;

/* Close the ODS HTML destination */
ods html close; 

/* Set the output directory and graph size */
ods listing gpath="c:file_path_location\Graphs";
ods graphics / reset imagename="HAVE" imagefmt=JPEG width=1200px height=700px;

/* Generate the modified graph with data labels */
proc sgrender data=have template=barline_labels;
  format Total_Obs2 comma12.;
run;
1 ACCEPTED SOLUTION

Accepted Solutions
DanH_sas
SAS Super FREQ

The best way is to use an axis table. I used an INNERMARGIN to protect the axis table from colliding with the bars. However, if you want the values inside of the bars, just remove the INNERMARGIN block.

 

proc template;
  define statgraph barline_labels;
    begingraph;
      entrytitle "HAVE - Compliance Rate and Total Observations";
      layout overlay/
        xaxisopts=(display=(tickvalues))
        yaxisopts=(label="# Observations" griddisplay=on offsetmin=0
          linearopts=(viewmin=0 viewmax=500
          thresholdmin=0 thresholdmax=1));
        innermargin / align=bottom;
           axistable x=Unit value=Total_Obs2 / display=(values);
        endinnermargin;
        barchartparm x=Unit y=Total_Obs2 /
          fillattrs=(transparency=0.4 color=Royalblue)
          segmentlabeltype=auto;
        seriesplot x=Unit y=Compliance_Rate / yaxis=y2 name="series"
          lineattrs=(color=green thickness=3)
          markerattrs=(symbol=circlefilled size=100)
          datalabel=Compliance_Rate  DATALABELPOSITION=bottom ;
        discretelegend "series";
      endlayout;
    endgraph;
  end;
run;

View solution in original post

8 REPLIES 8
DanH_sas
SAS Super FREQ

The best way is to use an axis table. I used an INNERMARGIN to protect the axis table from colliding with the bars. However, if you want the values inside of the bars, just remove the INNERMARGIN block.

 

proc template;
  define statgraph barline_labels;
    begingraph;
      entrytitle "HAVE - Compliance Rate and Total Observations";
      layout overlay/
        xaxisopts=(display=(tickvalues))
        yaxisopts=(label="# Observations" griddisplay=on offsetmin=0
          linearopts=(viewmin=0 viewmax=500
          thresholdmin=0 thresholdmax=1));
        innermargin / align=bottom;
           axistable x=Unit value=Total_Obs2 / display=(values);
        endinnermargin;
        barchartparm x=Unit y=Total_Obs2 /
          fillattrs=(transparency=0.4 color=Royalblue)
          segmentlabeltype=auto;
        seriesplot x=Unit y=Compliance_Rate / yaxis=y2 name="series"
          lineattrs=(color=green thickness=3)
          markerattrs=(symbol=circlefilled size=100)
          datalabel=Compliance_Rate  DATALABELPOSITION=bottom ;
        discretelegend "series";
      endlayout;
    endgraph;
  end;
run;
Scooby3g
Obsidian | Level 7

Hi @DanH_sas  and @ballardw ,

one more question >.<

is it possible to add criteria to have the bars be a certain color if it meets it? I tried the below method but was not successful. Would you have any suggestions?

 

data have;
  input Unit $ Total_Obs2 Compliance_Rate;
  datalines;
a 139 85.5
b 419 90.2
c 636 92.7
d 4854 88.3
e 3807 84.6
f 1903 93.8
;
run;

/* Create a new variable for color */
data have;
  set have;
  if Compliance_Rate >= 95 then Color = 'Blue';
  else if Compliance_Rate > 85 then Color = 'Yellow';
  else Color = 'Red';
run;

/* Define the modified graph template */
/* Define the modified graph template */
proc template;
  define statgraph barline_labels;
    begingraph;
      entrytitle "HAVE - Compliance Rate and Total Observations";

      /* Define color mapping */
      discreteattrmap name="color_map";
      value '>=95' / fillattrs=(color=Blue transparency=0.4);
      value '<95 & >85' / fillattrs=(color=Yellow transparency=0.4);
      value '<85' / fillattrs=(color=Red transparency=0.4);
      enddiscreteattrmap;

      layout overlay/
        xaxisopts=(display=(tickvalues))
        yaxisopts=(label="# Observations" griddisplay=on offsetmin=0
          linearopts=(viewmin=0 viewmax=500
          thresholdmin=0 thresholdmax=1));
        innermargin / align=bottom;
           axistable x=Unit value=Total_Obs2 / display=(values);
        endinnermargin;
        barchartparm x=Unit y=Total_Obs2 /
          fillattrsmap "color_map";
        seriesplot x=Unit y=Compliance_Rate / yaxis=y2 name="series"
          lineattrs=(color=green thickness=3)
          markerattrs=(symbol=circlefilled size=100)
          datalabel=Compliance_Rate  DATALABELPOSITION=bottom ;
        discretelegend "series";
      endlayout;
    endgraph;
  end;
run;


/* Close the ODS HTML destination */
ods html close; 

/* Set the output directory and graph size */
ods listing gpath="C:file path\_Graphs";
ods graphics / reset imagename="HAVE" imagefmt=JPEG width=1200px height=700px;

/* Generate the modified graph with data labels */
proc sgrender data=have template=barline_labels;
  format Total_Obs2 comma12.;
run;

 

 

 

ballardw
Super User

I think  you are missing the association between the variable to use the attribute map and the map,  the DISCRETEATTRVAR statement. From the discretteattrmap documentation first the map is defined, then the variable to use the map.

      discreteattrmap name="symbols" / trimleading=true ignorecase=true 
        discretelegendentrypolicy=attrmap;
        value "M" / markerattrs=(color=blue symbol=diamondfilled);
        value "F" / markerattrs=(color=green symbol=circlefilled); 
        value "U" / markerattrs=(color=red symbol=starfilled); 
      enddiscreteattrmap;

      /* Create attribute map variable GROUPMARKERS to associate attribute
         map SYMBOLS with column Sex */
      discreteattrvar attrvar=groupmarkers var=sex attrmap="symbols";
Scooby3g
Obsidian | Level 7

@ballardw  I added the  DISCRETEATTRVAR statement you had suggested and removed "fillattrs=(transparency=0.4 color=Color);" from the line of code but the color logic isnt applied. Did I miss something? 

 

I may be approaching this wrong in the code...what i want to do is have each Bar (Total_obs2) be a specific color base on the HH_rate variable. Is that possible at all? 

 

This is the code I used.

 

proc template;
  define statgraph barline;
    begingraph;
      entrytitle "Have - HH Rate and Total HH Observations ";
		
	  
      /* Define color mapping */
	  	discreteattrmap name="color_map" / trimleading=true ignorecase=true ;
      value '>=95' / fillattrs=(color=Blue transparency=0.4);
      value '<95 & >85' / fillattrs=(color=Yellow transparency=0.4);
      value '<85' / fillattrs=(color=Red transparency=0.4);
      enddiscreteattrmap;
		
		discreteattrvar attrvar=groupmarkers var=color attrmap="color_map";

      layout overlay/
        xaxisopts=(display=(tickvalues))
        yaxisopts=(label="# HH Observations" griddisplay=on offsetmin=0
          linearopts=(viewmin=0 viewmax=500
          thresholdmin=0 thresholdmax=1))
        y2axisopts=(label="HH Rate %" griddisplay=on offsetmin=0
          linearopts=(viewmin=0 viewmax=100
          thresholdmin=0 thresholdmax=0)) ;
		  innermargin / align=bottom;
           axistable x=Unit value=Total_Obs2 / display=(values);
        endinnermargin;
        barchartparm x=Unit y=Total_Obs2 /;
/*          fillattrs=(transparency=0.4 color=Color);*/
        
        seriesplot x=Unit y=Compliance_Rate / yaxis=y2 name="series" 
          lineattrs=(color=green thickness=3)
          markerattrs=(symbol=circlefilled size=100)
          datalabel=Compliance_Rate; 
        discretelegend "series";
      endlayout;
    endgraph;
  end;
run;

 

DanH_sas
SAS Super FREQ

I think your best option for this case is to use a Range Attributes Map and a COLORRESPONSE on your bar chart. You will not require the intermediate data set to map colors to ranges. As @ballardw mentioned, you need to use an attrvar statement to bind the real variable and the attrmap together. That attrvar is used on the COLORRESPONSE option of the bar chart to color it.

 

/* Define the modified graph template */
/* Define the modified graph template */
proc template;
  define statgraph barline_labels;
    begingraph;
      entrytitle "HAVE - Compliance Rate and Total Observations";

      /* Define color mapping */
      rangeattrmap name="color_map";
      range over / rangecolor=Blue;
      range 85-<95 / rangecolor=Yellow;
      range min-<85 / rangecolor=Red;
      endrangeattrmap;

      rangeattrvar attrvar=compvar var=Compliance_Rate attrmap="color_map";

      layout overlay/
        xaxisopts=(display=(tickvalues))
        yaxisopts=(label="# Observations" griddisplay=on offsetmin=0
          linearopts=(viewmin=0 viewmax=500
          thresholdmin=0 thresholdmax=1));
        innermargin / align=bottom;
           axistable x=Unit value=Total_Obs2 / display=(values);
        endinnermargin;
        barchartparm x=Unit y=Total_Obs2 / datatransparency=0.4
          colorresponse=compvar;
        seriesplot x=Unit y=Compliance_Rate / yaxis=y2 name="series"
          lineattrs=(color=green thickness=3)
          markerattrs=(symbol=circlefilled size=100)
          datalabel=Compliance_Rate  DATALABELPOSITION=bottom ;
        discretelegend "series";
      endlayout;
    endgraph;
  end;
run;
Scooby3g
Obsidian | Level 7

Thank you so much @DanH_sas !!!!! 

ballardw
Super User

Do you mean show the value of Total_obs2 on the Axis? Inside the bar at the base? Outside the graph below the bar? Where do the values go?

AXISTABLE might be appropriate.

Or add an additional Y coordinate value and use a text plot (may be more flexible if you want the values inside the bar when close to the compliance_rate values).

data have;
  input Unit $ Total_Obs2 Compliance_Rate ;
  texty=100;
  datalines;
a 139 85.5
b 419 90.2
c 636 92.7
d 4854 88.3
e 3807 84.6
f 1903 93.8
;
run;

/* Define the modified graph template */
proc template;
  define statgraph barline_labels;
    begingraph;
      entrytitle "HAVE - Compliance Rate and Total Observations";
      layout overlay/
        xaxisopts=(display=(tickvalues))
        yaxisopts=(label="# Observations" griddisplay=on offsetmin=0
          linearopts=(viewmin=0 viewmax=500
          thresholdmin=0 thresholdmax=1));
        barchartparm x=Unit y=Total_Obs2 /
          fillattrs=(transparency=0.4 color=Royalblue)
          segmentlabeltype=none;
        seriesplot x=Unit y=Compliance_Rate / yaxis=y2 name="series" 
          lineattrs=(color=green thickness=3)
          markerattrs=(symbol=circlefilled size=100)
          datalabel=Compliance_Rate  DATALABELPOSITION=bottom ;
        discretelegend "series";
        textplot x=unit y=texty text= Total_Obs2;
      endlayout;
    endgraph;
  end;
run;

 

 

Running your example code you may have collisions with some of the Compliance_rate labels.

SAS Innovate 2025: Register Now

Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
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
  • 8 replies
  • 1261 views
  • 3 likes
  • 3 in conversation