Skip to content

Commit 264af12

Browse files
igerberclaude
andcommitted
Fix doc snippet bugs: wrong params, data-shape mismatches, and harden test
- Fix Bacon wording: "negative weights" → accurate Goodman-Bacon (2021) language - Fix first_treat column name in Bacon examples (2 files) - Fix 15 doc snippet bugs: wrong reference_period, column names, return types, and estimator-data mismatches (SyntheticDiD, wild bootstrap, diagnostics) - Harden test_doc_snippets: fail on all exceptions except NameError - Add mock dataset loaders so dataset page snippets execute without network - Add dose/exposure columns to test namespace for troubleshooting snippets Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5da7d86 commit 264af12

8 files changed

Lines changed: 187 additions & 62 deletions

File tree

docs/api/diagnostics.rst

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,15 @@ Example
7676

7777
.. code-block:: python
7878
79-
from diff_diff import permutation_test
79+
from diff_diff import permutation_test, generate_did_data
8080
81+
panel = generate_did_data(n_units=100, n_periods=10, treatment_effect=2.0)
8182
result = permutation_test(
82-
data,
83-
outcome='y',
83+
panel,
84+
outcome='outcome',
8485
treatment='treated',
85-
time='period',
86-
unit='unit_id',
86+
time='post',
87+
unit='unit',
8788
n_permutations=1000
8889
)
8990
@@ -101,18 +102,20 @@ Example
101102

102103
.. code-block:: python
103104
104-
from diff_diff import leave_one_out_test
105+
from diff_diff import leave_one_out_test, generate_did_data
105106
107+
panel = generate_did_data(n_units=100, n_periods=10, treatment_effect=2.0)
106108
result = leave_one_out_test(
107-
data,
108-
outcome='y',
109+
panel,
110+
outcome='outcome',
109111
treatment='treated',
110-
time='period',
111-
unit='unit_id'
112+
time='post',
113+
unit='unit'
112114
)
113115
114116
# Check if results are driven by single units
115-
print(f"Effect range: [{result.min_effect:.3f}, {result.max_effect:.3f}]")
117+
loo = result.leave_one_out_effects
118+
print(f"Effect range: [{min(loo.values()):.3f}, {max(loo.values()):.3f}]")
116119
117120
run_all_placebo_tests
118121
---------------------

docs/api/prep.rst

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ Example
137137
138138
from diff_diff import make_post_indicator
139139
140-
data['post'] = make_post_indicator(
140+
data = make_post_indicator(
141141
data,
142142
time_column='period',
143143
treatment_start=5
@@ -267,17 +267,19 @@ Example
267267
268268
from diff_diff import validate_did_data
269269
270-
is_valid, issues = validate_did_data(
270+
result = validate_did_data(
271271
data,
272272
outcome='outcome',
273273
treatment='treated',
274274
time='period',
275275
unit='unit_id'
276276
)
277277
278-
if not is_valid:
279-
for issue in issues:
280-
print(f"Issue: {issue}")
278+
if not result['valid']:
279+
for error in result['errors']:
280+
print(f"Error: {error}")
281+
for warning in result['warnings']:
282+
print(f"Warning: {warning}")
281283
282284
summarize_did_data
283285
~~~~~~~~~~~~~~~~~~
@@ -301,9 +303,7 @@ Example
301303
unit='unit_id'
302304
)
303305
304-
print(f"N units: {summary['n_units']}")
305-
print(f"N periods: {summary['n_periods']}")
306-
print(f"Treatment fraction: {summary['treatment_fraction']:.1%}")
306+
print(summary)
307307
308308
Control Unit Selection
309309
----------------------
@@ -320,16 +320,17 @@ Example
320320

321321
.. code-block:: python
322322
323-
from diff_diff import rank_control_units
323+
from diff_diff import rank_control_units, generate_did_data
324324
325+
panel = generate_did_data(n_units=100, n_periods=10, treatment_effect=2.0)
325326
ranked = rank_control_units(
326-
data,
327-
unit_column='unit_id',
327+
panel,
328+
unit_column='unit',
328329
time_column='period',
329330
outcome_column='outcome',
330331
treatment_column='treated',
331-
pre_periods=[0, 1, 2, 3]
332+
pre_periods=[0, 1, 2, 3, 4]
332333
)
333334
334335
# Select top 10 control units
335-
best_controls = ranked.head(10)['unit_id'].tolist()
336+
best_controls = ranked.head(10)['unit'].tolist()

docs/api/utils.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Example
3131
pre_periods=[0, 1, 2, 3]
3232
)
3333
34-
print(f"F-statistic: {result['f_stat']:.3f}")
34+
print(f"t-statistic: {result['t_statistic']:.3f}")
3535
print(f"p-value: {result['p_value']:.3f}")
3636
3737
if result['p_value'] > 0.05:
@@ -87,12 +87,14 @@ Example
8787

8888
.. code-block:: python
8989
90-
from diff_diff import DifferenceInDifferences
90+
from diff_diff import DifferenceInDifferences, generate_did_data
91+
92+
panel = generate_did_data(n_units=200, n_periods=10, treatment_effect=2.0)
9193
9294
# Use wild bootstrap via the estimator's inference parameter (recommended)
9395
did = DifferenceInDifferences(inference='wild_bootstrap', n_bootstrap=999,
94-
cluster='unit_id')
95-
results = did.fit(data, outcome='y', treatment='treated',
96+
cluster='unit')
97+
results = did.fit(panel, outcome='outcome', treatment='treated',
9698
time='post')
9799
98100
print(f"Bootstrap SE: {results.se:.3f}")

docs/api/visualization.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Example
2222
# Fit event study model
2323
model = MultiPeriodDiD()
2424
results = model.fit(data, outcome='y', treatment='treated',
25-
time='period', unit='unit_id', reference_period=-1)
25+
time='period', unit='unit_id', reference_period=2)
2626
2727
# Create plot
2828
fig = plot_event_study(results)

docs/choosing_estimator.rst

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ Use :class:`~diff_diff.MultiPeriodDiD` when:
157157
158158
event = MultiPeriodDiD()
159159
results = event.fit(data, outcome='y', treatment='treated',
160-
time='period', unit='unit_id', reference_period=-1)
160+
time='period', unit='unit_id', reference_period=2)
161161
162162
# Visualize
163163
plot_event_study(results)
@@ -205,10 +205,12 @@ Use :class:`~diff_diff.SyntheticDiD` when:
205205

206206
.. code-block:: python
207207
208-
from diff_diff import SyntheticDiD
208+
from diff_diff import SyntheticDiD, generate_did_data
209209
210+
# SyntheticDiD requires block treatment (constant within units)
211+
block_data = generate_did_data(n_units=40, n_periods=10, treatment_effect=2.0)
210212
sdid = SyntheticDiD()
211-
results = sdid.fit(data, outcome='y', unit='unit_id',
213+
results = sdid.fit(block_data, outcome='outcome', unit='unit',
212214
time='period', treatment='treated')
213215
214216
# View the unit weights
@@ -412,7 +414,7 @@ Use :class:`~diff_diff.BaconDecomposition` when:
412414

413415
- You want to **diagnose** whether TWFE is biased in your staggered setting
414416
- You need to see which 2x2 comparisons drive the TWFE estimate
415-
- You want to check for negative weights from forbidden comparisons
417+
- You want to check whether later-vs-earlier or already-treated-as-control comparisons carry substantial weight
416418

417419
Goodman-Bacon (2021) decomposes the TWFE estimate into a weighted average of
418420
all 2x2 DiD comparisons and their weights.
@@ -423,7 +425,7 @@ all 2x2 DiD comparisons and their weights.
423425
424426
bacon = BaconDecomposition()
425427
results = bacon.fit(data, outcome='y', unit='unit_id',
426-
time='period', first_treat='treated')
428+
time='period', first_treat='first_treat')
427429
results.print_summary()
428430
429431
# Visualize the decomposition
@@ -535,14 +537,18 @@ For panel data, always cluster at the unit level unless you have a strong reason
535537

536538
.. code-block:: python
537539
540+
from diff_diff import generate_did_data
541+
542+
panel = generate_did_data(n_units=200, n_periods=10, treatment_effect=2.0)
543+
538544
# Good: Cluster at unit level for panel data
539-
did = DifferenceInDifferences(cluster='unit_id')
540-
results = did.fit(data, outcome='y', treatment='treated',
545+
did = DifferenceInDifferences(cluster='unit')
546+
results = did.fit(panel, outcome='outcome', treatment='treated',
541547
time='post')
542548
543549
# Better for few clusters: Wild bootstrap
544-
did = DifferenceInDifferences(inference='wild_bootstrap', cluster='state')
545-
results = did.fit(data, outcome='y', treatment='treated',
550+
did = DifferenceInDifferences(inference='wild_bootstrap', cluster='unit')
551+
results = did.fit(panel, outcome='outcome', treatment='treated',
546552
time='post')
547553
548554
When in Doubt

docs/quickstart.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ Examine treatment effects over time:
131131
outcome='outcome',
132132
treatment='treated',
133133
time='period',
134-
post_periods=[5, 6, 7, 8, 9],
134+
post_periods=[5, 6, 7],
135135
reference_period=4
136136
)
137137
@@ -154,7 +154,7 @@ When treatment is adopted at different times across units:
154154
outcome='outcome',
155155
unit='unit_id',
156156
time='period',
157-
first_treat='first_treatment_period'
157+
first_treat='first_treat'
158158
)
159159
160160
# View aggregated treatment effect

docs/troubleshooting.rst

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -363,11 +363,11 @@ during leave-one-out cross-validation (LOOCV).
363363
pre_periods = data.loc[data['post'] == 0, 'period'].nunique()
364364
print(f"Pre-treatment periods: {pre_periods}") # Must be >= 2; stability improves with >= 4
365365
366-
# If TROP cannot find valid parameters, try SyntheticDiD as a fallback
367-
from diff_diff import SyntheticDiD
368-
sdid = SyntheticDiD()
369-
results = sdid.fit(data, outcome='y', treatment='treatment',
370-
unit='unit_id', time='period')
366+
# If TROP cannot find valid parameters, try CallawaySantAnna as a fallback
367+
from diff_diff import CallawaySantAnna
368+
cs = CallawaySantAnna()
369+
results = cs.fit(data, outcome='y', unit='unit_id',
370+
time='period', first_treat='first_treat')
371371
372372
"LOOCV fits failed / numerical instability"
373373
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -572,7 +572,7 @@ unbalanced. Bacon decomposition requires a balanced panel.
572572
# Then run decomposition
573573
bacon = BaconDecomposition()
574574
results = bacon.fit(balanced, outcome='y', unit='unit_id',
575-
time='period', first_treat='treatment')
575+
time='period', first_treat='first_treat')
576576
577577
Deprecation Warnings
578578
--------------------
@@ -614,7 +614,7 @@ If you encounter issues not covered here:
614614
615615
data = generate_did_data(n_units=100, n_periods=10, treatment_effect=2.0)
616616
did = DifferenceInDifferences()
617-
results = did.fit(data, outcome='y', treatment='treated', time='post')
617+
results = did.fit(data, outcome='outcome', treatment='treated', time='post')
618618
print(f"True effect: 2.0, Estimated: {results.att:.3f}")
619619
620620
For bugs or feature requests, please open an issue on

0 commit comments

Comments
 (0)