Written and maintained by CASRAI Editorial Board
Last updated
graph bar draws vertical bars and graph hbar draws the identical chart rotated onto its side — every option covered here works the same way in both. By default each bar shows the mean of a variable; over() is the option that turns a single bar into a categorical breakdown, and it is what most of the syntax below revolves around.
This page covers the syntax specifically: choosing graph bar vs. graph hbar, building grouped and stacked variants with over() and asyvars, and adding value labels. For the underlying statistics — means, standard deviations, and how to report them — see CASRAI’s guide to descriptive statistics first if you need that background.
graph bar vs. graph hbar: which one to use
The two commands accept identical syntax; the only difference is orientation. graph bar puts categories on the horizontal axis and bar height encodes the value — the familiar default. graph hbar puts categories on the vertical axis and bar length encodes the value.
Use graph hbar whenever category labels are long or numerous: institution names, survey item text, or more than five or six over() levels all become unreadable as rotated labels under vertical bars, but read cleanly down the left margin of a horizontal chart. Use graph bar for short category labels (year, group, yes/no) where the default vertical orientation is more familiar to readers.
graph bar (mean) score, over(department)
graph hbar (mean) score, over(department)
Basic syntax: plotting means, sums, and other statistics
A bar in graph bar is always a summary statistic of a variable, never a raw value — there is no way to plot one bar per observation. The statistic defaults to the mean if you don’t specify one; write it as a parenthetical immediately before the variable to use something else:
graph bar score
graph bar (mean) score
graph bar (sum) score
graph bar (median) score
graph bar (count) score
The first two lines are equivalent. Common alternatives to (mean): (sum) for totals, (median), (sd), (count) for the number of non-missing observations, and (percent) for the share of observations falling in each category — useful for a distribution chart rather than a summary-statistic chart:
graph bar (percent), over(status)
Note there’s no variable name after (percent) here — percent-of-observations doesn’t need one; Stata is counting rows in each over() category, not summarizing a value.
The over() option: categorical breakdowns
over(groupvar) is what turns a single overall bar into one bar per category. It’s the option that does almost all the real work in a Stata bar chart:
graph bar (mean) score, over(department)
You can stack multiple over() options to nest categories — the first over() becomes the outer grouping (shown as the outer axis labels, spanning several bars), and each subsequent over() nests inside it:
graph bar (mean) score, over(department) over(cohort)
That example produces one cluster of bars per department, with a bar for each cohort inside every department’s cluster. Relabel category values inline instead of recoding the underlying variable with relabel():
graph bar (mean) score, over(status, relabel(1 "Active" 2 "Completed" 3 "Withdrawn"))
over() is not the same as the separate by() option: over() puts multiple categories inside one graph, while by() draws an entirely separate small-multiple graph per level — useful when a breakdown has too many combinations to read as nested bars in a single frame:
graph bar (mean) score, over(department) by(site)
Grouped (clustered) bars from multiple variables
Listing more than one y-variable produces a cluster of side-by-side bars automatically, without needing over() at all — one bar per variable, or one cluster of bars per variable if combined with over():
graph bar (mean) pretest posttest
graph bar (mean) pretest posttest, over(group)
The second line draws one cluster per group level, with a pretest bar and a posttest bar side by side inside each cluster — the standard “grouped bar chart” layout for a before/after or a multi-series comparison.
Stacked bars: asyvars and stack
By default, combining multiple y-variables with over() clusters the bars side by side, as above. To stack them into a single bar per category instead, add stack:
graph bar (mean) pretest posttest, over(group) stack
The harder case is stacking a single variable broken down by an over() group — e.g., one bar per department, each bar internally divided into segments for a status category. stack alone can’t do this, because with a single y-variable and no second series, there’s nothing to stack against. asyvars is the option that fixes this: it tells Stata to treat the levels of the first over() group as if they were separate y-variables, which gives stack something to work with:
graph bar (mean) score, over(status) over(department) asyvars stack
That produces one stacked bar per department, with each bar internally divided by status, and a legend keyed to the status categories — the composition chart most people actually mean when they ask for a “stacked bar chart in Stata.” Without asyvars, the same command draws nested clustered bars instead of stacked segments.
Adding value labels
blabel(bar) prints the value of each bar directly above (or, for graph hbar, beside) the bar itself — useful when exact figures matter and you don’t want a reader estimating them off the axis:
graph bar (mean) score, over(department) blabel(bar)
graph bar (mean) score, over(department) blabel(bar, format(%9.1f))
The format() suboption controls decimal places using a standard Stata display format — %9.1f above rounds to one decimal place. Without it, Stata prints the statistic’s default precision, which for a mean is often more decimal places than a chart needs.
Common mistakes
- Forgetting the default statistic is the mean, not the count.
graph bar scoreplots averagescore, not how many observations have each value ofscore— if you want a frequency count, use(count)explicitly, or usehistograminstead ifscoreis continuous. - Confusing
over()nesting order. The firstover()is the outer grouping and the last is the innermost; swapping the order ofover(department) over(cohort)toover(cohort) over(department)changes which variable’s labels sit on the outer axis, not just the visual grouping. - Adding
stackwithoutasyvarsand expecting a single-variable breakdown to stack. As above,stackneeds either multiple y-variables orasyvarsto have separate series to stack — on its own with one variable and oneover(), it has no effect. - Using
graph barfor many long category labels. Vertical bars force rotated or truncated axis text once you’re past a handful of categories; switch tograph hbarrather than fighting label angle options.
Frequently asked questions
What’s the actual difference between graph bar and graph hbar?
Orientation only. Every option — over(), asyvars, stack, blabel() — works identically in both; graph hbar simply draws categories on the vertical axis and values on the horizontal axis instead of the reverse.
How do I plot percentages instead of means?
Use (percent) as the statistic with no variable name: graph bar (percent), over(group) shows what share of observations fall into each level of group, rather than summarizing a separate outcome variable.
Why won’t stack produce a stacked bar for my single variable?
Because stack stacks series, and a single y-variable with one over() group has only one series. Add asyvars so the over() levels are treated as the stackable series.
Can I show two grouping variables side by side instead of nested?
Yes — list the outcome as multiple variables (one per series) combined with a single over(), rather than two over() options, to get clustered rather than nested bars. See the grouped-bars section above.








