diff-diff 3.1.3__tar.gz → 3.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. {diff_diff-3.1.3 → diff_diff-3.2.0}/PKG-INFO +41 -2
  2. {diff_diff-3.1.3 → diff_diff-3.2.0}/README.md +40 -1
  3. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/__init__.py +39 -1
  4. diff_diff-3.2.0/diff_diff/_nprobust_port.py +923 -0
  5. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/bootstrap_utils.py +44 -0
  6. diff_diff-3.2.0/diff_diff/business_report.py +2458 -0
  7. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/chaisemartin_dhaultfoeuille.py +886 -200
  8. diff_diff-3.2.0/diff_diff/chaisemartin_dhaultfoeuille_bootstrap.py +945 -0
  9. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/continuous_did.py +59 -3
  10. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/continuous_did_bspline.py +29 -3
  11. diff_diff-3.2.0/diff_diff/diagnostic_report.py +3219 -0
  12. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/efficient_did.py +52 -29
  13. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/efficient_did_covariates.py +64 -1
  14. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/efficient_did_results.py +6 -0
  15. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/estimators.py +330 -52
  16. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/guides/llms-full.txt +124 -1
  17. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/guides/llms-practitioner.txt +44 -0
  18. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/imputation.py +1 -0
  19. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/imputation_results.py +1 -0
  20. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/linalg.py +779 -75
  21. diff_diff-3.2.0/diff_diff/local_linear.py +783 -0
  22. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/power.py +37 -12
  23. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/practitioner.py +50 -8
  24. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/pretrends.py +34 -6
  25. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/results.py +166 -63
  26. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/stacked_did.py +1 -0
  27. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/stacked_did_results.py +1 -0
  28. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/staggered.py +78 -15
  29. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/staggered_results.py +8 -0
  30. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/staggered_triple_diff.py +80 -6
  31. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/staggered_triple_diff_results.py +5 -0
  32. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/sun_abraham.py +7 -0
  33. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/survey.py +19 -0
  34. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/synthetic_did.py +2 -0
  35. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/trop_global.py +36 -24
  36. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/trop_local.py +32 -25
  37. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/twfe.py +121 -10
  38. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/two_stage.py +17 -0
  39. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/two_stage_bootstrap.py +15 -0
  40. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/two_stage_results.py +1 -0
  41. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/utils.py +28 -4
  42. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/wooldridge.py +23 -3
  43. {diff_diff-3.1.3 → diff_diff-3.2.0}/pyproject.toml +1 -1
  44. {diff_diff-3.1.3 → diff_diff-3.2.0}/rust/Cargo.lock +3 -3
  45. {diff_diff-3.1.3 → diff_diff-3.2.0}/rust/Cargo.toml +1 -1
  46. diff_diff-3.1.3/diff_diff/chaisemartin_dhaultfoeuille_bootstrap.py +0 -590
  47. {diff_diff-3.1.3 → diff_diff-3.2.0}/LICENSE +0 -0
  48. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/_backend.py +0 -0
  49. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/_guides_api.py +0 -0
  50. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/bacon.py +0 -0
  51. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/chaisemartin_dhaultfoeuille_results.py +0 -0
  52. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/continuous_did_results.py +0 -0
  53. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/datasets.py +0 -0
  54. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/diagnostics.py +0 -0
  55. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/efficient_did_bootstrap.py +0 -0
  56. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/efficient_did_weights.py +0 -0
  57. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/guides/__init__.py +0 -0
  58. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/guides/llms.txt +0 -0
  59. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/honest_did.py +0 -0
  60. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/imputation_bootstrap.py +0 -0
  61. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/prep.py +0 -0
  62. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/prep_dgp.py +0 -0
  63. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/staggered_aggregation.py +0 -0
  64. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/staggered_bootstrap.py +0 -0
  65. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/triple_diff.py +0 -0
  66. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/trop.py +0 -0
  67. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/trop_results.py +0 -0
  68. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/visualization/__init__.py +0 -0
  69. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/visualization/_common.py +0 -0
  70. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/visualization/_continuous.py +0 -0
  71. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/visualization/_diagnostic.py +0 -0
  72. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/visualization/_event_study.py +0 -0
  73. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/visualization/_power.py +0 -0
  74. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/visualization/_staggered.py +0 -0
  75. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/visualization/_synthetic.py +0 -0
  76. {diff_diff-3.1.3 → diff_diff-3.2.0}/diff_diff/wooldridge_results.py +0 -0
  77. {diff_diff-3.1.3 → diff_diff-3.2.0}/rust/build.rs +0 -0
  78. {diff_diff-3.1.3 → diff_diff-3.2.0}/rust/src/bootstrap.rs +0 -0
  79. {diff_diff-3.1.3 → diff_diff-3.2.0}/rust/src/lib.rs +0 -0
  80. {diff_diff-3.1.3 → diff_diff-3.2.0}/rust/src/linalg.rs +0 -0
  81. {diff_diff-3.1.3 → diff_diff-3.2.0}/rust/src/trop.rs +0 -0
  82. {diff_diff-3.1.3 → diff_diff-3.2.0}/rust/src/weights.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diff-diff
3
- Version: 3.1.3
3
+ Version: 3.2.0
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: Operating System :: OS Independent
@@ -57,6 +57,7 @@ Project-URL: Repository, https://github.com/igerber/diff-diff
57
57
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
58
58
  [![Downloads](https://img.shields.io/pypi/dm/diff-diff.svg)](https://pypi.org/project/diff-diff/)
59
59
  [![Documentation](https://readthedocs.org/projects/diff-diff/badge/?version=stable)](https://diff-diff.readthedocs.io/en/stable/)
60
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19646175.svg)](https://doi.org/10.5281/zenodo.19646175)
60
61
 
61
62
  A Python library for Difference-in-Differences (DiD) causal inference analysis with an sklearn-like API and statsmodels-style outputs.
62
63
 
@@ -144,6 +145,38 @@ Measuring campaign lift? Evaluating a product launch? diff-diff handles the caus
144
145
  - **[Brand awareness survey tutorial](docs/tutorials/17_brand_awareness_survey.ipynb)** - Full example with complex survey design, brand funnel analysis, and staggered rollouts
145
146
  - **Have BRFSS/ACS/CPS individual records?** Use [`aggregate_survey()`](docs/api/prep.rst) to roll respondent-level microdata into a geographic-period panel with inverse-variance precision weights. The returned second-stage design uses analytic weights (`aweight`), so it works directly with `DifferenceInDifferences`, `TwoWayFixedEffects`, `MultiPeriodDiD`, `SunAbraham`, `ContinuousDiD`, and `EfficientDiD` (estimators marked **Full** in the [survey support matrix](docs/choosing_estimator.rst))
146
147
 
148
+ ### Experimental preview: `BusinessReport` and `DiagnosticReport`
149
+
150
+ diff-diff ships two preview classes, `BusinessReport` and `DiagnosticReport`, that produce plain-English output and a structured `to_dict()` schema from any fitted result. **Both are experimental in this release** — wording, verdict thresholds, and schema shape will change as the library learns from real practitioner usage. Do not anchor downstream tooling on the schema yet; the experimental flag is noted in the CHANGELOG.
151
+
152
+ ```python
153
+ from diff_diff import CallawaySantAnna, BusinessReport
154
+
155
+ cs = CallawaySantAnna(base_period="universal").fit(
156
+ df, outcome="revenue", unit="store", time="month",
157
+ first_treat="first_treat", aggregate="event_study",
158
+ )
159
+ report = BusinessReport(
160
+ cs,
161
+ outcome_label="Revenue per store",
162
+ outcome_unit="$",
163
+ business_question="Did the loyalty program lift revenue?",
164
+ treatment_label="the loyalty program",
165
+ # Optional: pass the panel + column names so the auto-constructed
166
+ # DiagnosticReport can run data-dependent checks (2x2 pre-trends,
167
+ # Goodman-Bacon decomposition, EfficientDiD Hausman pretest).
168
+ # Without these the auto path still runs but skips those checks.
169
+ data=df,
170
+ outcome="revenue",
171
+ unit="store",
172
+ time="month",
173
+ first_treat="first_treat",
174
+ )
175
+ print(report.summary())
176
+ ```
177
+
178
+ `BusinessReport` auto-constructs a `DiagnosticReport` so the summary mentions pre-trends, sensitivity, and design-effect findings in one call. Methodology (phrasing rules, verdict thresholds, schema stability) is documented in [docs/methodology/REPORTING.md](docs/methodology/REPORTING.md). Feedback on wording, applicability, and missing diagnostics is welcome — this is the part of the library most likely to evolve in the next few releases.
179
+
147
180
  Already know DiD? The [academic quickstart](docs/quickstart.rst) and [estimator guide](docs/choosing_estimator.rst) cover the full technical details.
148
181
 
149
182
  ## Features
@@ -3119,14 +3152,20 @@ If you use diff-diff in your research, please cite it:
3119
3152
  ```bibtex
3120
3153
  @software{diff_diff,
3121
3154
  title = {diff-diff: Difference-in-Differences Causal Inference for Python},
3122
- author = {{diff-diff contributors}},
3155
+ author = {Gerber, Isaac},
3156
+ year = {2026},
3123
3157
  url = {https://github.com/igerber/diff-diff},
3158
+ doi = {10.5281/zenodo.19646175},
3124
3159
  license = {MIT},
3125
3160
  }
3126
3161
  ```
3127
3162
 
3163
+ The DOI above is the Zenodo concept DOI — it always resolves to the latest release. To cite a specific version, look up its versioned DOI on [the Zenodo project page](https://doi.org/10.5281/zenodo.19646175).
3164
+
3128
3165
  See [`CITATION.cff`](CITATION.cff) for the full citation metadata.
3129
3166
 
3167
+ **Note on authorship**: academic citation (`CITATION.cff`, the BibTeX above) lists individual authors with ORCIDs per scholarly convention. Package metadata surfaces (`pyproject.toml`, Sphinx docs) list "diff-diff contributors" to acknowledge the collective — see [`CONTRIBUTORS.md`](CONTRIBUTORS.md) for the full list.
3168
+
3130
3169
  ## License
3131
3170
 
3132
3171
  MIT License
@@ -5,6 +5,7 @@
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Downloads](https://img.shields.io/pypi/dm/diff-diff.svg)](https://pypi.org/project/diff-diff/)
7
7
  [![Documentation](https://readthedocs.org/projects/diff-diff/badge/?version=stable)](https://diff-diff.readthedocs.io/en/stable/)
8
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19646175.svg)](https://doi.org/10.5281/zenodo.19646175)
8
9
 
9
10
  A Python library for Difference-in-Differences (DiD) causal inference analysis with an sklearn-like API and statsmodels-style outputs.
10
11
 
@@ -92,6 +93,38 @@ Measuring campaign lift? Evaluating a product launch? diff-diff handles the caus
92
93
  - **[Brand awareness survey tutorial](docs/tutorials/17_brand_awareness_survey.ipynb)** - Full example with complex survey design, brand funnel analysis, and staggered rollouts
93
94
  - **Have BRFSS/ACS/CPS individual records?** Use [`aggregate_survey()`](docs/api/prep.rst) to roll respondent-level microdata into a geographic-period panel with inverse-variance precision weights. The returned second-stage design uses analytic weights (`aweight`), so it works directly with `DifferenceInDifferences`, `TwoWayFixedEffects`, `MultiPeriodDiD`, `SunAbraham`, `ContinuousDiD`, and `EfficientDiD` (estimators marked **Full** in the [survey support matrix](docs/choosing_estimator.rst))
94
95
 
96
+ ### Experimental preview: `BusinessReport` and `DiagnosticReport`
97
+
98
+ diff-diff ships two preview classes, `BusinessReport` and `DiagnosticReport`, that produce plain-English output and a structured `to_dict()` schema from any fitted result. **Both are experimental in this release** — wording, verdict thresholds, and schema shape will change as the library learns from real practitioner usage. Do not anchor downstream tooling on the schema yet; the experimental flag is noted in the CHANGELOG.
99
+
100
+ ```python
101
+ from diff_diff import CallawaySantAnna, BusinessReport
102
+
103
+ cs = CallawaySantAnna(base_period="universal").fit(
104
+ df, outcome="revenue", unit="store", time="month",
105
+ first_treat="first_treat", aggregate="event_study",
106
+ )
107
+ report = BusinessReport(
108
+ cs,
109
+ outcome_label="Revenue per store",
110
+ outcome_unit="$",
111
+ business_question="Did the loyalty program lift revenue?",
112
+ treatment_label="the loyalty program",
113
+ # Optional: pass the panel + column names so the auto-constructed
114
+ # DiagnosticReport can run data-dependent checks (2x2 pre-trends,
115
+ # Goodman-Bacon decomposition, EfficientDiD Hausman pretest).
116
+ # Without these the auto path still runs but skips those checks.
117
+ data=df,
118
+ outcome="revenue",
119
+ unit="store",
120
+ time="month",
121
+ first_treat="first_treat",
122
+ )
123
+ print(report.summary())
124
+ ```
125
+
126
+ `BusinessReport` auto-constructs a `DiagnosticReport` so the summary mentions pre-trends, sensitivity, and design-effect findings in one call. Methodology (phrasing rules, verdict thresholds, schema stability) is documented in [docs/methodology/REPORTING.md](docs/methodology/REPORTING.md). Feedback on wording, applicability, and missing diagnostics is welcome — this is the part of the library most likely to evolve in the next few releases.
127
+
95
128
  Already know DiD? The [academic quickstart](docs/quickstart.rst) and [estimator guide](docs/choosing_estimator.rst) cover the full technical details.
96
129
 
97
130
  ## Features
@@ -3067,14 +3100,20 @@ If you use diff-diff in your research, please cite it:
3067
3100
  ```bibtex
3068
3101
  @software{diff_diff,
3069
3102
  title = {diff-diff: Difference-in-Differences Causal Inference for Python},
3070
- author = {{diff-diff contributors}},
3103
+ author = {Gerber, Isaac},
3104
+ year = {2026},
3071
3105
  url = {https://github.com/igerber/diff-diff},
3106
+ doi = {10.5281/zenodo.19646175},
3072
3107
  license = {MIT},
3073
3108
  }
3074
3109
  ```
3075
3110
 
3111
+ The DOI above is the Zenodo concept DOI — it always resolves to the latest release. To cite a specific version, look up its versioned DOI on [the Zenodo project page](https://doi.org/10.5281/zenodo.19646175).
3112
+
3076
3113
  See [`CITATION.cff`](CITATION.cff) for the full citation metadata.
3077
3114
 
3115
+ **Note on authorship**: academic citation (`CITATION.cff`, the BibTeX above) lists individual authors with ORCIDs per scholarly convention. Package metadata surfaces (`pyproject.toml`, Sphinx docs) list "diff-diff contributors" to acknowledge the collective — see [`CONTRIBUTORS.md`](CONTRIBUTORS.md) for the full list.
3116
+
3078
3117
  ## License
3079
3118
 
3080
3119
  MIT License
@@ -43,6 +43,17 @@ from diff_diff.linalg import (
43
43
  InferenceResult,
44
44
  LinearRegression,
45
45
  )
46
+ from diff_diff.local_linear import (
47
+ KERNELS,
48
+ BandwidthResult,
49
+ LocalLinearFit,
50
+ epanechnikov_kernel,
51
+ kernel_moments,
52
+ local_linear_fit,
53
+ mse_optimal_bandwidth,
54
+ triangular_kernel,
55
+ uniform_kernel,
56
+ )
46
57
  from diff_diff.estimators import (
47
58
  DifferenceInDifferences,
48
59
  MultiPeriodDiD,
@@ -202,6 +213,16 @@ from diff_diff.visualization import (
202
213
  plot_synth_weights,
203
214
  )
204
215
  from diff_diff.practitioner import practitioner_next_steps
216
+ from diff_diff.business_report import (
217
+ BUSINESS_REPORT_SCHEMA_VERSION,
218
+ BusinessContext,
219
+ BusinessReport,
220
+ )
221
+ from diff_diff.diagnostic_report import (
222
+ DIAGNOSTIC_REPORT_SCHEMA_VERSION,
223
+ DiagnosticReport,
224
+ DiagnosticReportResults,
225
+ )
205
226
  from diff_diff._guides_api import get_llm_guide
206
227
  from diff_diff.datasets import (
207
228
  clear_cache,
@@ -231,7 +252,7 @@ EDiD = EfficientDiD
231
252
  ETWFE = WooldridgeDiD
232
253
  DCDH = ChaisemartinDHaultfoeuille
233
254
 
234
- __version__ = "3.1.3"
255
+ __version__ = "3.2.0"
235
256
  __all__ = [
236
257
  # Estimators
237
258
  "DifferenceInDifferences",
@@ -395,6 +416,17 @@ __all__ = [
395
416
  # Linear algebra helpers
396
417
  "LinearRegression",
397
418
  "InferenceResult",
419
+ # Local-linear regression infrastructure (Phase 1a for HeterogeneousAdoptionDiD)
420
+ "KERNELS",
421
+ "LocalLinearFit",
422
+ "epanechnikov_kernel",
423
+ "kernel_moments",
424
+ "local_linear_fit",
425
+ "triangular_kernel",
426
+ "uniform_kernel",
427
+ # MSE-optimal bandwidth selector (Phase 1b for HeterogeneousAdoptionDiD)
428
+ "BandwidthResult",
429
+ "mse_optimal_bandwidth",
398
430
  # Datasets
399
431
  "load_card_krueger",
400
432
  "load_castle_doctrine",
@@ -405,6 +437,12 @@ __all__ = [
405
437
  "clear_cache",
406
438
  # Practitioner guidance
407
439
  "practitioner_next_steps",
440
+ "BusinessReport",
441
+ "BusinessContext",
442
+ "BUSINESS_REPORT_SCHEMA_VERSION",
443
+ "DiagnosticReport",
444
+ "DiagnosticReportResults",
445
+ "DIAGNOSTIC_REPORT_SCHEMA_VERSION",
408
446
  # LLM guide accessor
409
447
  "get_llm_guide",
410
448
  ]