Stata FAQ
How can I make a bar graph with error bars?

Say that you were looking at writing scores broken down by race and ses.  You might want to graph the mean and confidence interval for each group using a bar chart with error bars as illustrated below. This FAQ shows how you can make a graph like this, building it up step by step.
First, lets get the data file we will be using.
use http://www.ats.ucla.edu/stat/stata/notes/hsb2, clear
Now, let's use the collapse command to make the mean and standard deviation by race and ses.
collapse (mean) meanwrite= write (sd) sdwrite=write (count) n=write, by(race ses)
Now, let's make the upper and lower values of the confidence interval.
generate hiwrite = meanwrite + invttail(n-1,0.025)*(sdwrite / sqrt(n))
generate lowrite = meanwrite - invttail(n-1,0.025)*(sdwrite / sqrt(n))
Now we are ready to make a bar graph of the data The graph bar command makes a pretty good bar graph.
graph bar meanwrite, over(race) over(ses)
We can make the graph look a bit prettier by adding the asyvars option as shown below.
graph bar meanwrite, over(race) over(ses) asyvars 
But, this graph does not have the error bars in it. Unfortunately, as nice as the graph bar command is, it does not permit error bars. However, we can make a twoway graph that has error bars as shown below. Unfortunately, this graph is not as attractive as the graph from graph bar.
graph twoway (bar meanwrite race) (rcap hiwrite lowrite race), by(ses)
So, we have a conundrum. The graph bar command will make a lovely bar graph, but will not support error bars. The twoway bar command makes lovely error bars, but it does not resemble the nice graph that we liked from the graph bar command. However, we can finesse the twoway bar command to make a graph that resembles the graph bar command and then combine that with error bars. Here is a step by step process.

First, we will make a variable sesrace that will be a single variable that contains the ses and race information. Note how sesrace has a gap between the levels of ses (at 5 and 10).
generate sesrace = race    if ses == 1
replace  sesrace = race+5  if ses == 2
replace  sesrace = race+10 if ses == 3
sort sesrace
list sesrace ses race, sepby(ses)

     +---------------------------------+
     | sesrace      ses           race |
     |---------------------------------|
  1. |       1      low       hispanic |
  2. |       2      low          asian |
  3. |       3      low   african-amer |
  4. |       4      low          white |
     |---------------------------------|
  5. |       6   middle       hispanic |
  6. |       7   middle          asian |
  7. |       8   middle   african-amer |
  8. |       9   middle          white |
     |---------------------------------|
  9. |      11     high       hispanic |
 10. |      12     high          asian |
 11. |      13     high   african-amer |
 12. |      14     high          white |
     +---------------------------------+
Now, we will make a graph using graph twoway. Notice how the bars are in three groups of four bars. The three groups correspond to the three levels of ses and the four bars within each group correspond to the four levels of race. You can relate this grouping to the way that we constructed raceses above.
twoway (bar meanwrite sesrace)
We can now overlay the error bars by overlaying a rcap graph
twoway (bar meanwrite sesrace) (rcap hiwrite lowrite sesrace)
This kind of looks like what we want, but it would look nicer if each of the bars for the four different races were different colors. We can do this by overlaying four separate bar graphs, one for each racial group.
twoway (bar meanwrite sesrace if race==1) ///
       (bar meanwrite sesrace if race==2) ///
       (bar meanwrite sesrace if race==3) ///
       (bar meanwrite sesrace if race==4) ///
       (rcap hiwrite lowrite sesrace)
This is looking better, but let's use the legend to label the bars better.
twoway (bar meanwrite sesrace if race==1) ///
       (bar meanwrite sesrace if race==2) ///
       (bar meanwrite sesrace if race==3) ///
       (bar meanwrite sesrace if race==4) ///
       (rcap hiwrite lowrite sesrace), ///
       legend( order(1 "Hispanic" 2 "Asian" 3 "Black" 4 "White") )
The legend labels the bars nicely, but would look cleaner if it were just one row and the x axis of the graph does not convey that the three groups of bars correspond to the three groups of ses. We can use the xlabel() option to remedy that. We also add better titles for the x and y axes as well.
twoway (bar meanwrite sesrace if race==1) ///
       (bar meanwrite sesrace if race==2) ///
       (bar meanwrite sesrace if race==3) ///
       (bar meanwrite sesrace if race==4) ///
       (rcap hiwrite lowrite sesrace), ///
       legend(row(1) order(1 "Hispanic" 2 "Asian" 3 "Black" 4 "White") ) ///
       xlabel( 2.5 "Low" 7.5 "Middle" 12.5 "High", noticks) ///
       xtitle("Socio Economic Status") ytitle("Mean Writing Score")
Now we have a graph that looks like the kind of graph that we would get from graph bar but by finessing graph twoway bar into making this pretty graph, we could then overlay the rbar graph to get the error bars we desired.

How to cite this page

Report an error on this page or leave a comment

The content of this web site should not be construed as an endorsement of any particular web site, book, or software product by the University of California.