*! version 7.02 24nov2013

Title

binscatter -- Binned scatterplots

Syntax

binscatter
varlist [if] [in]
[weight]
[, options]

where varlist is

y_1 [y_2 [...]] x

options

Main
by(varname) plot separate series for each group (see important notes below)

medians plot within-bin medians instead of means

Bins
nquantiles(#) number of equal-sized bins to be created; default is 20

genxq(varname) generate quantile variable containing the bins

discrete each x-value to be used as a separate bin

xq(varname) variable which already contains bins; bins therefore not recomputed

Controls
controls(varlist) residualize the x & y variables on controls before plotting

absorb(varname) residualize the x & y variables on a categorical variable

noaddmean do not add the mean of each variable back to its residuals

Fit Line
linetype(binscatter##linetype:linetype) type of fit line; default is lfit, may also be qfit, connect, or none

rd(numlist) create regression discontinuity at x-values

reportreg display the regressions used to estimate the fit lines

Graph Style
colors(colorstylelist) ordered list of colors

mcolors(colorstylelist) overriding ordered list of colors for the markers

lcolors(colorstylelist) overriding ordered list of colors for the lines

msymbols(symbolstylelist) ordered list of symbols

twoway_options titles, legends, axes, added lines and text,
regions, name, aspect ratio, etc.

Save Output
savegraph(filename) save graph to file; format automatically detected from extension [ex: .gph .jpg .png]

savedata(filename) save filename.csv containg scatterpoint data, and filename.do to process data into graph

replace overwrite existing files

fastxtile options
nofastxtile use xtile instead of fastxtile

randvar(varname) use varname to sample observations when computing quantile boundaries

randcut(#) upper bound on randvar() used to cut the sample; default is randcut(1)

randn(#) number of observations to sample when computing quantile boundaries

aweights and fweights are allowed;
see weight.

Description

binscatter generates binned scatterplots, and is optimized for speed in large datasets.

Binned scatterplots provide a non-parametric way of visualizing the relationship between two variables.
With a large number of observations, a scatterplot that plots every data point would become too crowded
to interpret visually. binscatter groups the x-axis variable into equal-sized bins, computes the
mean of the x-axis and y-axis variables within each bin, then creates a scatterplot of these data points.
The result is a non-parametric visualization of the conditional expectation function.

binscatter provides built-in options to control for covariates before plotting the relationship
(see Controls). Additionally, binscatter will plot fit lines based
on the underlying data, and can automatically handle regression discontinuities (see Fit Line).

Options

Main

by(varname) plots a separate series for each by-value. Both numeric and string by-variables
are supported, but numeric by-variables will have faster run times.

Users should be aware of the two ways in which binscatter does not condition on by-values:

1) When combined with controls() or absorb(), the program residualizes using the restricted model in which each covariate
has the same coefficient in each by-value sample. It does not run separate regressions for each by-value. If you wish to control for
covariates using a different model, you can residualize your x- and y-variables beforehand using your desired model then run binscatter
on the residuals you constructed.

2) When not combined with discrete or xq(), the program constructs a single set of bins
using the unconditional quantiles of the x-variable. It does not bin the x-variable separately for each by-value.
If you wish to use a different binning procedure (such as constructing equal-sized bins separately for each
by-value), you can construct a variable containing your desired bins beforehand, then run binscatter with xq().

medians creates the binned scatterplot using the median x- and y-value within each bin, rather than the mean.
This option only affects the scatter points; it does not, for instance, cause linetype(lfit)
to use quantile regression instead of OLS when drawing a fit line.

Bins

nquantiles(#) specifies the number of equal-sized bins to be created. This is equivalent to the number of
points in each series. The default is 20. If the x-variable has fewer
unique values than the number of bins specified, then discrete will be automatically invoked, and no
binning will be performed.
This option cannot be combined with discrete or xq().

Binning is performed after residualization when combined with controls() or absorb().
Note that the binning procedure is equivalent to running xtile, which in certain cases will generate
fewer quantile categories than specified. (e.g. sysuse auto; xtile temp=mpg, nq(20); tab temp)

genxq(varname) creates a categorical variable containing the computed bins.
This option cannot be combined with discrete or xq().

discrete specifies that the x-variable is discrete and that each x-value is to be treated as
a separate bin. binscatter will therefore plot the mean y-value associated with each x-value.
This option cannot be combined with nquantiles(), genxq() or xq().

In most cases, discrete should not be combined with controls() or absorb(), since residualization occurs before binning,
and in general the residual of a discrete variable will not be discrete.

xq(varname) specifies a categorical variable that contains the bins to be used, instead of binscatter generating them.
This option is typically used to avoid recomputing the bins needlessly when binscatter is being run repeatedly on the same sample
and with the same x-variable.
It may be convenient to use genxq(binvar) in the first iteration, and specify xq(binvar) in subsequent iterations.
Computing quantiles is computationally intensive in large datasets, so avoiding repetition can reduce run times considerably.
This option cannot be combined with nquantiles(), genxq() or discrete.

Care should be taken when combining xq() with controls() or absorb(). Binning takes place after residualization,
so if the sample changes or the control variables change, the bins ought to be recomputed as well.

Controls

controls(varlist) residualizes the x-variable and y-variables on the specified controls before binning and plotting.
To do so, binscatter runs a regression of each variable on the controls, generates the residuals, and adds the sample mean of
each variable back to its residuals.

absorb(varname) absorbs fixed effects in the categorical variable from the x-variable and y-variables before binning and plotting,
To do so, binscatter runs an areg of each variable with absorb(varname) and any controls() specified. It then generates the
residuals and adds the sample mean of each variable back to its residuals.

noaddmean prevents the sample mean of each variable from being added back to its residuals, when combined with controls() or absorb().

Fit Line

linetype(binscatter##linetype:linetype) specifies the type of line plotted on each series.
The default is lfit, which plots a linear fit line. Other options are qfit for a quadratic fit line,
connect for connected points, and none for no line.

Linear or quadratic fit lines are estimated using the underlying data, not the binned scatter points. When combined with
controls() or absorb(), the fit line is estimated after the variables have been residualized.

rd(numlist) draws a dashed vertical line at the specified x-values and generates regression discontinuities when combined with line(lfit|qfit).
Separate fit lines will be estimated below and above each discontinuity. These estimations are performed using the underlying data, not the binned scatter points.

The regression discontinuities do not affect the binned scatter points in any way.
Specifically, a bin may contain a discontinuity within its range, and therefore include data from both sides of the discontinuity.

reportreg displays the regressions used to estimate the fit lines in the results window.

Graph Style

colors(colorstylelist) specifies an ordered list of colors for each series

mcolors(colorstylelist) specifies an ordered list of colors for the markers of each series, which overrides any list provided in colors()

lcolors(colorstylelist) specifies an ordered list of colors for the line of each series, which overrides any list provided in colors()

msymbols(symbolstylelist) specifies an ordered list of symbols for each series

twoway_options:

Any unrecognized options added to binscatter are appended to the end of the twoway command which generates the
binned scatter plot.

These can be used to control the graph titles,
legends, axes, added lines and text,
regions, name, aspect ratio, etc.

Save Output

savegraph(filename) saves the graph to a file. The format is automatically detected from the extension specified [ex: .gph .jpg .png],
and either graph save or graph export is run. If no file extension is specified .gph is assumed.

savedata(filename) saves filename.csv containing the binned scatterpoint data, and filename.do which
loads the scatterpoint data, labels the variables, and plots the binscatter graph.

Note that the saved result e(cmd) provides an alternative way of capturing the binscatter graph and editing it.

replace specifies that files be overwritten if they alredy exist

fastxtile options

nofastxtile forces the use of xtile instead of fastxtile to compute bins. There is no situation where this should
be necessary or useful. The fastxile program generates identical results to xtile, but runs faster on large datasets, and has
additional options for random sampling which may be useful to increase speed.

fastxtile is built into the binscatter code, but may also be installed
separately from SSC (click here to install) for use outside of binscatter.

randvar(varname) requests that varname be used to select a
sample of observations when computing the quantile boundaries. Sampling increases
the speed of the binning procedure, but generates bins which are only approximately equal-sized
due to sampling error. It is possible to omit this option and still perform random sampling from U[0,1]
as described below in randcut() and randn().

randcut(#) specifies the upper bound on the variable contained
in randvar(varname). Quantile boundaries are approximated using observations for which
randvar() <= #. If no variable is specified in randvar(),
a standard uniform random variable is generated. The default is randcut(1).
This option cannot be combined with randn().

randn(#) specifies an approximate number of observations to sample when
computing the quantile boundaries. Quantile boundaries are approximated using observations
for which a uniform random variable is <= #/N. The exact number of observations
sampled may therefore differ from #, but it equals # in expectation. When this option is
combined with randvar(varname), varname ought to be distributed U[0,1].
Otherwise, a standard uniform random variable is generated. This option cannot be combined
with randcut().

Examples

Load the 1988 extract of the National Longitudinal Survey of Young Women and Mature Women.

. sysuse nlsw88

. keep if inrange(age,35,44) & inrange(race,1,2)

What is the relationship between job tenure and wages?

. scatter wage tenure

. binscatter wage tenure

The scatter was too crowded to be easily interpetable. The binscatter is cleaner, but a linear fit looks unreasonable.

Try a quadratic fit.

. binscatter wage tenure, line(qfit)

We can also plot a linear regression discontinuity.

. binscatter wage tenure, rd(2.5)

What is the relationship between age and wages?

. scatter wage age

. binscatter wage age

The binscatter is again much easier to interpret. (Note that binscatter automatically
used each age as a discrete bin, since there are fewer than 20 unique values.)

How does the relationship vary by race?

. binscatter wage age, by(race)

The relationship between age and wages is very different for whites and blacks. But what if we control for occupation?

. binscatter wage age, by(race) absorb(occupation)

A very different picture emerges. Let's label this graph nicely.

. binscatter wage age, by(race) absorb(occupation) msymbols(O T) xtitle(Age) ytitle(Hourly Wage) legend(lab(1 White) lab(2 Black))

Saved Results

binscatter saves the following in e():

Scalars

e(N) number of observations

Macros

e(graphcmd) twoway command used to generate graph, which does not depend on loaded data

Note: it is often important to reference this result using `"`e(graphcmd)'"'
rather than e(graphcmd) in order to avoid truncation due to Stata's character limit for strings.

Matrices

e(byvalues) ordered list of by-values (if numeric by-variable specified)

e(rdintervals) ordered list of rd intervals (if rd specified)

e(y#_coefs) fit line coefficients for #th y-variable (if lfit or qfit specified)

Functions

e(sample) marks sample

Author

Michael Stepner

stepner@mit.edu

Acknowledgements

The present version of binscatter is based on a program first written by Jessica Laird.

This program was developed under the guidance and direction of Raj Chetty and John
Friedman. Laszlo Sandor provided suggestions which improved the program considerably, and offered abundant help
testing it.

Thank you also to the users of early versions of the program who devoted time to reporting
the bugs that they encountered.