RainbowStats · Panel econometrics
TWFE
Estimate a policy effect after removing unit differences and shared date shocks. See exactly where negative treatment weights enter.
One outcome, many units
model=TWFE(outcomes,date1,date2,...)
model=TWFE(outcomes,date1,date2,...,ARG(START_DATE,yyyymmdd),ARG(END_DATE,yyyymmdd))
TWO_WAY_FIXED_EFFECTS is an alias. Each series measures the same outcome for a different unit. This is not the usual regression LIST of Y followed by X variables.
| Input | Meaning |
|---|---|
| outcomes | A LIST/DataList of at least two states, countries, firms or other units, with matching frequency and outcome units. |
| date1,date2,… | Exactly one YYYYMMDD adoption marker for each series, in LIST order. Use 0 for never treated. Treatment is absorbing. |
| START_DATE / END_DATE | Optional inclusive sample limits. Supply ARG options after all adoption dates. Trimming changes the treatment weights. |
The model uses exact timestamp intersection, drops an entire common date if any outcome is nonfinite, and estimates a balanced panel. It never interpolates or changes frequency. Maximum: 1,000,000 retained unit-date cells.
The first retained observation on or after adoption is treated. With monthly observations dated July 1, a July 8 policy first treats August 1. Inspect EXTRACT(model,adoption).
Example
Model=TWFE()
slide(Model)
Positive effects, negative coefficient
This built-in, deterministic example needs no external data. Thirty units are observed over the twelve months of 2020. Twenty-four adopt in March and six in November. Untreated outcomes consist only of persistent unit differences and a shared date path.
Early adopters' cohort mean gain is +1 through October, then +20 in November and December. Late adopters' mean gain is +1. Small centered unit deviations preserve these means and keep all treatment effects positive. Nevertheless, negative TWFE weights on the early adopters' late gains produce a negative coefficient.
Heterogeneous=TWFE()
Constant=TWFE(CONSTANT_DEMO)
HeterogeneousSummary=EXTRACT(Heterogeneous,summary)
ConstantSummary=EXTRACT(Constant,summary)
Weights=EXTRACT(Heterogeneous,weight_chart)
slide(HeterogeneousSummary,ConstantSummary,Weights)
The constant-effect version has the same dates and weights, but every treated observation gains +2. TWFE recovers +2. Negative weights are a vulnerability whose consequences depend on effect heterogeneity. The slideshow explains the known effects and their true equal-cell average. Synthetic clustered intervals are illustrative; this deterministic construction is not an inference-coverage experiment.
Real-data example: adult-use retail sales and unemployment
Outcomes=LIST(COUR,WAUR,ORUR,UTUR,IDUR,WYUR)
Policy=TWFE(Outcomes,20140101,20140708,20151001,0,0,0,ARG(START_DATE,20100101),ARG(END_DATE,20191231))
slide(Policy)
FRED state unemployment rates: Colorado, Washington, Oregon, Utah, Idaho and Wyoming, in that order. The markers represent licensed adult-use retail sales, not medical access or enactment dates. Colorado begins January 2014; Washington July 8, 2014; Oregon October 2015. The comparison states remain untreated for this treatment through 2019.
The coefficient is in unemployment-rate percentage points. This is a demonstration of specification and diagnostics, not evidence that retail legalization caused unemployment changes. State-specific shocks, differing trends, spillovers and policy selection can undermine the design. Six unit clusters give weak conventional inference; inspect the paths and weights before interpreting a result. The end date excludes COVID.
EXTRACT(Policy,adoption)
EXTRACT(Policy,weight_summary)
EXTRACT(Policy,cohort_weights)
EXTRACT(Policy,diagnostics)What is estimated?
Y_it = intercept + unitFE_i + dateFE_t + beta D_it + error_it
D_tilde = D - unit_mean(D) - date_mean(D) + grand_mean(D)
beta = sum(D_tilde * Y_tilde) / sum(D_tilde^2)
Every retained unit-date observation has equal weight. A state is not population-weighted. Unit and date effects each use sum-to-zero normalization. Fitted outcomes include both sets of fixed effects. Within R-squared compares the residual sum of squares with the outcome variation remaining after both sets of fixed effects.
Treatment must retain variation after both fixed effects. All units adopting together, no treatment, or a treatment indicator constant within each unit cannot identify beta; those designs return an explanatory error.
Why inspect negative weights?
w_it = D_it * D_tilde_it / sum(D_tilde^2)
Under parallel untreated trends and no anticipation, the static TWFE estimand is a weighted sum of treated-cell effects. The weights sum to one, but may be negative. A negative weight is a coefficient-design property, not proof that the policy effect is negative.
weights returns these treatment-effect weights, with zero on untreated cells. linear_weights returns D_tilde/sum(D_tilde²) for all cells; multiplying those weights by outcomes and summing reproduces beta. These are different quantities.
negative_weight_mass is the absolute sum of negative weights. negative_weight_share is negative mass divided by total absolute mass. negative_weight_fraction is the fraction of treated cells receiving negative weights, including zero-weight treated cells in the denominator. The two shares return fractions between 0 and 1, not percentages.
The weight chart aggregates positive, negative and net treated-cell weights by calendar date. Its sum across all dates is one for the net line. Cohort weights use the supplied policy-date markers. Neither is a Goodman-Bacon decomposition or a cohort ATT estimate.
Extractable results
| Extract | Return and meaning |
|---|---|
| slideshow | Educational D3 presentation; also the default model display/JSON. |
| summary, coefficients, diagnostics | HTML panels with effect, inference, sample and design limits. |
| adoption, weight_summary, cohort_weights, unit_effects | HTML tables; unit indices are 1-based and match input LIST order. |
| effect or beta; se; p_value; ci | Coefficient, unit-cluster CR1 SE, t(G−1) p-value, and 1×2 matrix [95% lower, upper]. No ATT alias is supplied. |
| coefficient_data | 1×6 numeric matrix: beta, SE, t, p-value, lower, upper. |
| actual, fitted, residuals, treatment | DataLists in input order. Treatment contains binary 0/1 paths. |
| weights, linear_weights, residualized_treatment, residualized_outcome | DataLists in input order, with exactly the retained common dates. |
| outcome_chart, weight_chart, fit_chart | Editable D3 line charts: raw cohort means, treatment weights and in-sample actual/fitted for the first unit. |
| time_effects | Series of centered date fixed effects, in outcome units. |
| panel_data | Numeric matrix in unit-major order: unit_index, YYYYMMDD date, outcome, treatment, fitted, residual, residualized treatment, treatment-effect weight. |
| within_r_squared, nobs, cluster_count | Numeric fit and sample summaries. |
| negative_weight_mass, negative_weight_share, negative_weight_fraction | Numeric negative-weight diagnostics defined above. |
| json | Structured JSON text containing metadata, labeled panel columns and all retained unit-date rows. Nonfinite summary values become null. |
Chart=EXTRACT(Policy,weight_chart)
Chart=SET_TITLE(Chart,TWFE_Treatment_Weights)
Chart=SET_YAXIS(Chart,Normalized_Weight)
ChartInference and interpretation
Unit-clustered sandwich scores allow arbitrary within-unit serial correlation and heteroskedasticity. Units are assumed independent. CR1 multiplies the variance by G/(G−1) × (N−1)/(N−K), with full model rank K=G+T. Intervals and p-values use Student t with G−1 df. Software that omits nested unit fixed effects from K can report a different small-sample correction.
Few clusters, concentrated treatment variation and shared shocks can make conventional inference unreliable. The slideshow reports treatment-information concentration. t critical values do not repair a poor design. Near-zero estimated variance suppresses t and p rather than inventing significance.
For causal interpretation, justify parallel untreated trends, no anticipation, no spillovers and the comparison group. A raw cohort chart is descriptive; this static model does not estimate event-study leads/lags or perform a formal parallel-trends test.
This implementation covers one absorbing binary treatment on a balanced, equally weighted panel. It does not include additional covariates, population weights, continuous or reversible treatments, unbalanced-panel fitting, modern cohort-time ATT, imputation DiD, two-way clustering or a wild-cluster bootstrap.
DID handles a common-date two-group contrast. SSC uses a distinct synthetic-control approach for staggered adoption. Comparing methods is useful; agreement does not establish causal identification.
References
de Chaisemartin and D’Haultfœuille (2020), Two-way Fixed Effects Estimators with Heterogeneous Treatment Effects. Hull (October 2026), How Different are Different Differences-in-Differences? A Large-Scale Meta-Analysis. The built-in example illustrates the weighting mechanism; it does not reproduce Hull’s meta-analysis.
Available extracts
Save the command result in a variable, then use EXTRACT(result, "key"). Availability can depend on the input form and fitted model.
slideshowsummarycoefficientscoefficient_datadiagnosticsadoptionweight_summarycohort_weightsweightslinear_weightsweight_chartoutcome_chartfit_chartfittedresidualsactualtreatmentresidualized_treatmentresidualized_outcomeunit_effectstime_effectspanel_dataeffectbetasecip_valuewithin_r_squarednobscluster_countnegative_weight_massnegative_weight_sharenegative_weight_fractionjson
In indexed patterns such as beta:n, replace n with the required index.