diffindiff 2.5.8__tar.gz → 2.5.10__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.
- {diffindiff-2.5.8 → diffindiff-2.5.10}/PKG-INFO +9 -9
- {diffindiff-2.5.8 → diffindiff-2.5.10}/README.md +8 -8
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff/config.py +3 -3
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff/didanalysis.py +33 -15
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff/didanalysis_helper.py +55 -5
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff/diddata.py +4 -4
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff/didtools.py +65 -34
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff/tests/tests_diffindiff.py +2 -2
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff.egg-info/PKG-INFO +9 -9
- {diffindiff-2.5.8 → diffindiff-2.5.10}/setup.py +1 -1
- {diffindiff-2.5.8 → diffindiff-2.5.10}/MANIFEST.in +0 -0
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff/__init__.py +0 -0
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff/tests/__init__.py +0 -0
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff/tests/data/Corona_Hesse.xlsx +0 -0
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff/tests/data/counties_DE.csv +0 -0
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff/tests/data/curfew_DE.csv +0 -0
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff.egg-info/SOURCES.txt +0 -0
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff.egg-info/dependency_links.txt +0 -0
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff.egg-info/requires.txt +0 -0
- {diffindiff-2.5.8 → diffindiff-2.5.10}/diffindiff.egg-info/top_level.txt +0 -0
- {diffindiff-2.5.8 → diffindiff-2.5.10}/setup.cfg +0 -0
- {diffindiff-2.5.8 → diffindiff-2.5.10}/tests/test_diffindiff.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diffindiff
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.10
|
|
4
4
|
Summary: diffindiff: Python library for convenient Difference-in-Differences analyses
|
|
5
5
|
Author: Thomas Wieland
|
|
6
6
|
Author-email: geowieland@googlemail.com
|
|
@@ -26,6 +26,8 @@ Dynamic: summary
|
|
|
26
26
|
|
|
27
27
|
# diffindiff: Python library for convenient Difference-in-Differences analyses
|
|
28
28
|
|
|
29
|
+
 
|
|
30
|
+
|
|
29
31
|
This Python library is designed for performing Difference-in-Differences (DiD) analyses in a convenient way. The package is intended to be used in econometric analyses of natural experiments by researchers in economics, marketing, economic geography, and health sciences. It is designed to cover the entire workflow of a DiD analysis, while not requiring extensive programming skills. The package allows users to construct datasets, define treatment and control groups, and set treatment periods. DiD model analyses may be conducted with both datasets created by built-in functions and ready-to-use external datasets. Both simultaneous and staggered adoption are supported. The library allows for various extensions, such as two-way fixed effects models, group- or individual-specific effects, post-treatment periods, and triple-difference estimations. Additionally, it includes functions for visualizing results, such as plotting DiD coefficients with confidence intervals and illustrating the temporal evolution of staggered treatments. Furthermore, several functions for rigorous treatment configuration and data diagnostics are incorporated.
|
|
30
32
|
|
|
31
33
|
|
|
@@ -47,7 +49,7 @@ A case study that utilizes the diffindiff library (preprint) is available on [ar
|
|
|
47
49
|
|
|
48
50
|
If you use this software, please cite:
|
|
49
51
|
|
|
50
|
-
Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.
|
|
52
|
+
Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.10) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
## Installation
|
|
@@ -198,12 +200,10 @@ See the /tests directory for usage examples of most of the included functions.
|
|
|
198
200
|
This software was developed without the use of AI-generated code. The GitHub Copilot Chat in Microsoft Visual Studio Code using the GPT-5 mini model (by OpenAI) was used solely to assist in drafting and refining docstrings for documentation. The corresponding guidelines and constraints defined by the author are documented in `AGENTS-docstrings.md` in the [public GitHub repository](https://github.com/geowieland/diffindiff_official).
|
|
199
201
|
|
|
200
202
|
|
|
201
|
-
## What's new (v2.5.
|
|
203
|
+
## What's new (v2.5.10)
|
|
202
204
|
|
|
203
205
|
- Bugfixes
|
|
204
|
-
- didtools.
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
- Other:
|
|
209
|
-
- verbose option in any function which prints NOTEs, with all NOTEs being disabled when verbose = False
|
|
206
|
+
- Fixed some problems in `didtools.fit_metrics()` which relate to edge cases (e.g., no valid observations)
|
|
207
|
+
- Other
|
|
208
|
+
- Included deprecation warning for `didtools.model_wrapper()`
|
|
209
|
+
- Updates and corrections of docstring documentations
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# diffindiff: Python library for convenient Difference-in-Differences analyses
|
|
2
2
|
|
|
3
|
+
 
|
|
4
|
+
|
|
3
5
|
This Python library is designed for performing Difference-in-Differences (DiD) analyses in a convenient way. The package is intended to be used in econometric analyses of natural experiments by researchers in economics, marketing, economic geography, and health sciences. It is designed to cover the entire workflow of a DiD analysis, while not requiring extensive programming skills. The package allows users to construct datasets, define treatment and control groups, and set treatment periods. DiD model analyses may be conducted with both datasets created by built-in functions and ready-to-use external datasets. Both simultaneous and staggered adoption are supported. The library allows for various extensions, such as two-way fixed effects models, group- or individual-specific effects, post-treatment periods, and triple-difference estimations. Additionally, it includes functions for visualizing results, such as plotting DiD coefficients with confidence intervals and illustrating the temporal evolution of staggered treatments. Furthermore, several functions for rigorous treatment configuration and data diagnostics are incorporated.
|
|
4
6
|
|
|
5
7
|
|
|
@@ -21,7 +23,7 @@ A case study that utilizes the diffindiff library (preprint) is available on [ar
|
|
|
21
23
|
|
|
22
24
|
If you use this software, please cite:
|
|
23
25
|
|
|
24
|
-
Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.
|
|
26
|
+
Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.10) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
## Installation
|
|
@@ -172,12 +174,10 @@ See the /tests directory for usage examples of most of the included functions.
|
|
|
172
174
|
This software was developed without the use of AI-generated code. The GitHub Copilot Chat in Microsoft Visual Studio Code using the GPT-5 mini model (by OpenAI) was used solely to assist in drafting and refining docstrings for documentation. The corresponding guidelines and constraints defined by the author are documented in `AGENTS-docstrings.md` in the [public GitHub repository](https://github.com/geowieland/diffindiff_official).
|
|
173
175
|
|
|
174
176
|
|
|
175
|
-
## What's new (v2.5.
|
|
177
|
+
## What's new (v2.5.10)
|
|
176
178
|
|
|
177
179
|
- Bugfixes
|
|
178
|
-
- didtools.
|
|
179
|
-
|
|
180
|
-
-
|
|
181
|
-
-
|
|
182
|
-
- Other:
|
|
183
|
-
- verbose option in any function which prints NOTEs, with all NOTEs being disabled when verbose = False
|
|
180
|
+
- Fixed some problems in `didtools.fit_metrics()` which relate to edge cases (e.g., no valid observations)
|
|
181
|
+
- Other
|
|
182
|
+
- Included deprecation warning for `didtools.model_wrapper()`
|
|
183
|
+
- Updates and corrections of docstring documentations
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
# Author: Thomas Wieland
|
|
5
5
|
# ORCID: 0000-0001-5168-9846
|
|
6
6
|
# mail: geowieland@googlemail.com
|
|
7
|
-
# Version: 1.0.
|
|
8
|
-
# Last update: 2026-
|
|
7
|
+
# Version: 1.0.31
|
|
8
|
+
# Last update: 2026-11-14 09:55
|
|
9
9
|
# Copyright (c) 2025-2026 Thomas Wieland
|
|
10
10
|
#-----------------------------------------------------------------------
|
|
11
11
|
|
|
12
12
|
# Basic config:
|
|
13
13
|
|
|
14
14
|
PACKAGE_NAME = "diffindiff"
|
|
15
|
-
PACKAGE_VERSION = "2.5.
|
|
15
|
+
PACKAGE_VERSION = "2.5.10"
|
|
16
16
|
|
|
17
17
|
VERBOSE = False
|
|
18
18
|
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
# Author: Thomas Wieland
|
|
5
5
|
# ORCID: 0000-0001-5168-9846
|
|
6
6
|
# mail: geowieland@googlemail.com
|
|
7
|
-
# Version: 2.4.
|
|
8
|
-
# Last update: 2026-
|
|
7
|
+
# Version: 2.4.10
|
|
8
|
+
# Last update: 2026-11-14 09:53
|
|
9
9
|
# Copyright (c) 2024-2026 Thomas Wieland
|
|
10
10
|
#-----------------------------------------------------------------------
|
|
11
11
|
|
|
@@ -45,6 +45,8 @@ class DiffModel:
|
|
|
45
45
|
Predicted values of the DiD model.
|
|
46
46
|
did_model: statsmodels regression results object
|
|
47
47
|
The fitted DiD model object from statsmodels.
|
|
48
|
+
timestamp : dict
|
|
49
|
+
Metadata identifying the package version, function and creation time.
|
|
48
50
|
|
|
49
51
|
Returns
|
|
50
52
|
-------
|
|
@@ -124,6 +126,8 @@ class DiffModel:
|
|
|
124
126
|
Treatment name to analyse. If None, the first treatment in the model config is chosen.
|
|
125
127
|
after_treatment_col : str, optional
|
|
126
128
|
Column name of an after-treatment indicator, if present.
|
|
129
|
+
verbose : bool, optional
|
|
130
|
+
If True, print progress messages.
|
|
127
131
|
|
|
128
132
|
Returns
|
|
129
133
|
-------
|
|
@@ -1122,6 +1126,8 @@ class DiffModel:
|
|
|
1122
1126
|
retransform_log_outcome : bool, optional
|
|
1123
1127
|
If outcome was log-transformed, retransform to original scale for plotting.
|
|
1124
1128
|
Default is False.
|
|
1129
|
+
verbose : bool, optional
|
|
1130
|
+
If True, print progress messages.
|
|
1125
1131
|
|
|
1126
1132
|
Returns
|
|
1127
1133
|
-------
|
|
@@ -1334,6 +1340,8 @@ class DiffModel:
|
|
|
1334
1340
|
----------
|
|
1335
1341
|
treatment : str, optional
|
|
1336
1342
|
Treatment name to use for placebo assignment.
|
|
1343
|
+
after_treatment_col : str, optional
|
|
1344
|
+
Column name identifying the after-treatment period.
|
|
1337
1345
|
TG_col : str, optional
|
|
1338
1346
|
Column name identifying treatment group.
|
|
1339
1347
|
TT_col : str, optional
|
|
@@ -1344,6 +1352,8 @@ class DiffModel:
|
|
|
1344
1352
|
Fraction of the divided group to sample when creating placebo units.
|
|
1345
1353
|
random_state : int, optional
|
|
1346
1354
|
Seed for random sampling.
|
|
1355
|
+
verbose : bool, optional
|
|
1356
|
+
If True, print progress messages.
|
|
1347
1357
|
|
|
1348
1358
|
Returns
|
|
1349
1359
|
-------
|
|
@@ -1528,6 +1538,8 @@ class DiffModel:
|
|
|
1528
1538
|
If not none, filename of plot to be saved.
|
|
1529
1539
|
save_fig_kwargs : dict, optional
|
|
1530
1540
|
Optional arguments for plt.savefig() if save_fig is not None.
|
|
1541
|
+
verbose : bool, optional
|
|
1542
|
+
If True, print progress messages.
|
|
1531
1543
|
|
|
1532
1544
|
Returns
|
|
1533
1545
|
-------
|
|
@@ -1686,6 +1698,8 @@ class DiffModel:
|
|
|
1686
1698
|
If not none, filename of plot to be saved.
|
|
1687
1699
|
save_fig_kwargs : dict, optional
|
|
1688
1700
|
Optional arguments for plt.savefig() if save_fig is not None.
|
|
1701
|
+
verbose : bool, optional
|
|
1702
|
+
If True, print progress messages.
|
|
1689
1703
|
|
|
1690
1704
|
Returns
|
|
1691
1705
|
-------
|
|
@@ -1877,19 +1891,21 @@ class DiffModel:
|
|
|
1877
1891
|
outcome_col_expected_PI_lower_CG = f"{outcome_col}{config.DELIMITER}{config.EXPECTED_SUFFIX}{config.DELIMITER}{config.PI_LOWER_SUFFIX}{config.DELIMITER}{config.CG_COL}"
|
|
1878
1892
|
outcome_col_expected_PI_upper_CG = f"{outcome_col}{config.DELIMITER}{config.EXPECTED_SUFFIX}{config.DELIMITER}{config.PI_UPPER_SUFFIX}{config.DELIMITER}{config.CG_COL}"
|
|
1879
1893
|
|
|
1880
|
-
model_data_TG_CG.columns
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1894
|
+
model_data_TG_CG.columns = [
|
|
1895
|
+
"t",
|
|
1896
|
+
outcome_col_observed_TG,
|
|
1897
|
+
outcome_col_observed_CG,
|
|
1898
|
+
outcome_col_expected_TG,
|
|
1899
|
+
outcome_col_expected_CG,
|
|
1900
|
+
outcome_col_expected_CI_lower_TG,
|
|
1901
|
+
outcome_col_expected_CI_upper_TG,
|
|
1902
|
+
outcome_col_expected_CI_lower_CG,
|
|
1903
|
+
outcome_col_expected_CI_upper_CG,
|
|
1904
|
+
outcome_col_expected_PI_lower_TG,
|
|
1905
|
+
outcome_col_expected_PI_upper_TG,
|
|
1906
|
+
outcome_col_expected_PI_lower_CG,
|
|
1907
|
+
outcome_col_expected_PI_upper_CG,
|
|
1908
|
+
]
|
|
1893
1909
|
|
|
1894
1910
|
if plot_size_auto:
|
|
1895
1911
|
if model_config["pre_post"]:
|
|
@@ -2159,6 +2175,8 @@ class DiffModel:
|
|
|
2159
2175
|
If not none, filename of plot to be saved.
|
|
2160
2176
|
save_fig_kwargs : dict, optional
|
|
2161
2177
|
Optional arguments for plt.savefig() if save_fig is not None.
|
|
2178
|
+
verbose : bool, optional
|
|
2179
|
+
If True, print progress messages.
|
|
2162
2180
|
|
|
2163
2181
|
Returns
|
|
2164
2182
|
-------
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
# Author: Thomas Wieland
|
|
5
5
|
# ORCID: 0000-0001-5168-9846
|
|
6
6
|
# mail: geowieland@googlemail.com
|
|
7
|
-
# Version: 1.2.
|
|
8
|
-
# Last update: 2026-
|
|
7
|
+
# Version: 1.2.7
|
|
8
|
+
# Last update: 2026-11-14 09:52
|
|
9
9
|
# Copyright (c) 2025-2026 Thomas Wieland
|
|
10
10
|
#-----------------------------------------------------------------------
|
|
11
11
|
|
|
@@ -147,6 +147,14 @@ def demean_variables(
|
|
|
147
147
|
Names of spillover variables to demean (default: None).
|
|
148
148
|
interactions : list, optional
|
|
149
149
|
Names of interaction variables to demean (default: None).
|
|
150
|
+
TG_col : list, optional
|
|
151
|
+
Names of treatment-group indicator variables to demean.
|
|
152
|
+
TT_col : list, optional
|
|
153
|
+
Names of treatment-time indicator variables to demean.
|
|
154
|
+
after_treatment_col : list, optional
|
|
155
|
+
Names of after-treatment indicator variables to demean.
|
|
156
|
+
ATT_col : list, optional
|
|
157
|
+
Names of average treatment effect on the treated variables to demean.
|
|
150
158
|
FE_unit : bool, optional
|
|
151
159
|
If True, remove unit means (within-unit demeaning). Default is False.
|
|
152
160
|
FE_time : bool, optional
|
|
@@ -158,7 +166,7 @@ def demean_variables(
|
|
|
158
166
|
Convergence tolerance for iterative demeaning. Iteration stops when the
|
|
159
167
|
maximum absolute change across observations is below `tol`. Default is 1e-8.
|
|
160
168
|
verbose : bool, optional
|
|
161
|
-
If True, print progress
|
|
169
|
+
If True, print progress messages.
|
|
162
170
|
|
|
163
171
|
Returns
|
|
164
172
|
-------
|
|
@@ -1246,8 +1254,48 @@ def extract_model_results(
|
|
|
1246
1254
|
----------
|
|
1247
1255
|
fit_result : list
|
|
1248
1256
|
Result returned by ``ols_fit`` or ``ml_fit``.
|
|
1249
|
-
|
|
1250
|
-
|
|
1257
|
+
TG_col : list, optional
|
|
1258
|
+
Treatment-group variable names used to identify treatment-group coefficients.
|
|
1259
|
+
TT_col : list, optional
|
|
1260
|
+
Treatment-time variable names used to identify treatment-time coefficients.
|
|
1261
|
+
treatment_col : list, optional
|
|
1262
|
+
Treatment variable names used to identify treatment coefficients.
|
|
1263
|
+
after_treatment_col : list, optional
|
|
1264
|
+
After-treatment variable names used to identify after-treatment coefficients.
|
|
1265
|
+
ATT_col : list, optional
|
|
1266
|
+
Variable names used to identify average treatment effect on the treated coefficients.
|
|
1267
|
+
spillover_vars : list, optional
|
|
1268
|
+
Spillover variable names used to identify spillover coefficients.
|
|
1269
|
+
FE_unit_vars : list, optional
|
|
1270
|
+
Unit fixed-effect variable names used to identify fixed-effect coefficients.
|
|
1271
|
+
dummy_unit_original : list, optional
|
|
1272
|
+
Original unit identifiers corresponding to unit fixed-effect dummy variables.
|
|
1273
|
+
FE_time_vars : list, optional
|
|
1274
|
+
Time fixed-effect variable names used to identify fixed-effect coefficients.
|
|
1275
|
+
dummy_time_original : list, optional
|
|
1276
|
+
Original time identifiers corresponding to time fixed-effect dummy variables.
|
|
1277
|
+
FE_group_vars : list, optional
|
|
1278
|
+
Group fixed-effect variable names used to identify fixed-effect coefficients.
|
|
1279
|
+
dummy_group_original : list, optional
|
|
1280
|
+
Original group identifiers corresponding to group fixed-effect dummy variables.
|
|
1281
|
+
ITE_vars : list, optional
|
|
1282
|
+
Variable names used to identify individual treatment effect coefficients.
|
|
1283
|
+
GTE_vars : list, optional
|
|
1284
|
+
Variable names used to identify group treatment effect coefficients.
|
|
1285
|
+
ITT_vars : list, optional
|
|
1286
|
+
Variable names used to identify individual treatment-on-the-treated coefficients.
|
|
1287
|
+
GTT_vars : list, optional
|
|
1288
|
+
Variable names used to identify group treatment-on-the-treated coefficients.
|
|
1289
|
+
TG_x_BG_x_TT_col : list, optional
|
|
1290
|
+
Variable names used to identify treatment-group, background-group and treatment-time interaction coefficients.
|
|
1291
|
+
BG_col : list, optional
|
|
1292
|
+
Background-group variable names used to identify background-group coefficients.
|
|
1293
|
+
TG_x_BG_col : list, optional
|
|
1294
|
+
Variable names used to identify treatment-group and background-group interaction coefficients.
|
|
1295
|
+
BG_x_TT_col : list, optional
|
|
1296
|
+
Variable names used to identify background-group and treatment-time interaction coefficients.
|
|
1297
|
+
covariates : list, optional
|
|
1298
|
+
Covariate names used to identify covariate coefficients.
|
|
1251
1299
|
verbose : bool, optional
|
|
1252
1300
|
If True, print progress messages.
|
|
1253
1301
|
|
|
@@ -1831,6 +1879,8 @@ def remove_duplicates_from_dict(
|
|
|
1831
1879
|
----------
|
|
1832
1880
|
any_dict : dict
|
|
1833
1881
|
Dictionary from which to remove duplicates.
|
|
1882
|
+
ignore_key : any, optional
|
|
1883
|
+
Dictionary key to exclude when comparing values for duplicates.
|
|
1834
1884
|
|
|
1835
1885
|
Returns
|
|
1836
1886
|
-------
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
# Author: Thomas Wieland
|
|
5
5
|
# ORCID: 0000-0001-5168-9846
|
|
6
6
|
# mail: geowieland@googlemail.com
|
|
7
|
-
# Version: 2.3.
|
|
8
|
-
# Last update: 2026-
|
|
7
|
+
# Version: 2.3.6
|
|
8
|
+
# Last update: 2026-11-14 11:13
|
|
9
9
|
# Copyright (c) 2024-2026 Thomas Wieland
|
|
10
10
|
#-----------------------------------------------------------------------
|
|
11
11
|
|
|
@@ -2958,12 +2958,12 @@ def create_counterfactual(
|
|
|
2958
2958
|
verbose = verbose
|
|
2959
2959
|
)
|
|
2960
2960
|
|
|
2961
|
-
model_estimation_error = counterfactual_pred[
|
|
2961
|
+
model_estimation_error = counterfactual_pred[5]
|
|
2962
2962
|
model_estimation_error_text = ""
|
|
2963
2963
|
|
|
2964
2964
|
if model_estimation_error:
|
|
2965
2965
|
|
|
2966
|
-
model_estimation_error_text = counterfactual_pred[
|
|
2966
|
+
model_estimation_error_text = counterfactual_pred[6]
|
|
2967
2967
|
print(f"WARNING: The desired {config.MODEL_WRAPPER_AVAILABLE[model_type]} model was not estimated. The following error occurred: '{model_estimation_error_text}'.")
|
|
2968
2968
|
|
|
2969
2969
|
else:
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
# Author: Thomas Wieland
|
|
5
5
|
# ORCID: 0000-0001-5168-9846
|
|
6
6
|
# mail: geowieland@googlemail.com
|
|
7
|
-
# Version: 2.2.
|
|
8
|
-
# Last update: 2026-
|
|
7
|
+
# Version: 2.2.10
|
|
8
|
+
# Last update: 2026-11-14 11:18
|
|
9
9
|
# Copyright (c) 2025-2026 Thomas Wieland
|
|
10
10
|
#-----------------------------------------------------------------------
|
|
11
11
|
|
|
@@ -25,6 +25,7 @@ from sklearn.preprocessing import StandardScaler
|
|
|
25
25
|
from sklearn.linear_model import LinearRegression
|
|
26
26
|
from sklearn.model_selection import train_test_split
|
|
27
27
|
from sklearn.neural_network import MLPRegressor
|
|
28
|
+
import warnings
|
|
28
29
|
|
|
29
30
|
try:
|
|
30
31
|
from xgboost import XGBRegressor
|
|
@@ -1402,7 +1403,6 @@ def model_wrapper(
|
|
|
1402
1403
|
):
|
|
1403
1404
|
|
|
1404
1405
|
"""
|
|
1405
|
-
|
|
1406
1406
|
Train a machine learning or OLS regression model and return predictions and metrics.
|
|
1407
1407
|
|
|
1408
1408
|
Parameters
|
|
@@ -1430,6 +1430,14 @@ def model_wrapper(
|
|
|
1430
1430
|
using this as the class labels.
|
|
1431
1431
|
Passed to `sklearn.model_selection.train_test_split`;
|
|
1432
1432
|
see the corresponding documentation.
|
|
1433
|
+
X_train : array-like, optional
|
|
1434
|
+
User-defined training feature data. Supply together with the other training and testing subsets.
|
|
1435
|
+
X_test : array-like, optional
|
|
1436
|
+
User-defined testing feature data. Supply together with the other training and testing subsets.
|
|
1437
|
+
y_train : array-like, optional
|
|
1438
|
+
User-defined training target values. Supply together with the other training and testing subsets.
|
|
1439
|
+
y_test : array-like, optional
|
|
1440
|
+
User-defined testing target values. Supply together with the other training and testing subsets.
|
|
1433
1441
|
model_n_estimators : int, optional
|
|
1434
1442
|
The number of estimators in the ensemble.
|
|
1435
1443
|
Passed to `BaggingRegressor` and `RandomForestRegressor`;
|
|
@@ -1507,6 +1515,12 @@ def model_wrapper(
|
|
|
1507
1515
|
>>> model_wrapper(y, X, model_type='ols')
|
|
1508
1516
|
"""
|
|
1509
1517
|
|
|
1518
|
+
warnings.warn(
|
|
1519
|
+
"didtools.model_wrapper() is deprecated and will be replaced in a future version.",
|
|
1520
|
+
DeprecationWarning,
|
|
1521
|
+
stacklevel=2
|
|
1522
|
+
)
|
|
1523
|
+
|
|
1510
1524
|
if model_type not in config.MODEL_WRAPPER_AVAILABLE_LIST:
|
|
1511
1525
|
raise ValueError(f"Please enter a valid model type: {', '.join(config.MODEL_WRAPPER_AVAILABLE_LIST)}.")
|
|
1512
1526
|
|
|
@@ -1618,6 +1632,7 @@ def model_wrapper(
|
|
|
1618
1632
|
random_state = random_state
|
|
1619
1633
|
)
|
|
1620
1634
|
else:
|
|
1635
|
+
model = {}
|
|
1621
1636
|
model_estimation_error = True
|
|
1622
1637
|
model_estimation_error_text = "XGBRegressor is not available. Please install xgboost to use this model type."
|
|
1623
1638
|
|
|
@@ -1630,6 +1645,7 @@ def model_wrapper(
|
|
|
1630
1645
|
random_state = random_state
|
|
1631
1646
|
)
|
|
1632
1647
|
else:
|
|
1648
|
+
model = {}
|
|
1633
1649
|
model_estimation_error = True
|
|
1634
1650
|
model_estimation_error_text = "LGBMRegressor is not available. Please install lightgbm to use this model type."
|
|
1635
1651
|
|
|
@@ -1656,7 +1672,8 @@ def model_wrapper(
|
|
|
1656
1672
|
]
|
|
1657
1673
|
)
|
|
1658
1674
|
|
|
1659
|
-
model
|
|
1675
|
+
if model != {}:
|
|
1676
|
+
model.fit(X_train, y_train)
|
|
1660
1677
|
|
|
1661
1678
|
except Exception as e:
|
|
1662
1679
|
|
|
@@ -1763,10 +1780,10 @@ def fit_metrics(
|
|
|
1763
1780
|
|
|
1764
1781
|
Raises
|
|
1765
1782
|
------
|
|
1766
|
-
AssertionError
|
|
1767
|
-
If observed and expected differ in length.
|
|
1768
1783
|
ValueError
|
|
1769
|
-
If observed and
|
|
1784
|
+
If observed and expected differ in length, or contain NaNs when remove_nan is False, or no observations are left after removing NaNs.
|
|
1785
|
+
TypeError
|
|
1786
|
+
If observed and/or expected are not numeric.
|
|
1770
1787
|
|
|
1771
1788
|
Examples
|
|
1772
1789
|
--------
|
|
@@ -1776,21 +1793,23 @@ def fit_metrics(
|
|
|
1776
1793
|
observed_no = len(observed)
|
|
1777
1794
|
expected_no = len(expected)
|
|
1778
1795
|
|
|
1779
|
-
|
|
1780
|
-
|
|
1796
|
+
if observed_no != expected_no:
|
|
1797
|
+
raise ValueError("Error while calculating fit metrics: Observed and expected differ in length")
|
|
1798
|
+
|
|
1781
1799
|
type_errors = []
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
except:
|
|
1791
|
-
type_errors.append("Expected column is not numeric.")
|
|
1800
|
+
try:
|
|
1801
|
+
observed = pd.to_numeric(observed)
|
|
1802
|
+
except:
|
|
1803
|
+
type_errors.append("Observed column is not numeric.")
|
|
1804
|
+
try:
|
|
1805
|
+
expected = pd.to_numeric(expected)
|
|
1806
|
+
except:
|
|
1807
|
+
type_errors.append("Expected column is not numeric.")
|
|
1792
1808
|
if len(type_errors) > 0:
|
|
1793
1809
|
raise TypeError(f"Error(s) while calculating fit metrics: {', '.join(type_errors)}")
|
|
1810
|
+
|
|
1811
|
+
observed = pd.Series(observed).to_numpy(dtype=np.float64, na_value=np.nan)
|
|
1812
|
+
expected = pd.Series(expected).to_numpy(dtype=np.float64, na_value=np.nan)
|
|
1794
1813
|
|
|
1795
1814
|
if outcome_col is not None and isinstance(outcome_col, str):
|
|
1796
1815
|
outcome_observed_col = f"{outcome_col}{config.DELIMITER}{config.OBSERVED_SUFFIX}"
|
|
@@ -1798,18 +1817,19 @@ def fit_metrics(
|
|
|
1798
1817
|
else:
|
|
1799
1818
|
outcome_observed_col = f"outcome{config.DELIMITER}{config.OBSERVED_SUFFIX}"
|
|
1800
1819
|
outcome_expected_col = f"outcome{config.DELIMITER}{config.EXPECTED_SUFFIX}"
|
|
1820
|
+
|
|
1821
|
+
obs_exp = pd.DataFrame(
|
|
1822
|
+
{
|
|
1823
|
+
outcome_observed_col: observed,
|
|
1824
|
+
outcome_expected_col: expected
|
|
1825
|
+
}
|
|
1826
|
+
)
|
|
1827
|
+
observations_input = len(obs_exp)
|
|
1801
1828
|
|
|
1802
1829
|
if remove_nan:
|
|
1803
1830
|
|
|
1804
1831
|
observed = np.array(observed)
|
|
1805
1832
|
expected = np.array(expected)
|
|
1806
|
-
|
|
1807
|
-
obs_exp = pd.DataFrame(
|
|
1808
|
-
{
|
|
1809
|
-
outcome_observed_col: observed,
|
|
1810
|
-
outcome_expected_col: expected
|
|
1811
|
-
}
|
|
1812
|
-
)
|
|
1813
1833
|
|
|
1814
1834
|
obs_exp_clean = obs_exp.dropna(
|
|
1815
1835
|
subset=[
|
|
@@ -1820,6 +1840,9 @@ def fit_metrics(
|
|
|
1820
1840
|
|
|
1821
1841
|
observed = obs_exp_clean[outcome_observed_col].to_numpy()
|
|
1822
1842
|
expected = obs_exp_clean[outcome_expected_col].to_numpy()
|
|
1843
|
+
|
|
1844
|
+
observed_no = len(observed)
|
|
1845
|
+
expected_no = len(expected)
|
|
1823
1846
|
|
|
1824
1847
|
else:
|
|
1825
1848
|
|
|
@@ -1830,12 +1853,15 @@ def fit_metrics(
|
|
|
1830
1853
|
value_errors.append("Expected data contains NaNs.")
|
|
1831
1854
|
if len(value_errors) > 0:
|
|
1832
1855
|
raise ValueError(f"Error(s) while calculating fit metrics: {' '.join(value_errors)} Parameter 'remove_nan' is False.")
|
|
1833
|
-
|
|
1856
|
+
|
|
1857
|
+
observations = len(observed)
|
|
1858
|
+
|
|
1859
|
+
if observations == 0:
|
|
1860
|
+
raise ValueError("No observations left for calculating model fit metrics.")
|
|
1861
|
+
|
|
1834
1862
|
if verbose:
|
|
1835
1863
|
print("Calculating model fit metrics", end = " ... ")
|
|
1836
1864
|
|
|
1837
|
-
observations = len(observed)
|
|
1838
|
-
|
|
1839
1865
|
residuals = np.array(observed)-np.array(expected)
|
|
1840
1866
|
residuals_sq = residuals**2
|
|
1841
1867
|
residuals_abs = abs(residuals)
|
|
@@ -1870,7 +1896,11 @@ def fit_metrics(
|
|
|
1870
1896
|
SAR = float(np.sum(residuals_abs))
|
|
1871
1897
|
observed_mean = float(np.sum(observed)/observed_no)
|
|
1872
1898
|
SQT = float(np.sum((observed-observed_mean)**2))
|
|
1873
|
-
|
|
1899
|
+
|
|
1900
|
+
RSQ = np.nan
|
|
1901
|
+
if SQT > 0:
|
|
1902
|
+
RSQ = float(1-(SSR/SQT))
|
|
1903
|
+
|
|
1874
1904
|
MSE = float(SSR/observed_no)
|
|
1875
1905
|
RMSE = float(np.sqrt(MSE))
|
|
1876
1906
|
MAE = float(SAR/observed_no)
|
|
@@ -1881,8 +1911,8 @@ def fit_metrics(
|
|
|
1881
1911
|
try:
|
|
1882
1912
|
indep_vars_no = int(indep_vars_no)
|
|
1883
1913
|
RSQ_ADJ = (1-(1-RSQ)*((observations-1)/(observations-indep_vars_no-1)))
|
|
1884
|
-
except:
|
|
1885
|
-
|
|
1914
|
+
except Exception as e:
|
|
1915
|
+
print(f"WARNING: Adj. R-squared can not be computed: {str(e)}")
|
|
1886
1916
|
|
|
1887
1917
|
model_fit_metrics = {
|
|
1888
1918
|
list(config.MODEL_FIT_METRICS.keys())[0]: SSR,
|
|
@@ -1905,8 +1935,9 @@ def fit_metrics(
|
|
|
1905
1935
|
if RSQ_ADJ == np.nan:
|
|
1906
1936
|
print("NOTE: As no number of independent vars was stated, no Adj. R-Squared is calculated.")
|
|
1907
1937
|
|
|
1908
|
-
|
|
1909
|
-
|
|
1938
|
+
if observations < observations_input:
|
|
1939
|
+
observations_input_nan = observations_input-observations
|
|
1940
|
+
print(f"WARNING: {observations_input_nan} pairs of observed and expected were removed due to NaN values.")
|
|
1910
1941
|
|
|
1911
1942
|
modelfit_results = [
|
|
1912
1943
|
model_residuals,
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
# Author: Thomas Wieland
|
|
5
5
|
# ORCID: 0000-0001-5168-9846
|
|
6
6
|
# mail: geowieland@googlemail.com
|
|
7
|
-
# Version: 2.2.
|
|
8
|
-
# Last update: 2026-
|
|
7
|
+
# Version: 2.2.2
|
|
8
|
+
# Last update: 2026-11-14 11:14
|
|
9
9
|
# Copyright (c) 2025-2026 Thomas Wieland
|
|
10
10
|
#-----------------------------------------------------------------------
|
|
11
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diffindiff
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.10
|
|
4
4
|
Summary: diffindiff: Python library for convenient Difference-in-Differences analyses
|
|
5
5
|
Author: Thomas Wieland
|
|
6
6
|
Author-email: geowieland@googlemail.com
|
|
@@ -26,6 +26,8 @@ Dynamic: summary
|
|
|
26
26
|
|
|
27
27
|
# diffindiff: Python library for convenient Difference-in-Differences analyses
|
|
28
28
|
|
|
29
|
+
 
|
|
30
|
+
|
|
29
31
|
This Python library is designed for performing Difference-in-Differences (DiD) analyses in a convenient way. The package is intended to be used in econometric analyses of natural experiments by researchers in economics, marketing, economic geography, and health sciences. It is designed to cover the entire workflow of a DiD analysis, while not requiring extensive programming skills. The package allows users to construct datasets, define treatment and control groups, and set treatment periods. DiD model analyses may be conducted with both datasets created by built-in functions and ready-to-use external datasets. Both simultaneous and staggered adoption are supported. The library allows for various extensions, such as two-way fixed effects models, group- or individual-specific effects, post-treatment periods, and triple-difference estimations. Additionally, it includes functions for visualizing results, such as plotting DiD coefficients with confidence intervals and illustrating the temporal evolution of staggered treatments. Furthermore, several functions for rigorous treatment configuration and data diagnostics are incorporated.
|
|
30
32
|
|
|
31
33
|
|
|
@@ -47,7 +49,7 @@ A case study that utilizes the diffindiff library (preprint) is available on [ar
|
|
|
47
49
|
|
|
48
50
|
If you use this software, please cite:
|
|
49
51
|
|
|
50
|
-
Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.
|
|
52
|
+
Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.10) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
## Installation
|
|
@@ -198,12 +200,10 @@ See the /tests directory for usage examples of most of the included functions.
|
|
|
198
200
|
This software was developed without the use of AI-generated code. The GitHub Copilot Chat in Microsoft Visual Studio Code using the GPT-5 mini model (by OpenAI) was used solely to assist in drafting and refining docstrings for documentation. The corresponding guidelines and constraints defined by the author are documented in `AGENTS-docstrings.md` in the [public GitHub repository](https://github.com/geowieland/diffindiff_official).
|
|
199
201
|
|
|
200
202
|
|
|
201
|
-
## What's new (v2.5.
|
|
203
|
+
## What's new (v2.5.10)
|
|
202
204
|
|
|
203
205
|
- Bugfixes
|
|
204
|
-
- didtools.
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
- Other:
|
|
209
|
-
- verbose option in any function which prints NOTEs, with all NOTEs being disabled when verbose = False
|
|
206
|
+
- Fixed some problems in `didtools.fit_metrics()` which relate to edge cases (e.g., no valid observations)
|
|
207
|
+
- Other
|
|
208
|
+
- Included deprecation warning for `didtools.model_wrapper()`
|
|
209
|
+
- Updates and corrections of docstring documentations
|
|
@@ -7,7 +7,7 @@ def read_README():
|
|
|
7
7
|
|
|
8
8
|
setup(
|
|
9
9
|
name='diffindiff',
|
|
10
|
-
version='2.5.
|
|
10
|
+
version='2.5.10',
|
|
11
11
|
description='diffindiff: Python library for convenient Difference-in-Differences analyses',
|
|
12
12
|
packages=find_packages(include=["diffindiff", "diffindiff.tests"]),
|
|
13
13
|
include_package_data=True,
|
|
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
|