diffindiff 2.5.9__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.
Files changed (22) hide show
  1. {diffindiff-2.5.9 → diffindiff-2.5.10}/PKG-INFO +9 -6
  2. {diffindiff-2.5.9 → diffindiff-2.5.10}/README.md +8 -5
  3. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff/config.py +3 -3
  4. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff/didanalysis.py +18 -2
  5. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff/didanalysis_helper.py +55 -5
  6. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff/diddata.py +2 -2
  7. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff/didtools.py +56 -32
  8. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff/tests/tests_diffindiff.py +2 -2
  9. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff.egg-info/PKG-INFO +9 -6
  10. {diffindiff-2.5.9 → diffindiff-2.5.10}/setup.py +1 -1
  11. {diffindiff-2.5.9 → diffindiff-2.5.10}/MANIFEST.in +0 -0
  12. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff/__init__.py +0 -0
  13. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff/tests/__init__.py +0 -0
  14. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff/tests/data/Corona_Hesse.xlsx +0 -0
  15. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff/tests/data/counties_DE.csv +0 -0
  16. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff/tests/data/curfew_DE.csv +0 -0
  17. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff.egg-info/SOURCES.txt +0 -0
  18. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff.egg-info/dependency_links.txt +0 -0
  19. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff.egg-info/requires.txt +0 -0
  20. {diffindiff-2.5.9 → diffindiff-2.5.10}/diffindiff.egg-info/top_level.txt +0 -0
  21. {diffindiff-2.5.9 → diffindiff-2.5.10}/setup.cfg +0 -0
  22. {diffindiff-2.5.9 → 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.9
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
+ ![Version](https://img.shields.io/pypi/v/diffindiff) ![Downloads](https://static.pepy.tech/badge/diffindiff)
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.9) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
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,9 +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.9)
203
+ ## What's new (v2.5.10)
202
204
 
203
205
  - Bugfixes
204
- - Adjusting column naming to be compatible with newer pandas versions in `didanalysis.DiffModel.plot()`
205
- - Check in `didtools.fit_metrics()` whether total sum of squares is positive before calculating $$R^2$$
206
- - Incorrect access to counterfactual results in the event of model fit errors or model unavailability has been fixed in `diddata.create_counterfactual()`
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
+ ![Version](https://img.shields.io/pypi/v/diffindiff) ![Downloads](https://static.pepy.tech/badge/diffindiff)
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.9) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
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,9 +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.9)
177
+ ## What's new (v2.5.10)
176
178
 
177
179
  - Bugfixes
178
- - Adjusting column naming to be compatible with newer pandas versions in `didanalysis.DiffModel.plot()`
179
- - Check in `didtools.fit_metrics()` whether total sum of squares is positive before calculating $$R^2$$
180
- - Incorrect access to counterfactual results in the event of model fit errors or model unavailability has been fixed in `diddata.create_counterfactual()`
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.30
8
- # Last update: 2026-10-24 09:29
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.9"
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.9
8
- # Last update: 2026-10-24 09:30
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
  -------
@@ -2161,6 +2175,8 @@ class DiffModel:
2161
2175
  If not none, filename of plot to be saved.
2162
2176
  save_fig_kwargs : dict, optional
2163
2177
  Optional arguments for plt.savefig() if save_fig is not None.
2178
+ verbose : bool, optional
2179
+ If True, print progress messages.
2164
2180
 
2165
2181
  Returns
2166
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.6
8
- # Last update: 2026-10-17 18:55
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 and informational messages (default: config.VERBOSE).
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
- Other parameters
1250
- Lists of variable names used to identify coefficients (TG_col, TT_col, etc.).
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.5
8
- # Last update: 2026-10-30 21:20
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
 
@@ -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.9
8
- # Last update: 2026-10-30 21:20
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
 
@@ -1766,10 +1780,10 @@ def fit_metrics(
1766
1780
 
1767
1781
  Raises
1768
1782
  ------
1769
- AssertionError
1770
- If observed and expected differ in length.
1771
1783
  ValueError
1772
- If observed and/or expected are not numeric, or contain NaNs when remove_nan is False.
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.
1773
1787
 
1774
1788
  Examples
1775
1789
  --------
@@ -1779,21 +1793,23 @@ def fit_metrics(
1779
1793
  observed_no = len(observed)
1780
1794
  expected_no = len(expected)
1781
1795
 
1782
- assert observed_no == expected_no, "Error while calculating fit metrics: Observed and expected differ in length"
1783
-
1796
+ if observed_no != expected_no:
1797
+ raise ValueError("Error while calculating fit metrics: Observed and expected differ in length")
1798
+
1784
1799
  type_errors = []
1785
- if not pd.api.types.is_numeric_dtype(observed) or not np.issubdtype(observed.dtype, np.number):
1786
- try:
1787
- observed = pd.to_numeric(observed)
1788
- except:
1789
- type_errors.append("Observed column is not numeric.")
1790
- if not pd.api.types.is_numeric_dtype(expected) or not np.issubdtype(expected.dtype, np.number):
1791
- try:
1792
- expected = pd.to_numeric(expected)
1793
- except:
1794
- 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.")
1795
1808
  if len(type_errors) > 0:
1796
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)
1797
1813
 
1798
1814
  if outcome_col is not None and isinstance(outcome_col, str):
1799
1815
  outcome_observed_col = f"{outcome_col}{config.DELIMITER}{config.OBSERVED_SUFFIX}"
@@ -1801,18 +1817,19 @@ def fit_metrics(
1801
1817
  else:
1802
1818
  outcome_observed_col = f"outcome{config.DELIMITER}{config.OBSERVED_SUFFIX}"
1803
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)
1804
1828
 
1805
1829
  if remove_nan:
1806
1830
 
1807
1831
  observed = np.array(observed)
1808
1832
  expected = np.array(expected)
1809
-
1810
- obs_exp = pd.DataFrame(
1811
- {
1812
- outcome_observed_col: observed,
1813
- outcome_expected_col: expected
1814
- }
1815
- )
1816
1833
 
1817
1834
  obs_exp_clean = obs_exp.dropna(
1818
1835
  subset=[
@@ -1823,6 +1840,9 @@ def fit_metrics(
1823
1840
 
1824
1841
  observed = obs_exp_clean[outcome_observed_col].to_numpy()
1825
1842
  expected = obs_exp_clean[outcome_expected_col].to_numpy()
1843
+
1844
+ observed_no = len(observed)
1845
+ expected_no = len(expected)
1826
1846
 
1827
1847
  else:
1828
1848
 
@@ -1833,12 +1853,15 @@ def fit_metrics(
1833
1853
  value_errors.append("Expected data contains NaNs.")
1834
1854
  if len(value_errors) > 0:
1835
1855
  raise ValueError(f"Error(s) while calculating fit metrics: {' '.join(value_errors)} Parameter 'remove_nan' is False.")
1836
-
1856
+
1857
+ observations = len(observed)
1858
+
1859
+ if observations == 0:
1860
+ raise ValueError("No observations left for calculating model fit metrics.")
1861
+
1837
1862
  if verbose:
1838
1863
  print("Calculating model fit metrics", end = " ... ")
1839
1864
 
1840
- observations = len(observed)
1841
-
1842
1865
  residuals = np.array(observed)-np.array(expected)
1843
1866
  residuals_sq = residuals**2
1844
1867
  residuals_abs = abs(residuals)
@@ -1888,8 +1911,8 @@ def fit_metrics(
1888
1911
  try:
1889
1912
  indep_vars_no = int(indep_vars_no)
1890
1913
  RSQ_ADJ = (1-(1-RSQ)*((observations-1)/(observations-indep_vars_no-1)))
1891
- except:
1892
- pass
1914
+ except Exception as e:
1915
+ print(f"WARNING: Adj. R-squared can not be computed: {str(e)}")
1893
1916
 
1894
1917
  model_fit_metrics = {
1895
1918
  list(config.MODEL_FIT_METRICS.keys())[0]: SSR,
@@ -1912,8 +1935,9 @@ def fit_metrics(
1912
1935
  if RSQ_ADJ == np.nan:
1913
1936
  print("NOTE: As no number of independent vars was stated, no Adj. R-Squared is calculated.")
1914
1937
 
1915
- if len(obs_exp_clean) < len(observed) or len(obs_exp_clean) < len(expected):
1916
- print("NOTE: Vectors 'observed' and/or 'expected' contain NaNs which were dropped.")
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.")
1917
1941
 
1918
1942
  modelfit_results = [
1919
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.1
8
- # Last update: 2026-10-17 14:50
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.9
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
+ ![Version](https://img.shields.io/pypi/v/diffindiff) ![Downloads](https://static.pepy.tech/badge/diffindiff)
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.9) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
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,9 +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.9)
203
+ ## What's new (v2.5.10)
202
204
 
203
205
  - Bugfixes
204
- - Adjusting column naming to be compatible with newer pandas versions in `didanalysis.DiffModel.plot()`
205
- - Check in `didtools.fit_metrics()` whether total sum of squares is positive before calculating $$R^2$$
206
- - Incorrect access to counterfactual results in the event of model fit errors or model unavailability has been fixed in `diddata.create_counterfactual()`
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.9',
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