Skip to content

Commit 905bf51

Browse files
committed
Prepare for 1.0.0 release
- Update version to 1.0.0 in __init__.py and pyproject.toml - Change development status classifier to Production/Stable - Add warning when SyntheticDiD bootstrap has >5% failure rate - Add troubleshooting guide to documentation - Document standard error computation differences across estimators - Update CHANGELOG with 1.0.0 release notes
1 parent 6bda204 commit 905bf51

7 files changed

Lines changed: 419 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,37 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.0.0] - 2026-01-04
9+
10+
### Added
11+
- **Goodman-Bacon decomposition** for TWFE diagnostics
12+
- `BaconDecomposition` class for decomposing TWFE into weighted 2x2 comparisons
13+
- `Comparison2x2` dataclass for individual comparisons (treated_vs_never, earlier_vs_later, later_vs_earlier)
14+
- `BaconDecompositionResults` with weights and estimates by comparison type
15+
- `bacon_decompose()` convenience function
16+
- `plot_bacon()` visualization for decomposition results
17+
- Integration via `TwoWayFixedEffects.decompose()` method
18+
- **Power analysis** for study design
19+
- `PowerAnalysis` class for analytical power calculations
20+
- `PowerResults` and `SimulationPowerResults` dataclasses
21+
- `compute_mde()`, `compute_power()`, `compute_sample_size()` convenience functions
22+
- `simulate_power()` for Monte Carlo simulation-based power analysis
23+
- `plot_power_curve()` visualization for power analysis
24+
- Tutorial notebook: `docs/tutorials/06_power_analysis.ipynb`
25+
- **Callaway-Sant'Anna multiplier bootstrap** for inference
26+
- `CSBootstrapResults` with standard errors, confidence intervals, p-values
27+
- Rademacher, Mammen, and Webb weight distributions
28+
- Bootstrap inference for all aggregation methods
29+
- **Troubleshooting guide** in documentation
30+
- **Standard error computation guide** explaining SE differences across estimators
31+
32+
### Changed
33+
- Updated package status to Production/Stable (was Alpha)
34+
- SyntheticDiD bootstrap now warns when >5% of iterations fail
35+
36+
### Fixed
37+
- Silent bootstrap failures in SyntheticDiD now produce warnings
38+
839
## [0.6.0]
940

1041
### Added
@@ -136,6 +167,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
136167
- `to_dict()` and `to_dataframe()` export methods
137168
- `is_significant` and `significance_stars` properties
138169

170+
[1.0.0]: https://github.com/igerber/diff-diff/compare/v0.6.0...v1.0.0
139171
[0.6.0]: https://github.com/igerber/diff-diff/compare/v0.5.0...v0.6.0
140172
[0.5.0]: https://github.com/igerber/diff-diff/compare/v0.4.0...v0.5.0
141173
[0.4.0]: https://github.com/igerber/diff-diff/compare/v0.3.0...v0.4.0

diff_diff/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
plot_sensitivity,
8686
)
8787

88-
__version__ = "0.9.0"
88+
__version__ = "1.0.0"
8989
__all__ = [
9090
# Estimators
9191
"DifferenceInDifferences",

diff_diff/estimators.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,6 +1774,20 @@ def _bootstrap_se(
17741774
continue
17751775

17761776
bootstrap_estimates = np.array(bootstrap_estimates)
1777+
1778+
# Warn if too many bootstrap iterations failed
1779+
n_successful = len(bootstrap_estimates)
1780+
failure_rate = 1 - (n_successful / self.n_bootstrap)
1781+
if failure_rate > 0.05:
1782+
warnings.warn(
1783+
f"Only {n_successful}/{self.n_bootstrap} bootstrap iterations succeeded "
1784+
f"({failure_rate:.1%} failure rate). Standard errors may be unreliable. "
1785+
f"This can occur with small samples, near-singular weight matrices, "
1786+
f"or insufficient pre-treatment periods.",
1787+
UserWarning,
1788+
stacklevel=2,
1789+
)
1790+
17771791
se = np.std(bootstrap_estimates, ddof=1) if len(bootstrap_estimates) > 1 else 0.0
17781792

17791793
return se, bootstrap_estimates

docs/choosing_estimator.rst

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,57 @@ Common Pitfalls
205205

206206
*Solution*: Always specify ``cluster_col`` for panel data.
207207

208+
Standard Error Methods
209+
----------------------
210+
211+
Different estimators compute standard errors differently. Understanding these
212+
differences helps interpret results and choose appropriate inference.
213+
214+
.. list-table::
215+
:header-rows: 1
216+
:widths: 20 25 55
217+
218+
* - Estimator
219+
- Default SE Method
220+
- Details
221+
* - ``DifferenceInDifferences``
222+
- HC1 (heteroskedasticity-robust)
223+
- Uses White's robust SEs by default. Specify ``cluster_col`` for cluster-robust SEs. Use ``inference='wild_bootstrap'`` for few clusters (<30).
224+
* - ``TwoWayFixedEffects``
225+
- Cluster-robust (unit level)
226+
- Always clusters at unit level after within-transformation. Specify ``cluster_col`` to override. Use ``inference='wild_bootstrap'`` for few clusters.
227+
* - ``MultiPeriodDiD``
228+
- HC1 (heteroskedasticity-robust)
229+
- Same as basic DiD. Cluster-robust available via ``cluster_col``. Wild bootstrap not yet supported for multi-coefficient inference.
230+
* - ``CallawaySantAnna``
231+
- Analytical (simple difference)
232+
- Uses simple variance of group-time means. Use ``bootstrap()`` method for multiplier bootstrap inference with proper SEs, CIs, and p-values.
233+
* - ``SyntheticDiD``
234+
- Bootstrap or placebo-based
235+
- Default uses bootstrap resampling. Set ``n_bootstrap=0`` for placebo-based inference using pre-treatment residuals.
236+
237+
**Recommendations by sample size:**
238+
239+
- **Large samples (N > 1000, clusters > 50)**: Default analytical SEs are reliable
240+
- **Medium samples (clusters 30-50)**: Cluster-robust SEs recommended
241+
- **Small samples (clusters < 30)**: Use wild cluster bootstrap (``inference='wild_bootstrap'``)
242+
- **Very few clusters (< 10)**: Use Webb 6-point distribution (``weight_type='webb'``)
243+
244+
**Common pitfall:** Forgetting to cluster when units are observed multiple times.
245+
For panel data, always cluster at the unit level unless you have a strong reason not to.
246+
247+
.. code-block:: python
248+
249+
# Good: Cluster at unit level for panel data
250+
did = DifferenceInDifferences()
251+
results = did.fit(data, outcome='y', treated='treated',
252+
post='post', cluster_col='unit_id')
253+
254+
# Better for few clusters: Wild bootstrap
255+
did = DifferenceInDifferences(inference='wild_bootstrap')
256+
results = did.fit(data, outcome='y', treated='treated',
257+
post='post', cluster_col='state')
258+
208259
When in Doubt
209260
-------------
210261

docs/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Quick Links
4040

4141
- :doc:`quickstart` - Get started with basic examples
4242
- :doc:`choosing_estimator` - Which estimator should I use?
43+
- :doc:`troubleshooting` - Common issues and solutions
4344
- :doc:`r_comparison` - Comparison with R packages
4445
- :doc:`python_comparison` - Comparison with Python packages
4546
- :doc:`api/index` - Full API reference
@@ -51,6 +52,7 @@ Quick Links
5152

5253
quickstart
5354
choosing_estimator
55+
troubleshooting
5456
r_comparison
5557
python_comparison
5658

0 commit comments

Comments
 (0)