diff-diff 2.4.0__tar.gz → 2.4.1__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 (41) hide show
  1. {diff_diff-2.4.0 → diff_diff-2.4.1}/PKG-INFO +1 -1
  2. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/__init__.py +1 -1
  3. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/diagnostics.py +4 -10
  4. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/estimators.py +5 -19
  5. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/imputation.py +18 -739
  6. diff_diff-2.4.1/diff_diff/imputation_bootstrap.py +310 -0
  7. diff_diff-2.4.1/diff_diff/imputation_results.py +426 -0
  8. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/linalg.py +6 -6
  9. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/staggered.py +16 -30
  10. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/staggered_aggregation.py +3 -10
  11. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/sun_abraham.py +7 -12
  12. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/synthetic_did.py +8 -19
  13. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/triple_diff.py +6 -11
  14. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/trop.py +12 -325
  15. diff_diff-2.4.1/diff_diff/trop_results.py +322 -0
  16. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/two_stage.py +23 -834
  17. diff_diff-2.4.1/diff_diff/two_stage_bootstrap.py +449 -0
  18. diff_diff-2.4.1/diff_diff/two_stage_results.py +379 -0
  19. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/utils.py +2 -3
  20. {diff_diff-2.4.0 → diff_diff-2.4.1}/pyproject.toml +1 -1
  21. {diff_diff-2.4.0 → diff_diff-2.4.1}/rust/Cargo.lock +1 -1
  22. {diff_diff-2.4.0 → diff_diff-2.4.1}/rust/Cargo.toml +1 -1
  23. {diff_diff-2.4.0 → diff_diff-2.4.1}/README.md +0 -0
  24. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/_backend.py +0 -0
  25. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/bacon.py +0 -0
  26. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/datasets.py +0 -0
  27. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/honest_did.py +0 -0
  28. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/power.py +0 -0
  29. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/prep.py +0 -0
  30. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/prep_dgp.py +0 -0
  31. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/pretrends.py +0 -0
  32. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/results.py +0 -0
  33. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/staggered_bootstrap.py +0 -0
  34. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/staggered_results.py +0 -0
  35. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/twfe.py +0 -0
  36. {diff_diff-2.4.0 → diff_diff-2.4.1}/diff_diff/visualization.py +0 -0
  37. {diff_diff-2.4.0 → diff_diff-2.4.1}/rust/src/bootstrap.rs +0 -0
  38. {diff_diff-2.4.0 → diff_diff-2.4.1}/rust/src/lib.rs +0 -0
  39. {diff_diff-2.4.0 → diff_diff-2.4.1}/rust/src/linalg.rs +0 -0
  40. {diff_diff-2.4.0 → diff_diff-2.4.1}/rust/src/trop.rs +0 -0
  41. {diff_diff-2.4.0 → diff_diff-2.4.1}/rust/src/weights.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diff-diff
3
- Version: 2.4.0
3
+ Version: 2.4.1
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: Operating System :: OS Independent
@@ -148,7 +148,7 @@ from diff_diff.datasets import (
148
148
  load_mpdta,
149
149
  )
150
150
 
151
- __version__ = "2.4.0"
151
+ __version__ = "2.4.1"
152
152
  __all__ = [
153
153
  # Estimators
154
154
  "DifferenceInDifferences",
@@ -19,7 +19,7 @@ import pandas as pd
19
19
 
20
20
  from diff_diff.estimators import DifferenceInDifferences
21
21
  from diff_diff.results import _get_significance_stars
22
- from diff_diff.utils import compute_confidence_interval, compute_p_value
22
+ from diff_diff.utils import safe_inference
23
23
 
24
24
 
25
25
  @dataclass
@@ -661,7 +661,7 @@ def permutation_test(
661
661
  ci_lower = np.percentile(valid_effects, alpha / 2 * 100)
662
662
  ci_upper = np.percentile(valid_effects, (1 - alpha / 2) * 100)
663
663
 
664
- # T-stat from original estimate
664
+ # NOTE: Not using safe_inference — p_value is permutation-based, CI is percentile-based.
665
665
  t_stat = original_att / se if np.isfinite(se) and se > 0 else np.nan
666
666
 
667
667
  return PlaceboTestResults(
@@ -782,15 +782,9 @@ def leave_one_out_test(
782
782
 
783
783
  # Statistics of LOO distribution
784
784
  mean_effect = np.mean(valid_effects)
785
- se = np.std(valid_effects, ddof=1) if len(valid_effects) > 1 else 0.0
786
- t_stat = mean_effect / se if np.isfinite(se) and se > 0 else np.nan
787
-
788
- # Use t-distribution for p-value
785
+ se = np.std(valid_effects, ddof=1) if len(valid_effects) > 1 else np.nan
789
786
  df = len(valid_effects) - 1 if len(valid_effects) > 1 else 1
790
- p_value = compute_p_value(t_stat, df=df)
791
-
792
- # CI
793
- conf_int = compute_confidence_interval(mean_effect, se, alpha, df=df) if np.isfinite(se) and se > 0 else (np.nan, np.nan)
787
+ t_stat, p_value, conf_int = safe_inference(mean_effect, se, alpha=alpha, df=df)
794
788
 
795
789
  return PlaceboTestResults(
796
790
  test_type="leave_one_out",
@@ -27,9 +27,8 @@ from diff_diff.linalg import (
27
27
  from diff_diff.results import DiDResults, MultiPeriodDiDResults, PeriodEffect
28
28
  from diff_diff.utils import (
29
29
  WildBootstrapResults,
30
- compute_confidence_interval,
31
- compute_p_value,
32
30
  demean_by_group,
31
+ safe_inference,
33
32
  validate_binary,
34
33
  wild_bootstrap_se,
35
34
  )
@@ -1034,14 +1033,7 @@ class MultiPeriodDiD(DifferenceInDifferences):
1034
1033
  idx = interaction_indices[period]
1035
1034
  effect = coefficients[idx]
1036
1035
  se = np.sqrt(vcov[idx, idx])
1037
- if np.isfinite(se) and se > 0:
1038
- t_stat = effect / se
1039
- p_value = compute_p_value(t_stat, df=df)
1040
- conf_int = compute_confidence_interval(effect, se, self.alpha, df=df)
1041
- else:
1042
- t_stat = np.nan
1043
- p_value = np.nan
1044
- conf_int = (np.nan, np.nan)
1036
+ t_stat, p_value, conf_int = safe_inference(effect, se, alpha=self.alpha, df=df)
1045
1037
 
1046
1038
  period_effects[period] = PeriodEffect(
1047
1039
  period=period,
@@ -1085,15 +1077,9 @@ class MultiPeriodDiD(DifferenceInDifferences):
1085
1077
  avg_conf_int = (np.nan, np.nan)
1086
1078
  else:
1087
1079
  avg_se = float(np.sqrt(avg_var))
1088
- if np.isfinite(avg_se) and avg_se > 0:
1089
- avg_t_stat = avg_att / avg_se
1090
- avg_p_value = compute_p_value(avg_t_stat, df=df)
1091
- avg_conf_int = compute_confidence_interval(avg_att, avg_se, self.alpha, df=df)
1092
- else:
1093
- # Zero SE (degenerate case)
1094
- avg_t_stat = np.nan
1095
- avg_p_value = np.nan
1096
- avg_conf_int = (np.nan, np.nan)
1080
+ avg_t_stat, avg_p_value, avg_conf_int = safe_inference(
1081
+ avg_att, avg_se, alpha=self.alpha, df=df
1082
+ )
1097
1083
 
1098
1084
  # Count observations
1099
1085
  n_treated = int(np.sum(d))