diff-diff 2.1.6__tar.gz → 2.1.7__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.
- {diff_diff-2.1.6 → diff_diff-2.1.7}/PKG-INFO +1 -1
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/__init__.py +1 -1
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/visualization.py +44 -11
- {diff_diff-2.1.6 → diff_diff-2.1.7}/pyproject.toml +1 -1
- {diff_diff-2.1.6 → diff_diff-2.1.7}/rust/Cargo.lock +3 -3
- {diff_diff-2.1.6 → diff_diff-2.1.7}/rust/Cargo.toml +1 -1
- {diff_diff-2.1.6 → diff_diff-2.1.7}/README.md +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/_backend.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/bacon.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/datasets.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/diagnostics.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/estimators.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/honest_did.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/linalg.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/power.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/prep.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/prep_dgp.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/pretrends.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/results.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/staggered.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/staggered_aggregation.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/staggered_bootstrap.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/staggered_results.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/sun_abraham.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/synthetic_did.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/triple_diff.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/trop.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/twfe.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/diff_diff/utils.py +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/rust/src/bootstrap.rs +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/rust/src/lib.rs +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/rust/src/linalg.rs +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/rust/src/trop.rs +0 -0
- {diff_diff-2.1.6 → diff_diff-2.1.7}/rust/src/weights.rs +0 -0
|
@@ -73,8 +73,10 @@ def plot_event_study(
|
|
|
73
73
|
periods : list, optional
|
|
74
74
|
List of periods to plot. If None, uses all periods from results.
|
|
75
75
|
reference_period : any, optional
|
|
76
|
-
The reference period
|
|
77
|
-
|
|
76
|
+
The reference period to highlight. When explicitly provided, effects
|
|
77
|
+
are normalized (ref effect subtracted) and ref SE is set to NaN.
|
|
78
|
+
When None and auto-inferred from results, only hollow marker styling
|
|
79
|
+
is applied (no normalization). If None, tries to infer from results.
|
|
78
80
|
pre_periods : list, optional
|
|
79
81
|
List of pre-treatment periods. Used for shading.
|
|
80
82
|
post_periods : list, optional
|
|
@@ -151,8 +153,9 @@ def plot_event_study(
|
|
|
151
153
|
trends holds. Large pre-treatment effects suggest the assumption may
|
|
152
154
|
be violated.
|
|
153
155
|
|
|
154
|
-
2. **Reference period**: Usually the last pre-treatment period (t=-1)
|
|
155
|
-
|
|
156
|
+
2. **Reference period**: Usually the last pre-treatment period (t=-1).
|
|
157
|
+
When explicitly specified via ``reference_period``, effects are normalized
|
|
158
|
+
to zero at this period. When auto-inferred, shown with hollow marker only.
|
|
156
159
|
|
|
157
160
|
3. **Post-treatment periods**: The treatment effects of interest. These
|
|
158
161
|
show how the outcome evolved after treatment.
|
|
@@ -170,10 +173,18 @@ def plot_event_study(
|
|
|
170
173
|
|
|
171
174
|
from scipy import stats as scipy_stats
|
|
172
175
|
|
|
176
|
+
# Track if reference_period was explicitly provided by user
|
|
177
|
+
reference_period_explicit = reference_period is not None
|
|
178
|
+
|
|
173
179
|
# Extract data from results if provided
|
|
174
180
|
if results is not None:
|
|
175
|
-
|
|
176
|
-
|
|
181
|
+
extracted = _extract_plot_data(
|
|
182
|
+
results, periods, pre_periods, post_periods, reference_period
|
|
183
|
+
)
|
|
184
|
+
effects, se, periods, pre_periods, post_periods, reference_period, reference_inferred = extracted
|
|
185
|
+
# If reference was inferred from results, it was NOT explicitly provided
|
|
186
|
+
if reference_inferred:
|
|
187
|
+
reference_period_explicit = False
|
|
177
188
|
elif effects is None or se is None:
|
|
178
189
|
raise ValueError(
|
|
179
190
|
"Must provide either 'results' or both 'effects' and 'se'"
|
|
@@ -192,6 +203,19 @@ def plot_event_study(
|
|
|
192
203
|
# Compute confidence intervals
|
|
193
204
|
critical_value = scipy_stats.norm.ppf(1 - alpha / 2)
|
|
194
205
|
|
|
206
|
+
# Normalize effects to reference period ONLY if explicitly specified by user
|
|
207
|
+
# Auto-inferred reference periods (from CallawaySantAnna) just get hollow marker styling,
|
|
208
|
+
# NO normalization. This prevents unintended normalization when the reference period
|
|
209
|
+
# isn't a true identifying constraint (e.g., CallawaySantAnna with base_period="varying").
|
|
210
|
+
if (reference_period is not None and reference_period in effects and
|
|
211
|
+
reference_period_explicit):
|
|
212
|
+
ref_effect = effects[reference_period]
|
|
213
|
+
if np.isfinite(ref_effect):
|
|
214
|
+
effects = {p: e - ref_effect for p, e in effects.items()}
|
|
215
|
+
# Set reference SE to NaN (it's now a constraint, not an estimate)
|
|
216
|
+
# This follows fixest convention where the omitted category has no SE/CI
|
|
217
|
+
se = {p: (np.nan if p == reference_period else s) for p, s in se.items()}
|
|
218
|
+
|
|
195
219
|
plot_data = []
|
|
196
220
|
for period in periods:
|
|
197
221
|
effect = effects.get(period, np.nan)
|
|
@@ -304,14 +328,17 @@ def _extract_plot_data(
|
|
|
304
328
|
pre_periods: Optional[List[Any]],
|
|
305
329
|
post_periods: Optional[List[Any]],
|
|
306
330
|
reference_period: Optional[Any],
|
|
307
|
-
) -> Tuple[Dict, Dict, List, List, List, Any]:
|
|
331
|
+
) -> Tuple[Dict, Dict, List, List, List, Any, bool]:
|
|
308
332
|
"""
|
|
309
333
|
Extract plotting data from various result types.
|
|
310
334
|
|
|
311
335
|
Returns
|
|
312
336
|
-------
|
|
313
337
|
tuple
|
|
314
|
-
(effects, se, periods, pre_periods, post_periods, reference_period)
|
|
338
|
+
(effects, se, periods, pre_periods, post_periods, reference_period, reference_inferred)
|
|
339
|
+
|
|
340
|
+
reference_inferred is True if reference_period was auto-detected from results
|
|
341
|
+
rather than explicitly provided by the user.
|
|
315
342
|
"""
|
|
316
343
|
# Handle DataFrame input
|
|
317
344
|
if isinstance(results, pd.DataFrame):
|
|
@@ -328,7 +355,8 @@ def _extract_plot_data(
|
|
|
328
355
|
if periods is None:
|
|
329
356
|
periods = list(results['period'])
|
|
330
357
|
|
|
331
|
-
|
|
358
|
+
# DataFrame input: reference_period was already set by caller, never inferred here
|
|
359
|
+
return effects, se, periods, pre_periods, post_periods, reference_period, False
|
|
332
360
|
|
|
333
361
|
# Handle MultiPeriodDiDResults
|
|
334
362
|
if hasattr(results, 'period_effects'):
|
|
@@ -348,7 +376,8 @@ def _extract_plot_data(
|
|
|
348
376
|
if periods is None:
|
|
349
377
|
periods = post_periods
|
|
350
378
|
|
|
351
|
-
|
|
379
|
+
# MultiPeriodDiDResults: reference_period was already set by caller, never inferred here
|
|
380
|
+
return effects, se, periods, pre_periods, post_periods, reference_period, False
|
|
352
381
|
|
|
353
382
|
# Handle CallawaySantAnnaResults (event study aggregation)
|
|
354
383
|
if hasattr(results, 'event_study_effects') and results.event_study_effects is not None:
|
|
@@ -362,8 +391,12 @@ def _extract_plot_data(
|
|
|
362
391
|
if periods is None:
|
|
363
392
|
periods = sorted(effects.keys())
|
|
364
393
|
|
|
394
|
+
# Track if reference_period was explicitly provided vs auto-inferred
|
|
395
|
+
reference_inferred = False
|
|
396
|
+
|
|
365
397
|
# Reference period is typically -1 for event study
|
|
366
398
|
if reference_period is None:
|
|
399
|
+
reference_inferred = True # We're about to infer it
|
|
367
400
|
# Detect reference period from n_groups=0 marker (normalization constraint)
|
|
368
401
|
# This handles anticipation > 0 where reference is at e = -1 - anticipation
|
|
369
402
|
for period, effect_data in results.event_study_effects.items():
|
|
@@ -380,7 +413,7 @@ def _extract_plot_data(
|
|
|
380
413
|
if post_periods is None:
|
|
381
414
|
post_periods = [p for p in periods if p >= 0]
|
|
382
415
|
|
|
383
|
-
return effects, se, periods, pre_periods, post_periods, reference_period
|
|
416
|
+
return effects, se, periods, pre_periods, post_periods, reference_period, reference_inferred
|
|
384
417
|
|
|
385
418
|
raise TypeError(
|
|
386
419
|
f"Cannot extract plot data from {type(results).__name__}. "
|
|
@@ -289,7 +289,7 @@ dependencies = [
|
|
|
289
289
|
|
|
290
290
|
[[package]]
|
|
291
291
|
name = "diff_diff_rust"
|
|
292
|
-
version = "2.1.
|
|
292
|
+
version = "2.1.7"
|
|
293
293
|
dependencies = [
|
|
294
294
|
"ndarray",
|
|
295
295
|
"ndarray-linalg",
|
|
@@ -1846,9 +1846,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
|
|
1846
1846
|
|
|
1847
1847
|
[[package]]
|
|
1848
1848
|
name = "uuid"
|
|
1849
|
-
version = "1.
|
|
1849
|
+
version = "1.20.0"
|
|
1850
1850
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1851
|
-
checksum = "
|
|
1851
|
+
checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
|
|
1852
1852
|
dependencies = [
|
|
1853
1853
|
"getrandom 0.3.4",
|
|
1854
1854
|
"js-sys",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|