diff-diff 2.4.0__tar.gz → 2.4.2__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.4.0 → diff_diff-2.4.2}/PKG-INFO +4 -4
- {diff_diff-2.4.0 → diff_diff-2.4.2}/README.md +3 -3
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/__init__.py +1 -1
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/_backend.py +21 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/diagnostics.py +4 -10
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/estimators.py +5 -19
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/imputation.py +30 -754
- diff_diff-2.4.2/diff_diff/imputation_bootstrap.py +333 -0
- diff_diff-2.4.2/diff_diff/imputation_results.py +426 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/linalg.py +6 -6
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/staggered.py +16 -30
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/staggered_aggregation.py +3 -10
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/sun_abraham.py +7 -34
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/synthetic_did.py +8 -19
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/triple_diff.py +6 -11
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/trop.py +19 -326
- diff_diff-2.4.2/diff_diff/trop_results.py +322 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/two_stage.py +85 -843
- diff_diff-2.4.2/diff_diff/two_stage_bootstrap.py +459 -0
- diff_diff-2.4.2/diff_diff/two_stage_results.py +379 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/utils.py +2 -101
- {diff_diff-2.4.0 → diff_diff-2.4.2}/pyproject.toml +1 -1
- {diff_diff-2.4.0 → diff_diff-2.4.2}/rust/Cargo.lock +28 -1
- {diff_diff-2.4.0 → diff_diff-2.4.2}/rust/Cargo.toml +11 -3
- diff_diff-2.4.2/rust/build.rs +12 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/rust/src/lib.rs +25 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/rust/src/weights.rs +410 -45
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/bacon.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/datasets.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/honest_did.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/power.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/prep.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/prep_dgp.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/pretrends.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/results.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/staggered_bootstrap.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/staggered_results.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/twfe.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/diff_diff/visualization.py +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/rust/src/bootstrap.rs +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/rust/src/linalg.rs +0 -0
- {diff_diff-2.4.0 → diff_diff-2.4.2}/rust/src/trop.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diff-diff
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.2
|
|
4
4
|
Classifier: Development Status :: 5 - Production/Stable
|
|
5
5
|
Classifier: Intended Audience :: Science/Research
|
|
6
6
|
Classifier: Operating System :: OS Independent
|
|
@@ -2021,7 +2021,7 @@ TROP(
|
|
|
2021
2021
|
max_iter=100, # Max iterations for factor estimation
|
|
2022
2022
|
tol=1e-6, # Convergence tolerance
|
|
2023
2023
|
alpha=0.05, # Significance level for CIs
|
|
2024
|
-
n_bootstrap=200, # Bootstrap replications
|
|
2024
|
+
n_bootstrap=200, # Bootstrap replications (minimum 2; TROP requires bootstrap for SEs)
|
|
2025
2025
|
seed=None # Random seed
|
|
2026
2026
|
)
|
|
2027
2027
|
```
|
|
@@ -2102,8 +2102,6 @@ SunAbraham(
|
|
|
2102
2102
|
| `time` | str | Time period column |
|
|
2103
2103
|
| `first_treat` | str | Column with first treatment period (0 for never-treated) |
|
|
2104
2104
|
| `covariates` | list | Covariate column names |
|
|
2105
|
-
| `min_pre_periods` | int | Minimum pre-treatment periods to include |
|
|
2106
|
-
| `min_post_periods` | int | Minimum post-treatment periods to include |
|
|
2107
2105
|
|
|
2108
2106
|
### SunAbrahamResults
|
|
2109
2107
|
|
|
@@ -2143,6 +2141,7 @@ ImputationDiD(
|
|
|
2143
2141
|
alpha=0.05, # Significance level for CIs
|
|
2144
2142
|
cluster=None, # Column for cluster-robust SEs
|
|
2145
2143
|
n_bootstrap=0, # Bootstrap iterations (0 = analytical)
|
|
2144
|
+
bootstrap_weights='rademacher', # 'rademacher', 'mammen', or 'webb'
|
|
2146
2145
|
seed=None, # Random seed
|
|
2147
2146
|
rank_deficient_action='warn', # 'warn', 'error', or 'silent'
|
|
2148
2147
|
horizon_max=None, # Max event-study horizon
|
|
@@ -2197,6 +2196,7 @@ TwoStageDiD(
|
|
|
2197
2196
|
alpha=0.05, # Significance level for CIs
|
|
2198
2197
|
cluster=None, # Column for cluster-robust SEs (defaults to unit)
|
|
2199
2198
|
n_bootstrap=0, # Bootstrap iterations (0 = analytical GMM SEs)
|
|
2199
|
+
bootstrap_weights='rademacher', # 'rademacher', 'mammen', or 'webb'
|
|
2200
2200
|
seed=None, # Random seed
|
|
2201
2201
|
rank_deficient_action='warn', # 'warn', 'error', or 'silent'
|
|
2202
2202
|
horizon_max=None, # Max event-study horizon
|
|
@@ -1983,7 +1983,7 @@ TROP(
|
|
|
1983
1983
|
max_iter=100, # Max iterations for factor estimation
|
|
1984
1984
|
tol=1e-6, # Convergence tolerance
|
|
1985
1985
|
alpha=0.05, # Significance level for CIs
|
|
1986
|
-
n_bootstrap=200, # Bootstrap replications
|
|
1986
|
+
n_bootstrap=200, # Bootstrap replications (minimum 2; TROP requires bootstrap for SEs)
|
|
1987
1987
|
seed=None # Random seed
|
|
1988
1988
|
)
|
|
1989
1989
|
```
|
|
@@ -2064,8 +2064,6 @@ SunAbraham(
|
|
|
2064
2064
|
| `time` | str | Time period column |
|
|
2065
2065
|
| `first_treat` | str | Column with first treatment period (0 for never-treated) |
|
|
2066
2066
|
| `covariates` | list | Covariate column names |
|
|
2067
|
-
| `min_pre_periods` | int | Minimum pre-treatment periods to include |
|
|
2068
|
-
| `min_post_periods` | int | Minimum post-treatment periods to include |
|
|
2069
2067
|
|
|
2070
2068
|
### SunAbrahamResults
|
|
2071
2069
|
|
|
@@ -2105,6 +2103,7 @@ ImputationDiD(
|
|
|
2105
2103
|
alpha=0.05, # Significance level for CIs
|
|
2106
2104
|
cluster=None, # Column for cluster-robust SEs
|
|
2107
2105
|
n_bootstrap=0, # Bootstrap iterations (0 = analytical)
|
|
2106
|
+
bootstrap_weights='rademacher', # 'rademacher', 'mammen', or 'webb'
|
|
2108
2107
|
seed=None, # Random seed
|
|
2109
2108
|
rank_deficient_action='warn', # 'warn', 'error', or 'silent'
|
|
2110
2109
|
horizon_max=None, # Max event-study horizon
|
|
@@ -2159,6 +2158,7 @@ TwoStageDiD(
|
|
|
2159
2158
|
alpha=0.05, # Significance level for CIs
|
|
2160
2159
|
cluster=None, # Column for cluster-robust SEs (defaults to unit)
|
|
2161
2160
|
n_bootstrap=0, # Bootstrap iterations (0 = analytical GMM SEs)
|
|
2161
|
+
bootstrap_weights='rademacher', # 'rademacher', 'mammen', or 'webb'
|
|
2162
2162
|
seed=None, # Random seed
|
|
2163
2163
|
rank_deficient_action='warn', # 'warn', 'error', or 'silent'
|
|
2164
2164
|
horizon_max=None, # Max event-study horizon
|
|
@@ -35,6 +35,8 @@ try:
|
|
|
35
35
|
compute_time_weights as _rust_compute_time_weights,
|
|
36
36
|
compute_noise_level as _rust_compute_noise_level,
|
|
37
37
|
sc_weight_fw as _rust_sc_weight_fw,
|
|
38
|
+
# Diagnostics
|
|
39
|
+
rust_backend_info as _rust_backend_info,
|
|
38
40
|
)
|
|
39
41
|
_rust_available = True
|
|
40
42
|
except ImportError:
|
|
@@ -56,6 +58,7 @@ except ImportError:
|
|
|
56
58
|
_rust_compute_time_weights = None
|
|
57
59
|
_rust_compute_noise_level = None
|
|
58
60
|
_rust_sc_weight_fw = None
|
|
61
|
+
_rust_backend_info = None
|
|
59
62
|
|
|
60
63
|
# Determine final backend based on environment variable and availability
|
|
61
64
|
if _backend_env == 'python':
|
|
@@ -78,6 +81,7 @@ if _backend_env == 'python':
|
|
|
78
81
|
_rust_compute_time_weights = None
|
|
79
82
|
_rust_compute_noise_level = None
|
|
80
83
|
_rust_sc_weight_fw = None
|
|
84
|
+
_rust_backend_info = None
|
|
81
85
|
elif _backend_env == 'rust':
|
|
82
86
|
# Force Rust mode - fail if not available
|
|
83
87
|
if not _rust_available:
|
|
@@ -90,8 +94,25 @@ else:
|
|
|
90
94
|
# Auto mode - use Rust if available
|
|
91
95
|
HAS_RUST_BACKEND = _rust_available
|
|
92
96
|
|
|
97
|
+
|
|
98
|
+
def rust_backend_info():
|
|
99
|
+
"""Return compile-time BLAS feature information for the Rust backend.
|
|
100
|
+
|
|
101
|
+
Returns a dict with keys:
|
|
102
|
+
- 'blas': True if any BLAS backend is linked
|
|
103
|
+
- 'accelerate': True if Apple Accelerate is linked (macOS)
|
|
104
|
+
- 'openblas': True if OpenBLAS is linked (Linux)
|
|
105
|
+
|
|
106
|
+
If the Rust backend is not available, all values are False.
|
|
107
|
+
"""
|
|
108
|
+
if _rust_backend_info is not None:
|
|
109
|
+
return _rust_backend_info()
|
|
110
|
+
return {"blas": False, "accelerate": False, "openblas": False}
|
|
111
|
+
|
|
112
|
+
|
|
93
113
|
__all__ = [
|
|
94
114
|
'HAS_RUST_BACKEND',
|
|
115
|
+
'rust_backend_info',
|
|
95
116
|
'_rust_bootstrap_weights',
|
|
96
117
|
'_rust_synthetic_weights',
|
|
97
118
|
'_rust_project_simplex',
|
|
@@ -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
|
|
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
|
-
#
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
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))
|