Difference-in-Differences Without Stata: The Minimum Wage Study

New Jersey raised its minimum wage and Pennsylvania didn't. We open Card & Krueger's 410 restaurants as a Stata file, reshape to one row per store, cluster the standard errors by store and match the published tables. No code.

Data: Card & Krueger (1994), American Economic Review 84(4): 410 fast-food restaurants in New Jersey and Pennsylvania, surveyed in February and November 1992. We use CK1994.dta (Stata release 118, 820 rows, long format) from the data archive of Bruce Hansen's Econometrics: download "Econometrics Data.zip" and open CK1994/CK1994.dta. The original flat file is njmin.zip on David Card's data page. Every number below was computed from that file and checked against the paper's tables.

On April 1, 1992, New Jersey raised its minimum wage from $4.25 to $5.05 an hour. Pennsylvania, next door, didn't. David Card and Alan Krueger surveyed 410 fast-food restaurants on both sides of the border before and after, and counted the staff. The textbook model says New Jersey should have lost jobs. Whether it did depends on a number nobody can observe: what New Jersey employment would have done without the law.

Slopegraph of mean FTE employment per store: Pennsylvania falls from 23.33 to 21.17, New Jersey rises from 20.44 to 21.03, with a dashed counterfactual line
Mean FTE per store, February → November 1992. The dashed line is New Jersey on Pennsylvania's trend. The gap at November is the difference-in-differences, +2.75.

Difference-in-differences is the standard answer. Below, we open the data as a Stata file, reshape it to one row per store, and estimate the effect two ways in Stratum: a regression with standard errors clustered by store, and a within-store Welch test on the change scores. They agree, and each is checked against the paper's tables.

Step 1 — Open the .dta and define the outcome

File ▸ Import File… opens CK1994.dta directly, with no wizard and no conversion: 820 rows and 26 columns. It's long, with one row per store per survey wave and time coded 0/1. The file has no value labels, so Data ▸ Column Definition… ▸ Values adds them (0 = Pennsylvania, 1 = New Jersey; 0 = Feb 1992, 1 = Nov 1992; the four chain names). That's label define in a grid.

The Card and Krueger Stata file open in Stratum, long format with value labels
The .dta opened directly: 410 stores × 2 waves, with codes now reading as labels.

The outcome is full-time-equivalent employment as the paper defines it, built with one Add Column formula: [empft] + [nmgrs] + 0.5 * [emppt]. Stores that skipped any of the three counts come out missing (26 of 820 rows), and every analysis skips them.

Step 2 — Did the law actually bite?

A minimum wage nobody was paying below isn't a treatment. Box-plot the starting wage by wave, with the two states side by side. In February the states are twins: both medians are $4.50, and about a third of stores in each pay exactly $4.25. By November 89% of New Jersey stores pay exactly $5.05, and the New Jersey box has no height at all. Pennsylvania hasn't moved.

Box plots of starting wage by survey wave and state; New Jersey collapses onto 5.05 dollars in November
The policy bite. February: two identical distributions. November: New Jersey stacked on the new minimum, Pennsylvania unchanged.

Step 3 — Reshape wide, and the naive before-and-after

To compare each store with itself, February and November have to sit on the same row. A Stata user would reshape wide. In Stratum it's Data ▸ Split Columns… with Parameters FTE and wage_st, Split by time, and One row per: store. Constant columns (state, chain, ownership) are carried along once, and a new dataset, CK1994 – wide, opens with 410 rows and columns named FTE [time=Feb 1992], FTE [time=Nov 1992] and so on.

The wide dataset after Split Columns: one row per store with February and November FTE columns
reshape wide without code: 820 long rows become 410 stores, one row each.

Then one formula column, FTE_change. Insert Column or Function drops each split column into the formula with its ] escaped, so nobody types it: the field reads [FTE [time=Nov 1992]]] - [FTE [time=Feb 1992]]].

Filter to New Jersey and run a paired t-test on the February and November FTE columns, with Swap I / II so the difference reads November − February: across 309 stores, mean FTE went from 20.43 to 20.90, a change of +0.47, p = 0.33. Nothing detectable. That doesn't mean the law did nothing. A before-and-after in one state credits the law with everything else that happened in 1992, so it can't separate the policy from the calendar.

Step 4 — Pennsylvania as the control

Pennsylvania went through the same months, the same regional economy and the same chains without the increase, so its change is the counterfactual. Back in the long file, a Slopegraph in long mode (Entity = state, Stage = time, Value = FTE, Statistic = Mean) puts the four numbers on one chart:

Mean FTE per storeFeb 1992Nov 1992Change
Pennsylvania23.3321.17−2.17
New Jersey20.4421.03+0.59
Difference-in-differences+2.75

All four means match the paper's Table 3 to two decimals. The DiD is 0.59 − (−2.17) ≈ 2.75 FTE per store, about 13% of a New Jersey store's February staff.

2.75 or 2.76? The paper prints 2.76. Computed exactly from the public file, the DiD is 2.754. Every cell mean matches, and the paper's −2.16 for Pennsylvania is the difference of the rounded means (the exact value is −2.166). We can't trace the last digit further, and at 0.006 workers it changes nothing.

Step 5 — The DiD regression, clustered by store

The econometrics route is a regression on the long file: Analyze ▸ Linear Regression with response FTE, categorical predictors state and time, and Add Formula… [state]*[time] for the New Jersey × November cell. Its coefficient is the DiD, 2.754. With classical standard errors it looks weak: SE 1.69, p = 0.103 (the same test as a two-way ANOVA interaction, F = 2.66). But the 794 rows are 410 stores, nearly all measured twice, and a big store is big in both waves.

Set Standard Errors = Clustered, Cluster: store. Stratum applies Stata's small-sample factor, so this is vce(cluster store) digit for digit:

New Jersey × November (n = 794)EstimateSEp95% CI
Classical2.751.690.103[−0.56, 6.07]
Clustered by store (410 clusters)2.751.310.036[0.19, 5.32]
Linear regression of FTE on state, time and their product with cluster-robust standard errors by store
Same estimate, honest noise: cluster-robust standard errors (store, 410 clusters).

Step 6 — The within-store Welch test, and the agreement

The paper took the other road: difference within the store first. In the wide file, run Two-Sample on FTE_change, grouped by state. Only the 384 stores with employment in both waves have a change. Pennsylvania (code 0) is Group I, so Swap I / II makes the difference read New Jersey − Pennsylvania. Welch is the default, and the right one here: Pennsylvania's changes are more dispersed and Levene rejects equal variances (p ≈ 0.02).

Change in FTE, NJ − PAStratumPaper (Table 3, row 4)
Difference+2.752.75
Standard error1.34(1.34)
Welch t, p2.05, 0.043—
95% CI[0.09, 5.41]—
Cohen's d0.31—
Welch two-sample test of the within-store change in FTE, New Jersey versus Pennsylvania
Table 3, row 4, reproduced: 2.75 with a standard error of 1.34.

Two roads, one answer: 1.31 clustered, 1.34 from the change scores. The gap is the 26 stores seen in only one wave, which the regression can use and a change score can't; on the 384 balanced stores the clustered SE is 1.339 against Welch's 1.342.

Step 7 — Regression: matching Table 4

Table 4 regresses the change in FTE on a New Jersey dummy, then adds chain and company-ownership controls, on 357 stores (employment in both waves, a starting wage in both waves or closed). In the wide file, one formula column built from and(), or() and ismissing() defines that sample, and a Keep filter applies it. Then Analyze ▸ Linear Regression:

Model (Y = change in FTE)New Jersey (SE)Paper
(i) state only2.33 (1.19)2.33 (1.19)
(ii) + chain + company-owned2.30 (1.20)2.30 (1.20)
Linear regression of the change in FTE on state, chain and ownership, reproducing Table 4 of the paper
Table 4, column (ii), digit for digit: New Jersey 2.30 (1.20), SER 8.78, N = 357.

The paper's standard errors are classical. Set Standard Errors = Robust (HC1), Stata's , robust, and column (ii) reads 2.30 (1.45), p = 0.11: the same estimate, a standard error about a fifth wider, reflecting the unequal spread Levene flagged.

The specification test. New Jersey stores already paying $5.00 or more in February were barely touched by the law. Among them, FTE changed by −2.16, close to Pennsylvania's −2.28. Stores that started at $4.25 gained 1.21. So the unaffected stores in the treated state behave like the control state.

What the data show, and what they don't

This sample shows no sign of the employment loss the simple competitive model predicts: New Jersey held steady while the control state shed about two workers per store. It doesn't establish a universal law. The estimate is about two standard errors from zero, with a confidence interval from barely positive to about five workers, and robust standard errors make the Table 4 version less certain still. The result is also one of the most argued-over in economics: Neumark and Wascher (2000) re-examined it with payroll records and found negative effects, and Card and Krueger (2000) replied with BLS establishment data showing no meaningful loss. The design is the reason the argument is worth having.

The data are free and so is the trial. Download Stratum, open CK1994.dta, and see whether you can make the estimate move. You'll find Stratum also opens SPSS files the same way.
case-study difference-in-differences econometrics two-way-anova regression