diffindiff 2.5.0__tar.gz → 2.5.1__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.5.0 → diffindiff-2.5.1}/PKG-INFO +6 -13
  2. {diffindiff-2.5.0 → diffindiff-2.5.1}/README.md +5 -12
  3. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff/config.py +4 -3
  4. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff/didanalysis.py +88 -12
  5. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff/diddata.py +58 -13
  6. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff/tests/tests_diffindiff.py +59 -3
  7. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff.egg-info/PKG-INFO +6 -13
  8. {diffindiff-2.5.0 → diffindiff-2.5.1}/setup.py +1 -1
  9. {diffindiff-2.5.0 → diffindiff-2.5.1}/MANIFEST.in +0 -0
  10. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff/__init__.py +0 -0
  11. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff/didanalysis_helper.py +0 -0
  12. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff/didtools.py +0 -0
  13. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff/tests/__init__.py +0 -0
  14. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff/tests/data/Corona_Hesse.xlsx +0 -0
  15. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff/tests/data/counties_DE.csv +0 -0
  16. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff/tests/data/curfew_DE.csv +0 -0
  17. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff.egg-info/SOURCES.txt +0 -0
  18. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff.egg-info/dependency_links.txt +0 -0
  19. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff.egg-info/requires.txt +0 -0
  20. {diffindiff-2.5.0 → diffindiff-2.5.1}/diffindiff.egg-info/top_level.txt +0 -0
  21. {diffindiff-2.5.0 → diffindiff-2.5.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: diffindiff
3
- Version: 2.5.0
3
+ Version: 2.5.1
4
4
  Summary: diffindiff: Python library for convenient Difference-in-Differences analyses
5
5
  Author: Thomas Wieland
6
6
  Author-email: geowieland@googlemail.com
@@ -29,7 +29,7 @@ A research note featuring a case study that utilizes the diffindiff library is a
29
29
 
30
30
  If you use this software, please cite:
31
31
 
32
- Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.0) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
32
+ Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.1) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
33
33
 
34
34
 
35
35
  ## Installation
@@ -177,16 +177,9 @@ See the /tests directory for usage examples of most of the included functions.
177
177
  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).
178
178
 
179
179
 
180
- ## What's new (v2.5.0)
180
+ ## What's new (v2.5.1)
181
181
 
182
- - General
183
- - Update of dependencies to be compatible with Python 3.13 (still works with Python 3.11) while avoiding incompatibility conflict of statsmodels and scipy
184
- - Internal changes in some functions to be compatible with Python 3.13 (still works with Python 3.11)
185
- - Extensions
186
- - Creating synthetic control units with DiffData.add_synthetic() and conducting a Synthetic DiD analysis
187
- - didanalysis_helper.treatment_diagnostics() now additionally checks unique number of analysis units and time points
188
- - didanalysis_helper.data_diagnostics() now additionally checks whether covariates are constant; didanalysis.did_analysis() and .ddd_analysis() automatically skip such variables from the model analysis
189
182
  - Bugfixes
190
- - DiffData.add_own_counterfactual() now works correctly in any case (also when no. of treatments > 1)
191
- - Correct internal processing of treatment data in DiffTreatment and DiffData objects when treatments were added
192
- - Fixed missing treatment group definition in DiffData.add_treatment()
183
+ - In DiffData.create_treatment() and DiffData.create_data(), pre-post designs with after-treatment period (follow-up) now work correctly
184
+ - DiffModel.plot() nows supports pre-post designs with after-treatment period
185
+ - Some small stabilizations
@@ -21,7 +21,7 @@ A research note featuring a case study that utilizes the diffindiff library is a
21
21
 
22
22
  If you use this software, please cite:
23
23
 
24
- Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.0) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
24
+ Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.1) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
25
25
 
26
26
 
27
27
  ## Installation
@@ -169,16 +169,9 @@ See the /tests directory for usage examples of most of the included functions.
169
169
  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).
170
170
 
171
171
 
172
- ## What's new (v2.5.0)
172
+ ## What's new (v2.5.1)
173
173
 
174
- - General
175
- - Update of dependencies to be compatible with Python 3.13 (still works with Python 3.11) while avoiding incompatibility conflict of statsmodels and scipy
176
- - Internal changes in some functions to be compatible with Python 3.13 (still works with Python 3.11)
177
- - Extensions
178
- - Creating synthetic control units with DiffData.add_synthetic() and conducting a Synthetic DiD analysis
179
- - didanalysis_helper.treatment_diagnostics() now additionally checks unique number of analysis units and time points
180
- - didanalysis_helper.data_diagnostics() now additionally checks whether covariates are constant; didanalysis.did_analysis() and .ddd_analysis() automatically skip such variables from the model analysis
181
174
  - Bugfixes
182
- - DiffData.add_own_counterfactual() now works correctly in any case (also when no. of treatments > 1)
183
- - Correct internal processing of treatment data in DiffTreatment and DiffData objects when treatments were added
184
- - Fixed missing treatment group definition in DiffData.add_treatment()
175
+ - In DiffData.create_treatment() and DiffData.create_data(), pre-post designs with after-treatment period (follow-up) now work correctly
176
+ - DiffModel.plot() nows supports pre-post designs with after-treatment period
177
+ - Some small stabilizations
@@ -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.21
8
- # Last update: 2026-07-03 17:50
7
+ # Version: 1.0.22
8
+ # Last update: 2026-07-10 11:36
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.0"
15
+ PACKAGE_VERSION = "2.5.1"
16
16
 
17
17
  VERBOSE = False
18
18
 
@@ -52,6 +52,7 @@ TREATMENT_PERIOD_DESCRIPTION = f"{TREATMENT_DESCRIPTION} period"
52
52
  STUDY_PERIOD_DESCRIPTION = "Study period"
53
53
  PREPOST_DESCRIPTION = "Pre-post"
54
54
  AFTER_TREATMENT_PERIOD_DESCRIPTION = "After-treatment period"
55
+ FOLLOW_UP_DESCRIPTION = "Follow-up"
55
56
 
56
57
  UNITS_DESCRIPTION = "Units"
57
58
 
@@ -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.1
8
- # Last update: 2026-07-02 20:17
7
+ # Version: 2.4.2
8
+ # Last update: 2026-07-10 11:30
9
9
  # Copyright (c) 2024-2026 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -898,6 +898,49 @@ class DiffModel:
898
898
 
899
899
  treatment_effects = self.treatment_effects()
900
900
 
901
+ try:
902
+ if model_config.get("pre_post", False) and len(model_config.get("after_treatment_col", [])) > 0:
903
+
904
+ labels = treatment_effects.iloc[:, 0].astype(str).values
905
+ has_after = any((config.AFTER_TREATMENT_PERIOD_DESCRIPTION in lab) or (config.ATT_COL in lab) for lab in labels)
906
+ if not has_after:
907
+ model_results = self.data[0]
908
+
909
+ aate_key = config.EFFECTS_TYPES["AATE"]["model_results_key"] if "AATE" in config.EFFECTS_TYPES else "average_after_treatment_effects"
910
+ att_key = config.EFFECTS_TYPES["ATT"]["model_results_key"] if "ATT" in config.EFFECTS_TYPES else "after_treatment_time_effects"
911
+ appended = False
912
+
913
+ for key_try in [aate_key, att_key]:
914
+ if key_try in model_results and isinstance(model_results[key_try], dict):
915
+ for subkey, val in model_results[key_try].items():
916
+
917
+ if isinstance(val, dict):
918
+
919
+ coef_k = config.OLS_MODEL_RESULTS["coef"]["model_results_key"]
920
+ se_k = config.OLS_MODEL_RESULTS["coef_standard_errors"]["model_results_key"]
921
+ t_k = config.OLS_MODEL_RESULTS["coef_teststatistic"]["model_results_key"]
922
+ p_k = config.OLS_MODEL_RESULTS["coef_p"]["model_results_key"]
923
+ cil_k = config.OLS_MODEL_RESULTS["coef_confint_lower"]["model_results_key"]
924
+ ciu_k = config.OLS_MODEL_RESULTS["coef_confint_upper"]["model_results_key"]
925
+
926
+ new_row = {
927
+ "": f"{config.EFFECTS_TYPES.get('AATE', {'description': 'After-treatment'})['description']} {subkey}",
928
+ "Estimate": val.get(coef_k, np.nan),
929
+ "SE": val.get(se_k, np.nan),
930
+ "t": val.get(t_k, np.nan),
931
+ "p": val.get(p_k, np.nan),
932
+ "CI lower": val.get(cil_k, np.nan),
933
+ "CI upper": val.get(ciu_k, np.nan),
934
+ }
935
+ treatment_effects = pd.concat([treatment_effects, pd.DataFrame([new_row])], ignore_index=True)
936
+ appended = True
937
+ break
938
+ if appended:
939
+ break
940
+ except Exception:
941
+
942
+ pass
943
+
901
944
  if sort_by_coef:
902
945
  treatment_effects = treatment_effects.sort_values(
903
946
  by = treatment_effects.columns[1],
@@ -1807,14 +1850,14 @@ class DiffModel:
1807
1850
  model_data_TG_CG["t"] = pd.to_datetime(model_data_TG_CG["t"])
1808
1851
 
1809
1852
  if not model_config["pre_post"]:
1810
- pre_post_barplot = False
1853
+ pre_post_barplot = False
1811
1854
 
1812
1855
  if pre_post_barplot:
1813
1856
 
1814
1857
  x_pos_t1_TG = 0
1815
1858
  x_pos_t1_CG = x_pos_t1_TG + pre_post_bar_width
1816
1859
  x_pos_t2_TG = 1.5
1817
- x_pos_t2_CG = x_pos_t2_TG + pre_post_bar_width
1860
+ x_pos_t2_CG = x_pos_t2_TG + pre_post_bar_width
1818
1861
 
1819
1862
  plt.bar(
1820
1863
  x = x_pos_t1_TG,
@@ -1842,6 +1885,24 @@ class DiffModel:
1842
1885
  color=lines_col[3],
1843
1886
  width = pre_post_bar_width
1844
1887
  )
1888
+
1889
+ if len(model_data_TG_CG) == 3:
1890
+
1891
+ x_pos_t3_TG = 3
1892
+ x_pos_t3_CG = x_pos_t3_TG + pre_post_bar_width
1893
+
1894
+ plt.bar(
1895
+ x = x_pos_t3_TG,
1896
+ height = model_data_TG_CG[outcome_col_expected_TG][2],
1897
+ color = lines_col[2],
1898
+ width = pre_post_bar_width
1899
+ )
1900
+ plt.bar(
1901
+ x = x_pos_t3_CG,
1902
+ height = model_data_TG_CG[outcome_col_expected_CG][2],
1903
+ color=lines_col[3],
1904
+ width = pre_post_bar_width
1905
+ )
1845
1906
 
1846
1907
  plt.xlabel(x_label)
1847
1908
  plt.ylabel(y_label)
@@ -1935,16 +1996,29 @@ class DiffModel:
1935
1996
  ax.xaxis.set_major_formatter(DateFormatter(model_config["date_format"]))
1936
1997
 
1937
1998
  if model_config["pre_post"]:
1999
+
2000
+ ticks_line = model_data_TG_CG["t"].unique()
2001
+ ticks_bars = [0.25, 1.75]
2002
+
2003
+ if len(model_data_TG_CG) == 3:
2004
+
2005
+ if len(pre_post_ticks) < 3:
2006
+ pre_post_ticks.append("Follow-up")
2007
+
2008
+ ticks_bars.append(3.25)
2009
+
1938
2010
  if not pre_post_barplot:
2011
+
1939
2012
  plt.xticks(
1940
- model_data_TG_CG["t"].unique(),
1941
- labels = [pre_post_ticks[0], pre_post_ticks[1]]
2013
+ ticks_line,
2014
+ labels = pre_post_ticks
1942
2015
  )
1943
2016
  else:
1944
2017
  plt.xticks(
1945
- [0.25, 1.75],
1946
- labels = [pre_post_ticks[0], pre_post_ticks[1]]
1947
- )
2018
+ ticks_bars,
2019
+ labels = pre_post_ticks
2020
+ )
2021
+
1948
2022
  else:
1949
2023
  plt.xticks(rotation=90)
1950
2024
 
@@ -2644,6 +2718,8 @@ def did_analysis(
2644
2718
  FE_time_vars = []
2645
2719
  dummy_time_original = []
2646
2720
 
2721
+ drop_first = intercept
2722
+
2647
2723
  if not demean:
2648
2724
 
2649
2725
  if FE_unit:
@@ -2652,7 +2728,7 @@ def did_analysis(
2652
2728
  data = data,
2653
2729
  col = unit_col,
2654
2730
  type = "unit",
2655
- drop_first = intercept,
2731
+ drop_first = drop_first,
2656
2732
  verbose = verbose
2657
2733
  )
2658
2734
 
@@ -2667,7 +2743,7 @@ def did_analysis(
2667
2743
  data = data,
2668
2744
  col = time_col,
2669
2745
  type = "time",
2670
- drop_first = intercept,
2746
+ drop_first = drop_first,
2671
2747
  verbose = verbose
2672
2748
  )
2673
2749
 
@@ -2691,7 +2767,7 @@ def did_analysis(
2691
2767
  data = data,
2692
2768
  col = group_by,
2693
2769
  type = "group",
2694
- drop_first = intercept,
2770
+ drop_first = drop_first,
2695
2771
  verbose = verbose
2696
2772
  )
2697
2773
 
@@ -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.0
8
- # Last update: 2026-07-03 18:00
7
+ # Version: 2.3.1
8
+ # Last update: 2026-07-10 11:37
9
9
  # Copyright (c) 2024-2026 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -622,15 +622,36 @@ def create_treatment(
622
622
 
623
623
  if pre_post:
624
624
 
625
- after_treatment_period = False
625
+ if after_treatment_period:
626
+
627
+ if study_period[1] <= treatment_period[1]:
628
+
629
+ print(f"WARNING: after_treatment_period is set True, but end of study period ({study_period[1]}) is <= end of treatment period ({treatment_period[1]}). After treatment period is ignored.")
630
+
631
+ after_treatment_period = False
632
+
633
+ else:
634
+
635
+ study_period_range = [study_period[0], study_period[1]]
636
+ study_period_N = 3
637
+ study_period_counter = [1,2,3]
626
638
 
627
- study_period_range = [study_period[0], study_period[1]]
628
- study_period_N = 2
629
- study_period_counter = [1,2]
639
+ treatment_period_N = 1
640
+ TT_dummies = [0,1,0]
641
+ ATT_dummies = [0,0,1]
630
642
 
631
- treatment_period_range = [treatment_period[0], treatment_period[1]]
632
- treatment_period_N = 1
633
- TT_dummies = [0,1]
643
+ treatment_period_range = [study_period[0], treatment_period[1], study_period[1]]
644
+ after_treatment_period_range = treatment_period_range
645
+
646
+ else:
647
+
648
+ study_period_range = [study_period[0], study_period[1]]
649
+ study_period_N = 2
650
+ study_period_counter = [1,2]
651
+
652
+ treatment_period_range = [treatment_period[0], treatment_period[1]]
653
+ treatment_period_N = 1
654
+ TT_dummies = [0,1]
634
655
 
635
656
  study_period_range = pd.DataFrame (treatment_period_range, columns=[config.TIME_COL])
636
657
  study_period_range[config.TIME_COUNTER_COL] = pd.DataFrame(study_period_counter)
@@ -640,7 +661,7 @@ def create_treatment(
640
661
  TT_col: TT_dummies
641
662
  }
642
663
 
643
- TT_data = pd.DataFrame(TT_data)
664
+ TT_data = pd.DataFrame(TT_data)
644
665
 
645
666
  treatment_period_range = pd.DataFrame(
646
667
  study_period_range
@@ -650,6 +671,24 @@ def create_treatment(
650
671
  TT_data,
651
672
  how = "left"
652
673
  )
674
+
675
+ if after_treatment_period:
676
+
677
+ ATT_data = {
678
+ config.TIME_COL: after_treatment_period_range,
679
+ ATT_col: ATT_dummies
680
+ }
681
+
682
+ ATT_data = pd.DataFrame(ATT_data)
683
+
684
+ after_treatment_period_range = pd.DataFrame (after_treatment_period_range, columns=[config.TIME_COL])
685
+ after_treatment_period_range[config.TIME_COUNTER_COL] = pd.DataFrame(study_period_counter)
686
+
687
+ after_treatment_data_df = after_treatment_period_range.merge(ATT_data, how = "left")
688
+ after_treatment_data_df[ATT_col] = after_treatment_data_df[ATT_col].fillna(0)
689
+ after_treatment_data_df = after_treatment_data_df.drop(columns=[config.TIME_COL, config.TIME_COUNTER_COL])
690
+
691
+ treatment_data_df = pd.concat([treatment_data_df, after_treatment_data_df], axis=1)
653
692
 
654
693
  else:
655
694
 
@@ -689,7 +728,7 @@ def create_treatment(
689
728
 
690
729
  treatment_data_df[TT_col] = treatment_data_df[TT_col].fillna(0)
691
730
 
692
- if after_treatment_period:
731
+ if after_treatment_period and not pre_post:
693
732
 
694
733
  treatment_period_last = datetime.strptime(
695
734
  treatment_period[1],
@@ -720,6 +759,9 @@ def create_treatment(
720
759
 
721
760
  else:
722
761
  after_treatment_period_N = 0
762
+
763
+ if after_treatment_period and pre_post:
764
+ after_treatment_period_N = 1
723
765
 
724
766
  no_treatments = 1
725
767
 
@@ -735,7 +777,7 @@ def create_treatment(
735
777
  "date_format": date_format,
736
778
  "pre_post": pre_post
737
779
  }
738
-
780
+
739
781
  treatment_config = {
740
782
  0: {
741
783
  "treatment_name": treatment_name,
@@ -2050,7 +2092,10 @@ class DiffData:
2050
2092
  else:
2051
2093
  print(f" {config.STUDY_PERIOD_DESCRIPTION:<{config.DIDDATA_SUMMARY_MAX_WIDTH}} {treatment_meta['study_period_start']} - {treatment_meta['study_period_end']} ({treatment_meta['study_period']} {treatment_meta['frequency']})")
2052
2094
  print(f" {config.TREATMENT_PERIOD_DESCRIPTION:<{config.DIDDATA_SUMMARY_MAX_WIDTH}} {treatment_config[key]['treatment_period_start']} - {treatment_config[key]['treatment_period_end']} ({treatment_config[key]['treatment_period']} {treatment_meta['frequency']})")
2053
-
2095
+
2096
+ if value["after_treatment_period"]:
2097
+ print(f" {config.AFTER_TREATMENT_PERIOD_DESCRIPTION:<{config.DIDDATA_SUMMARY_MAX_WIDTH}} {treatment_config[key]['treatment_period_end']} vs. {treatment_meta['study_period_end']} ({config.FOLLOW_UP_DESCRIPTION})")
2098
+
2054
2099
  print("-" * total_width)
2055
2100
 
2056
2101
  print(f"Outcome '{outcome_col_original}'")
@@ -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.0.15
8
- # Last update: 2026-05-01 09:54
7
+ # Version: 2.1.0
8
+ # Last update: 2026-07-10 11:35
9
9
  # Copyright (c) 2025-2026 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -75,7 +75,7 @@ curfew_data_prepost=create_data(
75
75
  treatment_period=["2020-03-21", "2020-05-05"],
76
76
  freq="D",
77
77
  pre_post=True,
78
- after_treatment_period = True
78
+ after_treatment_period = False
79
79
  )
80
80
  # Creating DiD treatement dataset by defining groups and
81
81
  # treatment time at once
@@ -98,6 +98,9 @@ print(curfew_model_prepost.covariates())
98
98
  curfew_model_prepost.summary()
99
99
  # Model summary
100
100
 
101
+ print(curfew_model_prepost.didmodel().summary())
102
+ # Access statsmodels OLS object
103
+
101
104
  print(curfew_model_prepost.fit_metrics())
102
105
  # Show model fit metrics
103
106
 
@@ -137,6 +140,59 @@ curfew_model_prepost.plot_treatment_effects(
137
140
  )
138
141
  # plot effects
139
142
 
143
+
144
+ # Same analysis with after-treatment period (follow-up):
145
+
146
+ curfew_data_prepost_AT=create_data(
147
+ outcome_data=curfew_DE,
148
+ unit_id_col="county",
149
+ time_col="infection_date",
150
+ outcome_col="infections_cum_per100000",
151
+ treatment_group=
152
+ curfew_DE.loc[curfew_DE["Bundesland"].isin([9,10,14])]["county"],
153
+ control_group=
154
+ curfew_DE.loc[~curfew_DE["Bundesland"].isin([9,10,14])]["county"],
155
+ study_period=["2020-03-01", "2020-05-15"],
156
+ treatment_period=["2020-03-21", "2020-05-05"],
157
+ freq="D",
158
+ pre_post=True,
159
+ after_treatment_period = True
160
+ )
161
+ # Creating DiD treatement dataset by defining groups and
162
+ # treatment time at once
163
+
164
+ curfew_data_prepost_AT.summary()
165
+ # Summary of created data
166
+
167
+ curfew_model_prepost_AT=curfew_data_prepost_AT.analysis(verbose=True)
168
+ # Model analysis of created data
169
+
170
+ curfew_model_prepost_AT.summary()
171
+ # Model summary
172
+
173
+ curfew_model_prepost_AT.plot(
174
+ x_label="Timepoint",
175
+ y_label="Cumulative infections per 100,000",
176
+ plot_title="Curfew effectiveness pre-post - Groups over time",
177
+ plot_observed=False,
178
+ lines_col=[None,None,"blue","orange"],
179
+ lines_labels=[None,None,"Treatment group","Control group","Treatment group CI","Control group CI"],
180
+ lines_style=[None,None,"solid","solid"]
181
+ )
182
+ # Plot DiD pre vs. post results
183
+ # with user-determined style
184
+
185
+ curfew_model_prepost_AT.plot(
186
+ x_label="Timepoint",
187
+ y_label="Cumulative infections per 100,000",
188
+ plot_title="Curfew effectiveness pre-post - Groups over time",
189
+ lines_col=[None,None,"blue","orange"],
190
+ lines_labels=[None,None,"Treatment group","Control group","Treatment group CI", "Control group CI"],
191
+ pre_post_barplot=True
192
+ )
193
+ # Plot DiD pre vs. post results
194
+ # with user-determined style
195
+
140
196
  counties_DE=pd.read_csv("data/counties_DE.csv", sep=";", decimal=",", encoding='latin1')
141
197
  # Dataset with German county data
142
198
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: diffindiff
3
- Version: 2.5.0
3
+ Version: 2.5.1
4
4
  Summary: diffindiff: Python library for convenient Difference-in-Differences analyses
5
5
  Author: Thomas Wieland
6
6
  Author-email: geowieland@googlemail.com
@@ -29,7 +29,7 @@ A research note featuring a case study that utilizes the diffindiff library is a
29
29
 
30
30
  If you use this software, please cite:
31
31
 
32
- Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.0) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
32
+ Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.5.1) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
33
33
 
34
34
 
35
35
  ## Installation
@@ -177,16 +177,9 @@ See the /tests directory for usage examples of most of the included functions.
177
177
  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).
178
178
 
179
179
 
180
- ## What's new (v2.5.0)
180
+ ## What's new (v2.5.1)
181
181
 
182
- - General
183
- - Update of dependencies to be compatible with Python 3.13 (still works with Python 3.11) while avoiding incompatibility conflict of statsmodels and scipy
184
- - Internal changes in some functions to be compatible with Python 3.13 (still works with Python 3.11)
185
- - Extensions
186
- - Creating synthetic control units with DiffData.add_synthetic() and conducting a Synthetic DiD analysis
187
- - didanalysis_helper.treatment_diagnostics() now additionally checks unique number of analysis units and time points
188
- - didanalysis_helper.data_diagnostics() now additionally checks whether covariates are constant; didanalysis.did_analysis() and .ddd_analysis() automatically skip such variables from the model analysis
189
182
  - Bugfixes
190
- - DiffData.add_own_counterfactual() now works correctly in any case (also when no. of treatments > 1)
191
- - Correct internal processing of treatment data in DiffTreatment and DiffData objects when treatments were added
192
- - Fixed missing treatment group definition in DiffData.add_treatment()
183
+ - In DiffData.create_treatment() and DiffData.create_data(), pre-post designs with after-treatment period (follow-up) now work correctly
184
+ - DiffModel.plot() nows supports pre-post designs with after-treatment period
185
+ - Some small stabilizations
@@ -7,7 +7,7 @@ def read_README():
7
7
 
8
8
  setup(
9
9
  name='diffindiff',
10
- version='2.5.0',
10
+ version='2.5.1',
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