Written and maintained by CASRAI Editorial Board
Last updated
Stata has two ways to draw a histogram: the standalone histogram command, which is fast to type and covers bin control, scaling, a normal-curve overlay, and by-group panels on its own, and twoway histogram, the plottype form used inside a graph twoway call when you need to combine a histogram with another plot — a second overlaid histogram, a kernel density curve, a fitted function — in one coordinate space. Most single-histogram jobs only need the standalone command. Reach for twoway histogram specifically when you’re layering more than one plot together.
The histogram command: basic syntax
The minimal call needs only a variable name:
histogram varname
For example, plotting the distribution of grant review turnaround times (variable days):
histogram days
By default Stata treats the variable as continuous, chooses a bin count automatically (an algorithm based on the data’s range and sample size), and scales the y-axis to density — bar heights such that the total area sums to 1, not raw counts. That default surprises people used to counts-based histograms elsewhere; see the scaling section below to change it.
Controlling bins: bin(), width(), and start()
Three options control how the data is binned, and only one of the first two can be used at a time:
bin(#)— sets the number of bins directly. Stata divides the observed range into that many equal-width bins.width(#)— sets the bin width directly instead of the count. Use this when the width itself is the meaningful unit (e.g., one bin per 5-point score band) rather than an arbitrary bin count.start(#)— sets the left edge of the first bin. Combine with either option above to control exactly where bin boundaries fall, which matters when a natural boundary (a 0 point, a passing threshold) should sit exactly on a bin edge rather than splitting across two.
histogram days, bin(20)
histogram days, width(5) start(0)
bin() and width() cannot both be specified — Stata computes one from the other and rejects the command if you supply both.
For a variable that’s genuinely discrete — a count, a small set of integer values, a Likert item — add the discrete option instead of binning at all. It puts one bar on each distinct observed value rather than grouping values into ranges, which avoids the misleading gaps and merges a continuous-style bin width can create on count data:
histogram num_coauthors, discrete
Density, frequency, fraction, and percent: the y-axis scale
Four mutually exclusive options control what the y-axis represents:
density— the default. Bar heights scale so total bar area equals 1; this is what makes anormaloverlay (below) comparable to the bars, since a normal density curve is also scaled to unit area.frequency— raw observation counts per bin. The most intuitive reading for a general audience, but not on the same scale as a normal curve overlay.fraction— each bar’s share of the total sample, as a proportion (0 to 1).percent— the same asfraction, expressed as a percentage (0 to 100).
histogram days, frequency
histogram days, percent
If you plan to overlay a normal curve, leave the default density scale in place (or add normal, which forces it) — frequency/fraction/percent scaling with normal added will still run, but the curve and bars are no longer on a comparable footing.
Overlaying a normal curve: the normal option
Add normal to draw a normal density curve, fit to the variable’s own mean and standard deviation, over the histogram — a quick visual check of how far the data departs from normality:
histogram days, normal
Style the overlay line with normopts(), which accepts standard line options:
histogram days, normal normopts(lcolor(red) lwidth(medthick))
A normal overlay is a visual aid, not a formal test — a curve that looks close by eye can still fail a formal normality test on a large sample, and a curve that looks off by eye can still be fine for tests that are robust to mild non-normality. For a fuller treatment of checking the normality assumption itself, see CASRAI’s guide to normality of distribution, and for reading skew direction off the shape, left-skewed vs. right-skewed distributions.
By-group panels: the by() option
Add by(groupvar) to produce one histogram panel per distinct value of a grouping variable, laid out as small multiples on shared axes rather than overlaid in one plot area:
histogram days, by(department)
The standard by() layout controls apply, so you can adjust the panel grid and add a shared title:
histogram days, by(department, rows(2) title("Review turnaround by department"))
This is the right tool when you want to compare distributions side by side, each with its own clean, uncluttered bars. It is the wrong tool when you want the groups overlapping in a single plot area for a direct visual comparison of overlap — for that, use twoway histogram with transparency, covered next.
twoway histogram: combining a histogram with other plot types
The standalone histogram command draws one complete graph and accepts only histogram-specific options. twoway histogram is the same underlying plot, but used as one layer inside a graph twoway call, which lets you combine it with any other twoway plottype — another histogram, a kernel density estimate, a fitted line, an arbitrary function — in one shared coordinate space:
twoway (histogram days, percent) (kdensity days), legend(off)
That overlays a smoothed kernel density curve on top of the histogram bars, both on the same percent-equivalent scale. It’s a more flexible alternative to histogram‘s own normal option when you want a nonparametric density estimate rather than a normal curve fit, or when you want to layer more than one addition at once.
The case twoway histogram genuinely enables that the standalone command cannot is overlaying two histograms in the same plot area — comparing two groups’ distributions directly, not as separate panels:
twoway (histogram days if department == 1, color(blue%40)) ///
(histogram days if department == 2, color(red%40)), ///
legend(order(1 "Department A" 2 "Department B"))
Each parenthesized piece is a full if-restricted histogram call with its own styling; the %40 suffix on color() sets partial transparency, so overlapping bars from both groups stay visible instead of one occluding the other. by() gives you the same two distributions side by side in separate panels; twoway histogram with two layers gives you them overlapping in one panel — pick based on whether the comparison you want to show is “same axes, different panels” or “same panel, direct overlap.”
One shortcut worth knowing before reaching for full twoway syntax: the standalone histogram command has its own addplot() option, which adds a single extra twoway-plot layer without writing out the parenthesized twoway (...) (...) form at all:
histogram days, normal addplot(kdensity days)
addplot() is the simpler path for “one histogram plus one extra layer.” Write the full twoway (histogram ...) (...) syntax when you need more than one histogram layer, precise control over each layer’s draw order and styling, or a second y-axis (yaxis(2)) for a layer on a different scale.
Common mistakes
- Combining
bin()andwidth(). Stata rejects the command — pick one. If you need a specific width, usewidth()directly rather than trying to reverse-engineer the matchingbin()count. - Reading density bars as counts. The default y-axis is density (area = 1), not frequency. If a reviewer or a reader expects raw counts, add
frequencyexplicitly — don’t leave the default in place and relabel the axis by hand. - Binning genuinely discrete data. Running the default continuous binning on integer count data (number of publications, number of coauthors) can split or merge values in a way that misrepresents the distribution. Use
discretefor count-like variables instead of tuningbin()/width()to approximate it. - Expecting
by()to overlay groups.by()produces separate panels, not one overlapping plot — a common source of “I wanted them on the same axes” confusion. Use twotwoway histogramlayers with transparency instead. - Opaque overlapping bars. Two
twoway histogramlayers without a transparency setting (the%NNsuffix oncolor()) will have the second layer’s bars simply cover the first’s wherever they overlap, hiding real overlap in the data.
Frequently asked questions
What’s the difference between histogram and twoway histogram in Stata?
The histogram command draws one complete, standalone histogram and only accepts histogram-specific options. twoway histogram is the same plot used as one layer inside a graph twoway call, so it can be combined with other twoway plottypes — another histogram, a kernel density curve, a fitted line — in the same coordinate space. For a single histogram with no combination, they produce the same bars; the distinction only matters once you’re layering plots.
How do I control bin width in Stata’s histogram command?
Use bin(#) to set the number of bins, or width(#) to set the bin width directly — not both at once. Add start(#) to fix where the first bin begins if a natural boundary needs to land exactly on a bin edge.
How do I overlay a normal curve on a Stata histogram?
Add the normal option: histogram varname, normal. Stata fits the curve to the variable’s own mean and standard deviation and draws it on the same density scale as the bars. Style the line itself with normopts().
How do I make separate histograms for each group in Stata?
Add by(groupvar) to the histogram command for one panel per group value on shared axes. If you want the groups overlapping in a single plot area instead of separate panels, use two twoway histogram layers with a transparent color() setting.
Does a Stata histogram show counts or density by default?
Density — bar heights are scaled so the total area sums to 1, not raw observation counts. Add frequency to show counts, fraction for proportion of the total, or percent for percentage.
For the broader statistical logic behind histograms and how to read one regardless of software, see CASRAI’s guide to histograms. For related descriptive-statistics groundwork in Stata, see descriptive statistics and standard deviation. For other Stata procedures, see running a t-test in Stata and the collapse command in Stata, and for choosing Stata over another package, CASRAI’s comparisons of SPSS vs. Stata and R vs. Stata.








