diffindiff 2.3.2__tar.gz → 2.3.4__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 (21) hide show
  1. {diffindiff-2.3.2 → diffindiff-2.3.4}/PKG-INFO +5 -5
  2. {diffindiff-2.3.2 → diffindiff-2.3.4}/README.md +4 -4
  3. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff/config.py +3 -3
  4. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff/didanalysis.py +30 -7
  5. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff/didanalysis_helper.py +117 -2
  6. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff/diddata.py +13 -11
  7. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff.egg-info/PKG-INFO +5 -5
  8. {diffindiff-2.3.2 → diffindiff-2.3.4}/setup.py +1 -1
  9. {diffindiff-2.3.2 → diffindiff-2.3.4}/MANIFEST.in +0 -0
  10. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff/__init__.py +0 -0
  11. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff/didtools.py +0 -0
  12. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff/tests/__init__.py +0 -0
  13. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff/tests/data/Corona_Hesse.xlsx +0 -0
  14. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff/tests/data/counties_DE.csv +0 -0
  15. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff/tests/data/curfew_DE.csv +0 -0
  16. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff/tests/tests_diffindiff.py +0 -0
  17. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff.egg-info/SOURCES.txt +0 -0
  18. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff.egg-info/dependency_links.txt +0 -0
  19. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff.egg-info/requires.txt +0 -0
  20. {diffindiff-2.3.2 → diffindiff-2.3.4}/diffindiff.egg-info/top_level.txt +0 -0
  21. {diffindiff-2.3.2 → diffindiff-2.3.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: diffindiff
3
- Version: 2.3.2
3
+ Version: 2.3.4
4
4
  Summary: diffindiff: Python library for convenient Difference-in-Differences analyses
5
5
  Author: Thomas Wieland
6
6
  Author-email: geowieland@googlemail.com
@@ -27,7 +27,7 @@ Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geo
27
27
 
28
28
  If you use this software, please cite:
29
29
 
30
- Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.3.2) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
30
+ Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.3.4) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
31
31
 
32
32
 
33
33
  ## Installation
@@ -173,7 +173,7 @@ See the /tests directory for usage examples of most of the included functions.
173
173
  This software was developed without the use of AI-generated code. The Continue Agent 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).
174
174
 
175
175
 
176
- ## What's new (v2.3.2)
176
+ ## What's new (v2.3.4)
177
177
 
178
- - Extensions:
179
- - Re-transform log-transformed outcomes in DiffModel.plot() via parameter 'retransform_log_outcome'
178
+ - Bugfixes:
179
+ - Fixed bug in DiffData instance creation in diddata.merge_data()
@@ -19,7 +19,7 @@ Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geo
19
19
 
20
20
  If you use this software, please cite:
21
21
 
22
- Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.3.2) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
22
+ Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.3.4) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
23
23
 
24
24
 
25
25
  ## Installation
@@ -165,7 +165,7 @@ See the /tests directory for usage examples of most of the included functions.
165
165
  This software was developed without the use of AI-generated code. The Continue Agent 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).
166
166
 
167
167
 
168
- ## What's new (v2.3.2)
168
+ ## What's new (v2.3.4)
169
169
 
170
- - Extensions:
171
- - Re-transform log-transformed outcomes in DiffModel.plot() via parameter 'retransform_log_outcome'
170
+ - Bugfixes:
171
+ - Fixed bug in DiffData instance creation in diddata.merge_data()
@@ -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.10
8
- # Last update: 2026-03-05 21:27
7
+ # Version: 1.0.12
8
+ # Last update: 2026-03-14 11:28
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.3.2"
15
+ PACKAGE_VERSION = "2.3.4"
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.3.2
8
- # Last update: 2026-03-06 21:26
7
+ # Version: 2.3.3
8
+ # Last update: 2026-03-12 19:40
9
9
  # Copyright (c) 2024-2026 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -2088,6 +2088,7 @@ def did_analysis(
2088
2088
  covariates: list = None,
2089
2089
  spillover_treatment: list = None,
2090
2090
  spillover_units: list = None,
2091
+ interactions: dict = None,
2091
2092
  placebo: bool = False,
2092
2093
  confint_alpha = 0.05,
2093
2094
  bonferroni: bool = False,
@@ -2096,8 +2097,8 @@ def did_analysis(
2096
2097
  drop_missing: bool = True,
2097
2098
  missing_replace_by_zero: bool = False,
2098
2099
  fit_by: str = "ols_fit",
2099
- verbose: bool = config.VERBOSE
2100
- ):
2100
+ verbose: bool = False
2101
+ ):
2101
2102
 
2102
2103
  """
2103
2104
  Perform a Difference-in-Differences analysis with a given data frame.
@@ -2154,6 +2155,9 @@ def did_analysis(
2154
2155
  Treatment columns used to construct spillover variables.
2155
2156
  spillover_units : list
2156
2157
  Unit identifiers affected by spillovers.
2158
+ interactions : dict
2159
+ Dictionary with treatment interaction variables to be
2160
+ built from treatment variables.
2157
2161
  placebo : bool
2158
2162
  Run placebo analysis.
2159
2163
  confint_alpha : float
@@ -2171,7 +2175,7 @@ def did_analysis(
2171
2175
  fit_by : str
2172
2176
  Fitting method; e.g., 'ols_fit' or 'ml'.
2173
2177
  verbose : bool, optional
2174
- If True, print progress messages.
2178
+ If True, print progress messages.
2175
2179
 
2176
2180
  Returns
2177
2181
  -------
@@ -2211,6 +2215,8 @@ def did_analysis(
2211
2215
  spillover_treatment = []
2212
2216
  if spillover_units is None:
2213
2217
  spillover_units = []
2218
+ if interactions is None:
2219
+ interactions = {}
2214
2220
 
2215
2221
  tools.check_columns(
2216
2222
  df = data,
@@ -2624,11 +2630,28 @@ def did_analysis(
2624
2630
 
2625
2631
  did_formula = f"{did_formula} + {covariates_join}"
2626
2632
 
2627
- indep_vars_no = indep_vars_no+len(spillover_vars)+len(covariates)
2633
+ if len(interactions) > 0:
2634
+
2635
+ interactions_created = helper.create_interactions(
2636
+ data=data,
2637
+ interactions=interactions,
2638
+ verbose=verbose
2639
+ )
2640
+
2641
+ data = interactions_created[0]
2642
+
2643
+ did_formula = f"{did_formula} + {interactions_created[1]}"
2644
+
2645
+ treatment_col.extend(interactions_created[2])
2646
+
2647
+ no_treatments = no_treatments+len(interactions)
2648
+
2649
+ indep_vars_no = indep_vars_no+len(spillover_vars)+len(covariates)+len(interactions)
2628
2650
 
2629
2651
  did_formula = did_formula[:-1] if did_formula.endswith(" ") else did_formula
2630
2652
  did_formula = did_formula[:-1] if did_formula.endswith("+") else did_formula
2631
2653
  did_formula = did_formula[:-1] if did_formula.endswith(" ") else did_formula
2654
+
2632
2655
  if not intercept:
2633
2656
  did_formula = f"{did_formula} - 1"
2634
2657
 
@@ -2754,7 +2777,7 @@ def ddd_analysis(
2754
2777
  drop_missing: bool = True,
2755
2778
  missing_replace_by_zero: bool = False,
2756
2779
  fit_by: str = "ols_fit",
2757
- verbose: bool = config.VERBOSE
2780
+ verbose: bool = False
2758
2781
  ):
2759
2782
 
2760
2783
  """
@@ -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.1.0
8
- # Last update: 2025-02-28 13:06
7
+ # Version: 1.1.1
8
+ # Last update: 2025-03-12 20:44
9
9
  # Copyright (c) 2025-2026 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -369,6 +369,121 @@ def create_spillover(
369
369
  spillover_treatment_vars_join,
370
370
  spillover_treatment_vars
371
371
  ]
372
+
373
+ def create_interactions(
374
+ data: pd.DataFrame,
375
+ interactions: dict = None,
376
+ verbose: bool = config.VERBOSE
377
+ ):
378
+
379
+ """
380
+ Create interaction variables by multiplying specified treatment columns.
381
+
382
+ Parameters
383
+ ----------
384
+ data : pandas.DataFrame
385
+ Input data frame containing treatment columns.
386
+ interactions : dict, optional
387
+ Dictionary defining interaction variables. Each entry should be a
388
+ mapping with keys "name" (desired new column name) and
389
+ "treatments" (list of existing treatment column names to multiply).
390
+ verbose : bool, optional
391
+ If True, print progress messages.
392
+
393
+ Returns
394
+ -------
395
+ list
396
+ [data_with_interactions (DataFrame), join_string (str), interaction_vars (list)]
397
+
398
+ Raises
399
+ ------
400
+ TypeError
401
+ If ``interactions`` is not a dict.
402
+ KeyError
403
+ If a stated interaction name already exists in `data` or if any
404
+ referenced treatment column does not exist in `data`.
405
+ ValueError
406
+ If fewer than two treatments are provided to build an interaction.
407
+
408
+ Examples
409
+ --------
410
+ >>> create_interactions(
411
+ ... df,
412
+ ... {
413
+ ... 0: {
414
+ ... 'name': 'interaction_col',
415
+ ... 'treatments': ['treatment1', 'treatment2']
416
+ ... }
417
+ ... }
418
+ ... )
419
+ """
420
+
421
+ if interactions is None:
422
+ interactions = {}
423
+
424
+ if not isinstance(interactions, dict):
425
+ raise TypeError("Parameter 'interactions' must be a dict")
426
+
427
+ interaction_vars = []
428
+
429
+ if len(interactions) > 0:
430
+
431
+ if verbose:
432
+ print(f"Creating {len(interactions)} interaction variables", end = " ... ")
433
+
434
+ for key, value in interactions.items():
435
+
436
+ interaction_var_name = value["name"]
437
+
438
+ if interaction_var_name in data.columns:
439
+ raise KeyError(f"Stated column name '{interaction_var_name}' already exists in data frame")
440
+
441
+ interaction_var_name = tools.clean_column_name(interaction_var_name)
442
+
443
+ interaction_vars.append(interaction_var_name)
444
+
445
+ treatments = value["treatments"]
446
+
447
+ if len (treatments) < 2:
448
+ raise ValueError(f"To build interaction variables, at least two treatment variables must be stated, not {len(treatments)}")
449
+
450
+ treatment_cols_not_available = []
451
+
452
+ for treatment in treatments:
453
+ if treatment not in data.columns:
454
+ treatment_cols_not_available.append(treatment)
455
+
456
+ if len(treatment_cols_not_available) > 0:
457
+ raise KeyError(f"The following column(s) stated do not exist in the data frame: {', '.join(treatment_cols_not_available)}")
458
+
459
+ treatment1 = treatments[0]
460
+
461
+ data[interaction_var_name] = data[treatment1]
462
+
463
+ for key, treatment in enumerate(treatments):
464
+
465
+ if key == 0:
466
+ continue
467
+
468
+ data[interaction_var_name] = data[interaction_var_name]*data[treatment]
469
+
470
+ if verbose:
471
+ print("OK")
472
+
473
+ if len(interaction_vars) > 0:
474
+ print(f"NOTE: The following {len(interaction_vars)} interaction variables were created: {', '.join(interaction_vars)}.")
475
+
476
+ else:
477
+
478
+ print("WARNING: Parameter 'interactions' was not set. No interaction variables were created.")
479
+
480
+ interaction_vars_join = ' + '.join(interaction_vars)
481
+
482
+ return [
483
+ data,
484
+ interaction_vars_join,
485
+ interaction_vars
486
+ ]
372
487
 
373
488
  def data_diagnostics(
374
489
  data: pd.DataFrame,
@@ -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.2
8
- # Last update: 2026-03-06 21:27
7
+ # Version: 2.2.4
8
+ # Last update: 2026-03-14 11:28
9
9
  # Copyright (c) 2024-2026 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -815,7 +815,6 @@ class DiffData:
815
815
  def get_did_modeldata_df (self):
816
816
 
817
817
  """
818
-
819
818
  Return the DiD model data as a DataFrame.
820
819
 
821
820
  Returns
@@ -845,7 +844,6 @@ class DiffData:
845
844
  def get_did_groups(self):
846
845
 
847
846
  """
848
-
849
847
  Return the stored DiffGroups object.
850
848
 
851
849
  Returns
@@ -875,7 +873,6 @@ class DiffData:
875
873
  def get_did_treatment(self):
876
874
 
877
875
  """
878
-
879
876
  Return the stored DiffTreatment object.
880
877
 
881
878
  Returns
@@ -905,7 +902,6 @@ class DiffData:
905
902
  def get_unit_time_cols(self):
906
903
 
907
904
  """
908
-
909
905
  Return the original unit/time column names.
910
906
 
911
907
  Returns
@@ -935,7 +931,6 @@ class DiffData:
935
931
  def get_covariates(self):
936
932
 
937
933
  """
938
-
939
934
  Return the list of covariate column names.
940
935
 
941
936
  Returns
@@ -965,7 +960,6 @@ class DiffData:
965
960
  def get_treatment_cols(self):
966
961
 
967
962
  """
968
-
969
963
  Return the stored treatment columns metadata.
970
964
 
971
965
  Returns
@@ -995,7 +989,6 @@ class DiffData:
995
989
  def get_timestamp(self):
996
990
 
997
991
  """
998
-
999
992
  Return timestamp metadata for the DiffData object.
1000
993
 
1001
994
  Returns
@@ -1567,10 +1560,10 @@ class DiffData:
1567
1560
  self.data[4] = unit_time_col_original
1568
1561
  self.data[5] = covariates
1569
1562
  self.data[6] = treatment_cols_new
1570
- self.data[7][len(self.data[7])] = helper.create_timestamp(function="define_treatment")
1563
+ self.data[7][len(self.data[7])] = helper.create_timestamp(function="define_treatment")
1571
1564
 
1572
1565
  return self
1573
-
1566
+
1574
1567
  def add_segmentation(
1575
1568
  self,
1576
1569
  group_benefit: list
@@ -1796,6 +1789,8 @@ class DiffData:
1796
1789
 
1797
1790
  print("=" * total_width)
1798
1791
 
1792
+ return self
1793
+
1799
1794
  def analysis(
1800
1795
  self,
1801
1796
  log_outcome: bool = False,
@@ -1811,6 +1806,7 @@ class DiffData:
1811
1806
  group_by: str = None,
1812
1807
  spillover_treatment: list = None,
1813
1808
  spillover_units: list = None,
1809
+ interactions: dict = None,
1814
1810
  confint_alpha: float = 0.05,
1815
1811
  bonferroni: bool = False,
1816
1812
  drop_missing: bool = True,
@@ -1850,6 +1846,9 @@ class DiffData:
1850
1846
  Treatments for which to create spillover variables.
1851
1847
  spillover_units : list, optional
1852
1848
  Units to flag as exposed to spillover.
1849
+ interactions : dict
1850
+ Dictionary with treatment interaction variables to be
1851
+ built from treatment variables.
1853
1852
  confint_alpha : float, optional
1854
1853
  Significance level for confidence intervals.
1855
1854
  bonferroni : bool, optional
@@ -1887,6 +1886,8 @@ class DiffData:
1887
1886
  spillover_treatment = []
1888
1887
  if spillover_units is None:
1889
1888
  spillover_units = []
1889
+ if interactions is None:
1890
+ interactions = {}
1890
1891
 
1891
1892
  did_modeldata = self.get_did_modeldata_df()
1892
1893
  outcome_col_original = self.data[3]
@@ -1977,6 +1978,7 @@ class DiffData:
1977
1978
  covariates = covariates,
1978
1979
  spillover_treatment = spillover_treatment,
1979
1980
  spillover_units = spillover_units,
1981
+ interactions = interactions,
1980
1982
  confint_alpha = confint_alpha,
1981
1983
  bonferroni = bonferroni,
1982
1984
  freq = freq,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: diffindiff
3
- Version: 2.3.2
3
+ Version: 2.3.4
4
4
  Summary: diffindiff: Python library for convenient Difference-in-Differences analyses
5
5
  Author: Thomas Wieland
6
6
  Author-email: geowieland@googlemail.com
@@ -27,7 +27,7 @@ Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geo
27
27
 
28
28
  If you use this software, please cite:
29
29
 
30
- Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.3.2) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
30
+ Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.3.4) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
31
31
 
32
32
 
33
33
  ## Installation
@@ -173,7 +173,7 @@ See the /tests directory for usage examples of most of the included functions.
173
173
  This software was developed without the use of AI-generated code. The Continue Agent 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).
174
174
 
175
175
 
176
- ## What's new (v2.3.2)
176
+ ## What's new (v2.3.4)
177
177
 
178
- - Extensions:
179
- - Re-transform log-transformed outcomes in DiffModel.plot() via parameter 'retransform_log_outcome'
178
+ - Bugfixes:
179
+ - Fixed bug in DiffData instance creation in diddata.merge_data()
@@ -7,7 +7,7 @@ def read_README():
7
7
 
8
8
  setup(
9
9
  name='diffindiff',
10
- version='2.3.2',
10
+ version='2.3.4',
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