RainbowStats · Command reference

DID: Difference in Differences

Compare how an outcome changed for a treated group with how it changed for a control group. Inspect the paths before treatment, then edit and merge the resulting D3 charts.

One common treatment date Two groups Extractable D3 charts Conditional HAC interval

Quick start

Each list contains outcome series for its group. The date identifies the first treated observation on or after the supplied YYYYMMDD marker.

RainbowStats script
model=DID(treatedList,controlList,20140101);
SLIDESHOW(model);
EXTRACT(model,att);
EXTRACT(model,hac_ci);
chart=EXTRACT(model,event_chart);

DIFF_IN_DIFF and DIFFERENCE_IN_DIFFERENCES are aliases for DID.

What the numbers mean. att is the treated group's post-minus-pre change minus the control group's post-minus-pre change, in the original outcome units. hac_ci is a conditional 95% time-series interval for that same contrast. Neither establishes that treatment caused the change.

How the estimate is built

DID = (Treatedpost − Treatedpre) − (Controlpost − Controlpre) The means use all shared valid observation dates. Units have equal weight within each group; dates have equal weight within their pre or post period.

Before the marker

Compute each group's mean outcome over common pretreatment dates. The average treated-minus-control gap becomes the chart's zero baseline.

After the marker

Compute each group's post-treatment mean. Subtract the two changes. Positive and negative effects retain the outcome's original units.

The operator intersects dates across every supplied series and omits nonfinite observations. It requires at least three shared pre and two shared post observations. All series should measure the same outcome at the same frequency.

Syntax and arguments

Supported forms
model=DID(treatedList,controlList,treatmentDate);
model=DID(treatedList,controlList,treatmentDate,ARG(END_DATE,yyyymmdd));
ArgumentWhat to supply
treatedListA DataList of one or more treated outcome series; for a single state use LIST(stateSeries).
controlListA DataList of one or more comparison series that remain untreated over the analyzed interval.
treatmentDateAn integer date, YYYYMMDD. The first common observation on or after the date is post-treatment.
ARG(END_DATE,date)Optional last calendar date. Useful for stopping before a later shock or before a control unit adopts the policy.
Monthly dates matter. If retail sales begin July 8 and the series records each month on the first day, July 1 is pre-treatment and August 1 is the first post-treatment observation. Check event_data to confirm the split.

Editable charts

The event and regression chart extracts are D3LineChartOperator objects. Use RainbowStats chart commands to change titles and axes or add markers. The event path is the current treated-minus-control gap less its average pretreatment value.

ExtractHorizontal axisBest use
event_chartCalendar datesExamine what was happening around the actual treatment date.
event_time_chartObserved periods relative to treatment; zero is the first treated observationOverlay comparisons with different adoption dates using MERGE_CHARTS.
regression_chartObserved periods relative to treatment; zero is the first treated observationMerged pre and post scatter plots with separate fitted lines.
Customize a chart
chart=EXTRACT(model,event_time_chart);
chart=SET_TITLE(chart,Colorado_Unemployment_Gap);
chart=SET_XAXIS(chart,Observed_Months_Since_Treatment);
chart=SET_YAXIS(chart,Adjusted_Gap_in_Percentage_Points);
chart=ADD_XMARKER(chart,12,"One year after treatment");
chart;

Worked examples

Colorado compared with Utah

Use the FRED monthly unemployment series COUR and UTUR. Colorado's adult-use retail sales began January 1, 2014. The example ends in 2019 to exclude COVID from the comparison.

Single comparison
co=SINCE(COUR,20100101);
ut=SINCE(UTUR,20100101);
model=DID(LIST(co),LIST(ut),20140101,ARG(END_DATE,20191231));
SLIDESHOW(model);
EXTRACT(model,pretrend_slope);
EXTRACT(model,att);
EXTRACT(model,hac_se);
EXTRACT(model,hac_lags);
EXTRACT(model,hac_ci);
Interpret this example cautiously. The Colorado–Utah pretreatment gap rises and falls before the retail-sales marker. The operator's two-by-two contrast is computable, but this pair alone offers weak support for parallel untreated trends. A single straight-line pretrend slope cannot describe that curved path.

Compare two states in event time

Run a second comparison using Washington and Idaho. Washington's retail sales began July 8, 2014. The separate results share a numeric event-time axis even though their calendar adoption dates differ.

Two comparisons and one merged chart
co=SINCE(COUR,20100101);
ut=SINCE(UTUR,20100101);
wa=SINCE(WAUR,20100101);
id=SINCE(IDUR,20100101);
coModel=DID(LIST(co),LIST(ut),20140101,ARG(END_DATE,20191231));
waModel=DID(LIST(wa),LIST(id),20140708,ARG(END_DATE,20191231));
coChart=EXTRACT(coModel,event_time_chart);
waChart=EXTRACT(waModel,event_time_chart);
comparison=MERGE_CHARTS(Comparison,coChart,waChart);
comparison=SET_TITLE(comparison,Adult_Use_Retail_Sales_and_Unemployment);
comparison=SET_XAXIS(comparison,Observed_Monthly_Periods_Since_Treatment);
comparison=SET_YAXIS(comparison,Adjusted_Gap_in_Percentage_Points);
comparison=ADD_XMARKER(comparison,12,"One year after treatment");
comparison;

The current MERGE_CHARTS parser expects a leading name argument (Comparison) before its chart arguments. Merging overlays lines; it does not pool the two estimates.

Extractable results

EXTRACT(model,…)Result
slideshowSummary, event chart, pre/post regression table and merged scatter plot, and design diagnostics.
summaryGroup means, pre/post changes, DiD contrast, and sample counts.
event_chartEditable D3 chart on calendar dates.
event_time_chartEditable D3 chart on numeric periods since treatment.
regression_tablePre and post constant and slope, HAC SE for each, and post-minus-pre changes with HAC SE.
regression_chartEditable merged D3 scatter plot with separate fitted pre and post lines.
pre_scatter, post_scatterIndividual D3 scatter plots with fitted lines; compatible with MERGE_CHARTS.
regression_coefficientsThree-row matrix (pre, post, change): constant, SE, slope, SE.
event_dataMatrix: date, event period, treated mean, control mean, raw gap, adjusted gap.
diagnosticsAssumptions and descriptive pretreatment gap slope.
att or effectNumeric two-by-two DiD estimate in the outcome's units.
hac_seConditional time-series standard error for the shift in the group gap.
hac_lagsAutomatically chosen Newey–West/Bartlett lag count.
hac_ciTwo-column matrix: lower and upper conditional 95% interval bounds.
pretrend_slopeDescriptive linear slope of the pretreatment group gap per observed period.
jsonStructured result for downstream use.

The regressions fit the treated-minus-control gap separately before and after treatment, with the observation index centered at treatment. Thus the intercept difference is the fitted jump at treatment; the slope difference is the trend change per observation. Their HAC standard errors use a joint covariance matrix. With only two post observations, the fitted lines are available but trend standard errors are unavailable. The original att and hac_ci remain mean-shift DID results.

What makes a DiD interpretation credible?

  1. Inspect the full pretreatment path. A small average slope does not rule out large swings or a trend reversal.
  2. Choose controls before looking at the post-treatment result. Ask whether the groups plausibly would have followed parallel untreated trends.
  3. Check timing and spillovers. Anticipation, cross-border activity, and treatment of a control unit can undermine the comparison.
  4. Keep the outcome interpretation exact. For COUR and UTUR, a DiD estimate is measured in unemployment-rate percentage points.
Inference boundary. This is a panel with repeated observations over time, not a single cross section. RainbowStats fits the treated-minus-control gap to a constant plus a post-treatment indicator. The post coefficient equals the reported DiD contrast; its HAC standard error uses Newey–West/Bartlett weighting across observed dates and a normal approximation for the 95% interval. This is conditional time-series uncertainty, not a causal confidence interval when parallel untreated trends fail.

Lag count is automatic: the integer part of 4 × (T/100)^(2/9), capped below the shorter pre or post sample length. Read it with EXTRACT(model,hac_lags). The calculation assumes approximately equally spaced observations and a stable mean-shift model for the untreated group gap. It does not account for choosing a control state after inspecting results, anticipation, structural breaks, or a misspecified counterfactual; it reports no p-value. The importance of serial correlation in DiD is documented by Bertrand, Duflo, and Mullainathan. With few treated units, other inference designs can be appropriate; see Conley and Taber.

Staggered adoption is a different design. DID uses one common treatment date for the treated group. Use separate valid comparisons for different adoption dates. RainbowStats SSC addresses staggered adoption with synthetic controls; these are distinct estimators.