diff-diff 3.2.0__tar.gz → 3.3.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.
- {diff_diff-3.2.0 → diff_diff-3.3.0}/PKG-INFO +1 -1
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/__init__.py +64 -9
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/_backend.py +12 -4
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/_guides_api.py +7 -3
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/_nprobust_port.py +529 -40
- diff_diff-3.3.0/diff_diff/_reporting_helpers.py +634 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/bootstrap_utils.py +41 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/business_report.py +206 -11
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/chaisemartin_dhaultfoeuille.py +973 -234
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/chaisemartin_dhaultfoeuille_bootstrap.py +70 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/chaisemartin_dhaultfoeuille_results.py +289 -39
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/diagnostic_report.py +178 -16
- diff_diff-3.3.0/diff_diff/guides/llms-autonomous.txt +844 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/guides/llms-full.txt +26 -8
- diff_diff-3.3.0/diff_diff/had.py +4415 -0
- diff_diff-3.3.0/diff_diff/had_pretests.py +3019 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/local_linear.py +659 -110
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/power.py +5 -2
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/prep.py +96 -5
- diff_diff-3.3.0/diff_diff/profile.py +714 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/results.py +58 -7
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/survey.py +334 -1
- diff_diff-3.3.0/diff_diff/synthetic_did.py +2688 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/trop_global.py +29 -25
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/trop_local.py +60 -83
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/utils.py +575 -157
- {diff_diff-3.2.0 → diff_diff-3.3.0}/pyproject.toml +1 -1
- {diff_diff-3.2.0 → diff_diff-3.3.0}/rust/Cargo.lock +5 -5
- {diff_diff-3.2.0 → diff_diff-3.3.0}/rust/Cargo.toml +1 -1
- {diff_diff-3.2.0 → diff_diff-3.3.0}/rust/src/lib.rs +4 -2
- {diff_diff-3.2.0 → diff_diff-3.3.0}/rust/src/linalg.rs +1 -1
- {diff_diff-3.2.0 → diff_diff-3.3.0}/rust/src/trop.rs +289 -142
- {diff_diff-3.2.0 → diff_diff-3.3.0}/rust/src/weights.rs +582 -145
- diff_diff-3.2.0/diff_diff/synthetic_did.py +0 -1484
- {diff_diff-3.2.0 → diff_diff-3.3.0}/LICENSE +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/README.md +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/bacon.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/continuous_did.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/continuous_did_bspline.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/continuous_did_results.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/datasets.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/diagnostics.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/efficient_did.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/efficient_did_bootstrap.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/efficient_did_covariates.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/efficient_did_results.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/efficient_did_weights.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/estimators.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/guides/__init__.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/guides/llms-practitioner.txt +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/guides/llms.txt +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/honest_did.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/imputation.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/imputation_bootstrap.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/imputation_results.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/linalg.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/practitioner.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/prep_dgp.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/pretrends.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/stacked_did.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/stacked_did_results.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/staggered.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/staggered_aggregation.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/staggered_bootstrap.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/staggered_results.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/staggered_triple_diff.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/staggered_triple_diff_results.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/sun_abraham.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/triple_diff.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/trop.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/trop_results.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/twfe.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/two_stage.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/two_stage_bootstrap.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/two_stage_results.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/visualization/__init__.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/visualization/_common.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/visualization/_continuous.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/visualization/_diagnostic.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/visualization/_event_study.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/visualization/_power.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/visualization/_staggered.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/visualization/_synthetic.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/wooldridge.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/diff_diff/wooldridge_results.py +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/rust/build.rs +0 -0
- {diff_diff-3.2.0 → diff_diff-3.3.0}/rust/src/bootstrap.rs +0 -0
|
@@ -4,14 +4,20 @@ diff-diff: A library for Difference-in-Differences analysis.
|
|
|
4
4
|
This library provides sklearn-like estimators for causal inference
|
|
5
5
|
using the difference-in-differences methodology.
|
|
6
6
|
|
|
7
|
-
For
|
|
8
|
-
on Baker et al. (2025). After estimation, call
|
|
9
|
-
``practitioner_next_steps(results)`` for context-aware guidance on
|
|
10
|
-
remaining diagnostic steps.
|
|
7
|
+
For AI agents:
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
``get_llm_guide("
|
|
9
|
+
1. Describe your data: ``diff_diff.profile_panel(df, unit=..., time=...,
|
|
10
|
+
treatment=..., outcome=...)``
|
|
11
|
+
2. Consult the reference: ``diff_diff.get_llm_guide("autonomous")``
|
|
12
|
+
(estimator-support matrix + reasoning)
|
|
13
|
+
3. Follow the workflow: ``diff_diff.get_llm_guide("practitioner")``
|
|
14
|
+
(Baker et al. (2025) 8-step recipe)
|
|
15
|
+
4. Report results: ``diff_diff.BusinessReport(results)``
|
|
16
|
+
(structured agent-legible output)
|
|
17
|
+
|
|
18
|
+
For a comprehensive API reference call ``diff_diff.get_llm_guide("full")``;
|
|
19
|
+
``practitioner_next_steps(results)`` returns context-aware guidance after
|
|
20
|
+
any estimator's ``fit()``.
|
|
15
21
|
"""
|
|
16
22
|
|
|
17
23
|
# Import backend detection from dedicated module (avoids circular imports)
|
|
@@ -21,7 +27,6 @@ from diff_diff._backend import (
|
|
|
21
27
|
_rust_compute_robust_vcov,
|
|
22
28
|
_rust_project_simplex,
|
|
23
29
|
_rust_solve_ols,
|
|
24
|
-
_rust_synthetic_weights,
|
|
25
30
|
)
|
|
26
31
|
|
|
27
32
|
from diff_diff.bacon import (
|
|
@@ -46,7 +51,9 @@ from diff_diff.linalg import (
|
|
|
46
51
|
from diff_diff.local_linear import (
|
|
47
52
|
KERNELS,
|
|
48
53
|
BandwidthResult,
|
|
54
|
+
BiasCorrectedFit,
|
|
49
55
|
LocalLinearFit,
|
|
56
|
+
bias_corrected_local_linear,
|
|
50
57
|
epanechnikov_kernel,
|
|
51
58
|
kernel_moments,
|
|
52
59
|
local_linear_fit,
|
|
@@ -54,6 +61,25 @@ from diff_diff.local_linear import (
|
|
|
54
61
|
triangular_kernel,
|
|
55
62
|
uniform_kernel,
|
|
56
63
|
)
|
|
64
|
+
from diff_diff.had import (
|
|
65
|
+
HeterogeneousAdoptionDiD,
|
|
66
|
+
HeterogeneousAdoptionDiDEventStudyResults,
|
|
67
|
+
HeterogeneousAdoptionDiDResults,
|
|
68
|
+
)
|
|
69
|
+
from diff_diff.had_pretests import (
|
|
70
|
+
HADPretestReport,
|
|
71
|
+
QUGTestResults,
|
|
72
|
+
StuteJointResult,
|
|
73
|
+
StuteTestResults,
|
|
74
|
+
YatchewTestResults,
|
|
75
|
+
did_had_pretest_workflow,
|
|
76
|
+
joint_homogeneity_test,
|
|
77
|
+
joint_pretrends_test,
|
|
78
|
+
qug_test,
|
|
79
|
+
stute_joint_pretest,
|
|
80
|
+
stute_test,
|
|
81
|
+
yatchew_hr_test,
|
|
82
|
+
)
|
|
57
83
|
from diff_diff.estimators import (
|
|
58
84
|
DifferenceInDifferences,
|
|
59
85
|
MultiPeriodDiD,
|
|
@@ -224,6 +250,7 @@ from diff_diff.diagnostic_report import (
|
|
|
224
250
|
DiagnosticReportResults,
|
|
225
251
|
)
|
|
226
252
|
from diff_diff._guides_api import get_llm_guide
|
|
253
|
+
from diff_diff.profile import Alert, PanelProfile, profile_panel
|
|
227
254
|
from diff_diff.datasets import (
|
|
228
255
|
clear_cache,
|
|
229
256
|
list_datasets,
|
|
@@ -251,8 +278,9 @@ Bacon = BaconDecomposition
|
|
|
251
278
|
EDiD = EfficientDiD
|
|
252
279
|
ETWFE = WooldridgeDiD
|
|
253
280
|
DCDH = ChaisemartinDHaultfoeuille
|
|
281
|
+
HAD = HeterogeneousAdoptionDiD
|
|
254
282
|
|
|
255
|
-
__version__ = "3.
|
|
283
|
+
__version__ = "3.3.0"
|
|
256
284
|
__all__ = [
|
|
257
285
|
# Estimators
|
|
258
286
|
"DifferenceInDifferences",
|
|
@@ -427,6 +455,29 @@ __all__ = [
|
|
|
427
455
|
# MSE-optimal bandwidth selector (Phase 1b for HeterogeneousAdoptionDiD)
|
|
428
456
|
"BandwidthResult",
|
|
429
457
|
"mse_optimal_bandwidth",
|
|
458
|
+
# Bias-corrected local-linear (Phase 1c for HeterogeneousAdoptionDiD)
|
|
459
|
+
"BiasCorrectedFit",
|
|
460
|
+
"bias_corrected_local_linear",
|
|
461
|
+
# HeterogeneousAdoptionDiD (Phase 2a single-period, Phase 2b event study)
|
|
462
|
+
"HeterogeneousAdoptionDiD",
|
|
463
|
+
"HeterogeneousAdoptionDiDResults",
|
|
464
|
+
"HeterogeneousAdoptionDiDEventStudyResults",
|
|
465
|
+
"HAD",
|
|
466
|
+
# HeterogeneousAdoptionDiD pre-test diagnostics (Phase 3)
|
|
467
|
+
"qug_test",
|
|
468
|
+
"stute_test",
|
|
469
|
+
"yatchew_hr_test",
|
|
470
|
+
"did_had_pretest_workflow",
|
|
471
|
+
"QUGTestResults",
|
|
472
|
+
"StuteTestResults",
|
|
473
|
+
"YatchewTestResults",
|
|
474
|
+
"HADPretestReport",
|
|
475
|
+
# HAD joint pre-tests (Phase 3 follow-up) — multi-period event-study
|
|
476
|
+
# workflow dispatch via did_had_pretest_workflow(aggregate="event_study")
|
|
477
|
+
"stute_joint_pretest",
|
|
478
|
+
"joint_pretrends_test",
|
|
479
|
+
"joint_homogeneity_test",
|
|
480
|
+
"StuteJointResult",
|
|
430
481
|
# Datasets
|
|
431
482
|
"load_card_krueger",
|
|
432
483
|
"load_castle_doctrine",
|
|
@@ -443,6 +494,10 @@ __all__ = [
|
|
|
443
494
|
"DiagnosticReport",
|
|
444
495
|
"DiagnosticReportResults",
|
|
445
496
|
"DIAGNOSTIC_REPORT_SCHEMA_VERSION",
|
|
497
|
+
# Panel profiling (agent-facing pre-fit describe utility)
|
|
498
|
+
"profile_panel",
|
|
499
|
+
"PanelProfile",
|
|
500
|
+
"Alert",
|
|
446
501
|
# LLM guide accessor
|
|
447
502
|
"get_llm_guide",
|
|
448
503
|
]
|
|
@@ -19,7 +19,6 @@ _backend_env = os.environ.get("DIFF_DIFF_BACKEND", "auto").lower()
|
|
|
19
19
|
try:
|
|
20
20
|
from diff_diff._rust_backend import (
|
|
21
21
|
generate_bootstrap_weights_batch as _rust_bootstrap_weights,
|
|
22
|
-
compute_synthetic_weights as _rust_synthetic_weights,
|
|
23
22
|
project_simplex as _rust_project_simplex,
|
|
24
23
|
solve_ols as _rust_solve_ols,
|
|
25
24
|
compute_robust_vcov as _rust_compute_robust_vcov,
|
|
@@ -35,6 +34,9 @@ try:
|
|
|
35
34
|
compute_time_weights as _rust_compute_time_weights,
|
|
36
35
|
compute_noise_level as _rust_compute_noise_level,
|
|
37
36
|
sc_weight_fw as _rust_sc_weight_fw,
|
|
37
|
+
sc_weight_fw_with_convergence as _rust_sc_weight_fw_with_convergence,
|
|
38
|
+
sc_weight_fw_weighted as _rust_sc_weight_fw_weighted,
|
|
39
|
+
sc_weight_fw_weighted_with_convergence as _rust_sc_weight_fw_weighted_with_convergence,
|
|
38
40
|
# Diagnostics
|
|
39
41
|
rust_backend_info as _rust_backend_info,
|
|
40
42
|
)
|
|
@@ -43,7 +45,6 @@ try:
|
|
|
43
45
|
except ImportError:
|
|
44
46
|
_rust_available = False
|
|
45
47
|
_rust_bootstrap_weights = None
|
|
46
|
-
_rust_synthetic_weights = None
|
|
47
48
|
_rust_project_simplex = None
|
|
48
49
|
_rust_solve_ols = None
|
|
49
50
|
_rust_compute_robust_vcov = None
|
|
@@ -59,6 +60,9 @@ except ImportError:
|
|
|
59
60
|
_rust_compute_time_weights = None
|
|
60
61
|
_rust_compute_noise_level = None
|
|
61
62
|
_rust_sc_weight_fw = None
|
|
63
|
+
_rust_sc_weight_fw_with_convergence = None
|
|
64
|
+
_rust_sc_weight_fw_weighted = None
|
|
65
|
+
_rust_sc_weight_fw_weighted_with_convergence = None
|
|
62
66
|
_rust_backend_info = None
|
|
63
67
|
|
|
64
68
|
# Determine final backend based on environment variable and availability
|
|
@@ -66,7 +70,6 @@ if _backend_env == "python":
|
|
|
66
70
|
# Force pure Python mode - disable Rust even if available
|
|
67
71
|
HAS_RUST_BACKEND = False
|
|
68
72
|
_rust_bootstrap_weights = None
|
|
69
|
-
_rust_synthetic_weights = None
|
|
70
73
|
_rust_project_simplex = None
|
|
71
74
|
_rust_solve_ols = None
|
|
72
75
|
_rust_compute_robust_vcov = None
|
|
@@ -82,6 +85,9 @@ if _backend_env == "python":
|
|
|
82
85
|
_rust_compute_time_weights = None
|
|
83
86
|
_rust_compute_noise_level = None
|
|
84
87
|
_rust_sc_weight_fw = None
|
|
88
|
+
_rust_sc_weight_fw_with_convergence = None
|
|
89
|
+
_rust_sc_weight_fw_weighted = None
|
|
90
|
+
_rust_sc_weight_fw_weighted_with_convergence = None
|
|
85
91
|
_rust_backend_info = None
|
|
86
92
|
elif _backend_env == "rust":
|
|
87
93
|
# Force Rust mode - fail if not available
|
|
@@ -115,7 +121,6 @@ __all__ = [
|
|
|
115
121
|
"HAS_RUST_BACKEND",
|
|
116
122
|
"rust_backend_info",
|
|
117
123
|
"_rust_bootstrap_weights",
|
|
118
|
-
"_rust_synthetic_weights",
|
|
119
124
|
"_rust_project_simplex",
|
|
120
125
|
"_rust_solve_ols",
|
|
121
126
|
"_rust_compute_robust_vcov",
|
|
@@ -131,4 +136,7 @@ __all__ = [
|
|
|
131
136
|
"_rust_compute_time_weights",
|
|
132
137
|
"_rust_compute_noise_level",
|
|
133
138
|
"_rust_sc_weight_fw",
|
|
139
|
+
"_rust_sc_weight_fw_with_convergence",
|
|
140
|
+
"_rust_sc_weight_fw_weighted",
|
|
141
|
+
"_rust_sc_weight_fw_weighted_with_convergence",
|
|
134
142
|
]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Runtime accessor for bundled LLM guide files."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
from importlib.resources import files
|
|
@@ -7,6 +8,7 @@ _VARIANT_TO_FILE = {
|
|
|
7
8
|
"concise": "llms.txt",
|
|
8
9
|
"full": "llms-full.txt",
|
|
9
10
|
"practitioner": "llms-practitioner.txt",
|
|
11
|
+
"autonomous": "llms-autonomous.txt",
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
|
|
@@ -21,6 +23,10 @@ def get_llm_guide(variant: str = "concise") -> str:
|
|
|
21
23
|
- ``"concise"`` -- compact API reference (llms.txt)
|
|
22
24
|
- ``"full"`` -- complete API documentation (llms-full.txt)
|
|
23
25
|
- ``"practitioner"`` -- 8-step practitioner workflow (llms-practitioner.txt)
|
|
26
|
+
- ``"autonomous"`` -- reference guide for AI-agent use: estimator-support
|
|
27
|
+
matrix, per-design-feature reasoning, post-fit validation index, and
|
|
28
|
+
BR/DR schema (llms-autonomous.txt). Pair with
|
|
29
|
+
:func:`diff_diff.profile_panel` for pre-fit data description.
|
|
24
30
|
|
|
25
31
|
Returns
|
|
26
32
|
-------
|
|
@@ -42,7 +48,5 @@ def get_llm_guide(variant: str = "concise") -> str:
|
|
|
42
48
|
filename = _VARIANT_TO_FILE[variant]
|
|
43
49
|
except (KeyError, TypeError):
|
|
44
50
|
valid = ", ".join(repr(k) for k in _VARIANT_TO_FILE)
|
|
45
|
-
raise ValueError(
|
|
46
|
-
f"Unknown guide variant {variant!r}. Valid options: {valid}."
|
|
47
|
-
) from None
|
|
51
|
+
raise ValueError(f"Unknown guide variant {variant!r}. Valid options: {valid}.") from None
|
|
48
52
|
return files("diff_diff.guides").joinpath(filename).read_text(encoding="utf-8")
|