diffindiff 2.2.3__tar.gz → 2.2.5__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.2.3 → diffindiff-2.2.5}/PKG-INFO +51 -28
- {diffindiff-2.2.3 → diffindiff-2.2.5}/README.md +50 -27
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff/didanalysis.py +20 -6
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff/didanalysis_helper.py +18 -3
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff/diddata.py +26 -29
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff/didtools.py +93 -36
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff.egg-info/PKG-INFO +51 -28
- {diffindiff-2.2.3 → diffindiff-2.2.5}/setup.py +1 -2
- {diffindiff-2.2.3 → diffindiff-2.2.5}/MANIFEST.in +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff/__init__.py +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff/config.py +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff/tests/__init__.py +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff/tests/data/Corona_Hesse.xlsx +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff/tests/data/counties_DE.csv +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff/tests/data/curfew_DE.csv +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff/tests/tests_diffindiff.py +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff.egg-info/SOURCES.txt +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff.egg-info/dependency_links.txt +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff.egg-info/requires.txt +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/diffindiff.egg-info/top_level.txt +0 -0
- {diffindiff-2.2.3 → diffindiff-2.2.5}/setup.cfg +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: diffindiff
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.5
|
|
4
4
|
Summary: diffindiff: Python library for convenient Difference-in-Differences Analyses
|
|
5
5
|
Author: Thomas Wieland
|
|
6
6
|
Author-email: geowieland@googlemail.com
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
|
|
9
|
-
# diffindiff:
|
|
9
|
+
# diffindiff: A Python library for convenient difference-in-differences analyses
|
|
10
10
|
|
|
11
11
|
This Python library is designed for performing Difference-in-Differences (DiD) analyses in a convenient way. It 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 setting and data diagnostics are incorporated.
|
|
12
12
|
|
|
@@ -16,9 +16,33 @@ This Python library is designed for performing Difference-in-Differences (DiD) a
|
|
|
16
16
|
Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geowieland@googlemail.com)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
## Availability
|
|
20
|
+
|
|
21
|
+
- 📦 PyPI: [diffindiff](https://pypi.org/project/diffindiff/)
|
|
22
|
+
- 💻 GitHub Repository: [diffindiff_official](https://github.com/geowieland/diffindiff_official)
|
|
23
|
+
- 📄 DOI (Zenodo): [10.5281/zenodo.18639559](https://doi.org/10.5281/zenodo.18639559)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Citation
|
|
27
|
+
|
|
28
|
+
If you use this software, please cite:
|
|
29
|
+
|
|
30
|
+
Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.2.4) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
To install the package, use `pip`:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install diffindiff
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
To install the package from GitHub with `pip`:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install git+https://github.com/geowieland/diffindiff_official.git
|
|
45
|
+
```
|
|
22
46
|
|
|
23
47
|
|
|
24
48
|
## Features
|
|
@@ -54,25 +78,6 @@ Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geo
|
|
|
54
78
|
- Test for parallel trend assumption
|
|
55
79
|
|
|
56
80
|
|
|
57
|
-
## Literature
|
|
58
|
-
|
|
59
|
-
- Baker AC, Larcker DF, Wang CCY (2022) How much should we trust staggered difference-in-differences estimates? *Journal of Financial Economics* 144(2): 370-395. [10.1016/j.jfineco.2022.01.004](https://doi.org/10.1016/j.jfineco.2022.01.004)
|
|
60
|
-
- Card D, Krueger AD (1994) Minimum Wages and Employment: A Case Study of the Fast Food Industry in New Jersey and Pennsylvania. *The American Economic Review* 84(4): 772-793. [JSTOR](https://www.jstor.org/stable/2677856)
|
|
61
|
-
- de Haas S, Götz G, Heim S (2022) Measuring the effect of COVID‑19‑related night curfews in a bundled intervention within Germany. *Scientific Reports* 12: 19732. [10.1038/s41598-022-24086-9](https://doi.org/10.1038/s41598-022-24086-9)
|
|
62
|
-
- Goodman-Bacon A (2021) Difference-in-differences with variation in treatment timing. *Journal of Econometrics* 225(2): 254-277. [10.1016/j.jeconom.2021.03.014](https://doi.org/10.1016/j.jeconom.2021.03.014)
|
|
63
|
-
- Greene WH (2012) *Econometric Analysis*.
|
|
64
|
-
- Goldfarb A, Tucker C, Wang Y (2022) Conducting Research in Marketing with Quasi-Experiments. *Journal of Marketing* 86(3): 1-19. [10.1177/00222429221082977](https://doi.org/10.1177/00222429221082977)
|
|
65
|
-
- Isporhing IE, Lipfert M, Pestel N (2021) Does re-opening schools contribute to the spread of SARS-CoV-2? Evidence from staggered summer breaks in Germany. *Journal of Public Economics* 198: 104426. [10.1016/j.jpubeco.2021.104426](https://doi.org/10.1016/j.jpubeco.2021.104426)
|
|
66
|
-
- Li KT, Luo L, Pattabhiramaiah A (2024) Causal Inference with Quasi-Experimental Data. *IMPACT at JMR* November 13, 2024. [AMA](https://www.ama.org/marketing-news/causal-inference-with-quasi-experimental-data/)
|
|
67
|
-
- Olden A (2018) What do you buy when no one's watching? The effect of self-service checkouts on the composition of sales in retail. Discussion paper FOR 3/18, Norwegian School of Economics, Norway. [http://hdl.handle.net/11250/2490886](http://hdl.handle.net/11250/2490886)
|
|
68
|
-
- Olden A, Moen J (2022) The triple difference estimator. *The Econometrics Journal* 25(3): 531-553. [10.1093/ectj/utac010](https://doi.org/10.1093/ectj/utac010)
|
|
69
|
-
- Strassmann A, Çolak Y, Serra-Burriel M, Nordestgaard BG, Turk A, Afzal S, Puhan MA (2023) Nationwide indoor smoking ban and impact on smoking behaviour and lung function: a two-population natural experiment. *Thorax* 78(2): 144-150. [10.1136/thoraxjnl-2021-218436](https://doi.org/10.1136/thoraxjnl-2021-218436)
|
|
70
|
-
- Villa JM (2016) diff: Simplifying the estimation of difference-in-differences treatment effects. *The Stata Journal* 16(1): 52-71. [10.1177/1536867X1601600108](https://doi.org/10.1177/1536867X1601600108)
|
|
71
|
-
- von Bismarck-Osten C, Borusyak K, Schönberg U (2022) The role of schools in transmission of the SARS-CoV-2 virus: quasi-experimental evidence from Germany. *Economic Policy* 37(109): 87–130. [10.1093/epolic/eiac001](https://doi.org/10.1093/epolic/eiac001)
|
|
72
|
-
- Wieland T (2025) Assessing the effectiveness of non-pharmaceutical interventions in the SARS-CoV-2 pandemic: results of a natural experiment regarding Baden-Württemberg (Germany) and Switzerland in the second infection wave. *Journal of Public Health: From Theory to Practice* 33(11): 2497-2511. [10.1007/s10389-024-02218-x](https://doi.org/10.1007/s10389-024-02218-x)
|
|
73
|
-
- Wooldridge JM (2012) *Introductory Econometrics. A Modern Approach*.
|
|
74
|
-
|
|
75
|
-
|
|
76
81
|
## Examples
|
|
77
82
|
|
|
78
83
|
```python
|
|
@@ -143,9 +148,27 @@ curfew_model_withgroups.plot_group_treatment_effects(
|
|
|
143
148
|
See the /tests directory for usage examples of most of the included functions.
|
|
144
149
|
|
|
145
150
|
|
|
146
|
-
##
|
|
151
|
+
## Literature
|
|
147
152
|
|
|
148
|
-
|
|
153
|
+
- Baker AC, Larcker DF, Wang CCY (2022) How much should we trust staggered difference-in-differences estimates? *Journal of Financial Economics* 144(2): 370-395. [10.1016/j.jfineco.2022.01.004](https://doi.org/10.1016/j.jfineco.2022.01.004)
|
|
154
|
+
- Card D, Krueger AD (1994) Minimum Wages and Employment: A Case Study of the Fast Food Industry in New Jersey and Pennsylvania. *The American Economic Review* 84(4): 772-793. [JSTOR](https://www.jstor.org/stable/2677856)
|
|
155
|
+
- de Haas S, Götz G, Heim S (2022) Measuring the effect of COVID‑19‑related night curfews in a bundled intervention within Germany. *Scientific Reports* 12: 19732. [10.1038/s41598-022-24086-9](https://doi.org/10.1038/s41598-022-24086-9)
|
|
156
|
+
- Goodman-Bacon A (2021) Difference-in-differences with variation in treatment timing. *Journal of Econometrics* 225(2): 254-277. [10.1016/j.jeconom.2021.03.014](https://doi.org/10.1016/j.jeconom.2021.03.014)
|
|
157
|
+
- Greene WH (2012) *Econometric Analysis*.
|
|
158
|
+
- Goldfarb A, Tucker C, Wang Y (2022) Conducting Research in Marketing with Quasi-Experiments. *Journal of Marketing* 86(3): 1-19. [10.1177/00222429221082977](https://doi.org/10.1177/00222429221082977)
|
|
159
|
+
- Isporhing IE, Lipfert M, Pestel N (2021) Does re-opening schools contribute to the spread of SARS-CoV-2? Evidence from staggered summer breaks in Germany. *Journal of Public Economics* 198: 104426. [10.1016/j.jpubeco.2021.104426](https://doi.org/10.1016/j.jpubeco.2021.104426)
|
|
160
|
+
- Li KT, Luo L, Pattabhiramaiah A (2024) Causal Inference with Quasi-Experimental Data. *IMPACT at JMR* November 13, 2024. [AMA](https://www.ama.org/marketing-news/causal-inference-with-quasi-experimental-data/)
|
|
161
|
+
- Olden A (2018) What do you buy when no one's watching? The effect of self-service checkouts on the composition of sales in retail. Discussion paper FOR 3/18, Norwegian School of Economics, Norway. [http://hdl.handle.net/11250/2490886](http://hdl.handle.net/11250/2490886)
|
|
162
|
+
- Olden A, Moen J (2022) The triple difference estimator. *The Econometrics Journal* 25(3): 531-553. [10.1093/ectj/utac010](https://doi.org/10.1093/ectj/utac010)
|
|
163
|
+
- Strassmann A, Çolak Y, Serra-Burriel M, Nordestgaard BG, Turk A, Afzal S, Puhan MA (2023) Nationwide indoor smoking ban and impact on smoking behaviour and lung function: a two-population natural experiment. *Thorax* 78(2): 144-150. [10.1136/thoraxjnl-2021-218436](https://doi.org/10.1136/thoraxjnl-2021-218436)
|
|
164
|
+
- Villa JM (2016) diff: Simplifying the estimation of difference-in-differences treatment effects. *The Stata Journal* 16(1): 52-71. [10.1177/1536867X1601600108](https://doi.org/10.1177/1536867X1601600108)
|
|
165
|
+
- von Bismarck-Osten C, Borusyak K, Schönberg U (2022) The role of schools in transmission of the SARS-CoV-2 virus: quasi-experimental evidence from Germany. *Economic Policy* 37(109): 87–130. [10.1093/epolic/eiac001](https://doi.org/10.1093/epolic/eiac001)
|
|
166
|
+
- Wieland T (2025) Assessing the effectiveness of non-pharmaceutical interventions in the SARS-CoV-2 pandemic: results of a natural experiment regarding Baden-Württemberg (Germany) and Switzerland in the second infection wave. *Journal of Public Health: From Theory to Practice* 33(11): 2497-2511. [10.1007/s10389-024-02218-x](https://doi.org/10.1007/s10389-024-02218-x)
|
|
167
|
+
- Wooldridge JM (2012) *Introductory Econometrics. A Modern Approach*.
|
|
149
168
|
|
|
150
|
-
|
|
151
|
-
|
|
169
|
+
|
|
170
|
+
## What's new (v2.2.5)
|
|
171
|
+
- Bugfixes:
|
|
172
|
+
- Incorrect import
|
|
173
|
+
- Other:
|
|
174
|
+
- Update README
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# diffindiff:
|
|
1
|
+
# diffindiff: A Python library for convenient difference-in-differences analyses
|
|
2
2
|
|
|
3
3
|
This Python library is designed for performing Difference-in-Differences (DiD) analyses in a convenient way. It 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 setting and data diagnostics are incorporated.
|
|
4
4
|
|
|
@@ -8,9 +8,33 @@ This Python library is designed for performing Difference-in-Differences (DiD) a
|
|
|
8
8
|
Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geowieland@googlemail.com)
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
## Availability
|
|
12
|
+
|
|
13
|
+
- 📦 PyPI: [diffindiff](https://pypi.org/project/diffindiff/)
|
|
14
|
+
- 💻 GitHub Repository: [diffindiff_official](https://github.com/geowieland/diffindiff_official)
|
|
15
|
+
- 📄 DOI (Zenodo): [10.5281/zenodo.18639559](https://doi.org/10.5281/zenodo.18639559)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Citation
|
|
19
|
+
|
|
20
|
+
If you use this software, please cite:
|
|
21
|
+
|
|
22
|
+
Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.2.4) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
To install the package, use `pip`:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install diffindiff
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
To install the package from GitHub with `pip`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install git+https://github.com/geowieland/diffindiff_official.git
|
|
37
|
+
```
|
|
14
38
|
|
|
15
39
|
|
|
16
40
|
## Features
|
|
@@ -46,25 +70,6 @@ Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geo
|
|
|
46
70
|
- Test for parallel trend assumption
|
|
47
71
|
|
|
48
72
|
|
|
49
|
-
## Literature
|
|
50
|
-
|
|
51
|
-
- Baker AC, Larcker DF, Wang CCY (2022) How much should we trust staggered difference-in-differences estimates? *Journal of Financial Economics* 144(2): 370-395. [10.1016/j.jfineco.2022.01.004](https://doi.org/10.1016/j.jfineco.2022.01.004)
|
|
52
|
-
- Card D, Krueger AD (1994) Minimum Wages and Employment: A Case Study of the Fast Food Industry in New Jersey and Pennsylvania. *The American Economic Review* 84(4): 772-793. [JSTOR](https://www.jstor.org/stable/2677856)
|
|
53
|
-
- de Haas S, Götz G, Heim S (2022) Measuring the effect of COVID‑19‑related night curfews in a bundled intervention within Germany. *Scientific Reports* 12: 19732. [10.1038/s41598-022-24086-9](https://doi.org/10.1038/s41598-022-24086-9)
|
|
54
|
-
- Goodman-Bacon A (2021) Difference-in-differences with variation in treatment timing. *Journal of Econometrics* 225(2): 254-277. [10.1016/j.jeconom.2021.03.014](https://doi.org/10.1016/j.jeconom.2021.03.014)
|
|
55
|
-
- Greene WH (2012) *Econometric Analysis*.
|
|
56
|
-
- Goldfarb A, Tucker C, Wang Y (2022) Conducting Research in Marketing with Quasi-Experiments. *Journal of Marketing* 86(3): 1-19. [10.1177/00222429221082977](https://doi.org/10.1177/00222429221082977)
|
|
57
|
-
- Isporhing IE, Lipfert M, Pestel N (2021) Does re-opening schools contribute to the spread of SARS-CoV-2? Evidence from staggered summer breaks in Germany. *Journal of Public Economics* 198: 104426. [10.1016/j.jpubeco.2021.104426](https://doi.org/10.1016/j.jpubeco.2021.104426)
|
|
58
|
-
- Li KT, Luo L, Pattabhiramaiah A (2024) Causal Inference with Quasi-Experimental Data. *IMPACT at JMR* November 13, 2024. [AMA](https://www.ama.org/marketing-news/causal-inference-with-quasi-experimental-data/)
|
|
59
|
-
- Olden A (2018) What do you buy when no one's watching? The effect of self-service checkouts on the composition of sales in retail. Discussion paper FOR 3/18, Norwegian School of Economics, Norway. [http://hdl.handle.net/11250/2490886](http://hdl.handle.net/11250/2490886)
|
|
60
|
-
- Olden A, Moen J (2022) The triple difference estimator. *The Econometrics Journal* 25(3): 531-553. [10.1093/ectj/utac010](https://doi.org/10.1093/ectj/utac010)
|
|
61
|
-
- Strassmann A, Çolak Y, Serra-Burriel M, Nordestgaard BG, Turk A, Afzal S, Puhan MA (2023) Nationwide indoor smoking ban and impact on smoking behaviour and lung function: a two-population natural experiment. *Thorax* 78(2): 144-150. [10.1136/thoraxjnl-2021-218436](https://doi.org/10.1136/thoraxjnl-2021-218436)
|
|
62
|
-
- Villa JM (2016) diff: Simplifying the estimation of difference-in-differences treatment effects. *The Stata Journal* 16(1): 52-71. [10.1177/1536867X1601600108](https://doi.org/10.1177/1536867X1601600108)
|
|
63
|
-
- von Bismarck-Osten C, Borusyak K, Schönberg U (2022) The role of schools in transmission of the SARS-CoV-2 virus: quasi-experimental evidence from Germany. *Economic Policy* 37(109): 87–130. [10.1093/epolic/eiac001](https://doi.org/10.1093/epolic/eiac001)
|
|
64
|
-
- Wieland T (2025) Assessing the effectiveness of non-pharmaceutical interventions in the SARS-CoV-2 pandemic: results of a natural experiment regarding Baden-Württemberg (Germany) and Switzerland in the second infection wave. *Journal of Public Health: From Theory to Practice* 33(11): 2497-2511. [10.1007/s10389-024-02218-x](https://doi.org/10.1007/s10389-024-02218-x)
|
|
65
|
-
- Wooldridge JM (2012) *Introductory Econometrics. A Modern Approach*.
|
|
66
|
-
|
|
67
|
-
|
|
68
73
|
## Examples
|
|
69
74
|
|
|
70
75
|
```python
|
|
@@ -135,9 +140,27 @@ curfew_model_withgroups.plot_group_treatment_effects(
|
|
|
135
140
|
See the /tests directory for usage examples of most of the included functions.
|
|
136
141
|
|
|
137
142
|
|
|
138
|
-
##
|
|
143
|
+
## Literature
|
|
139
144
|
|
|
140
|
-
|
|
145
|
+
- Baker AC, Larcker DF, Wang CCY (2022) How much should we trust staggered difference-in-differences estimates? *Journal of Financial Economics* 144(2): 370-395. [10.1016/j.jfineco.2022.01.004](https://doi.org/10.1016/j.jfineco.2022.01.004)
|
|
146
|
+
- Card D, Krueger AD (1994) Minimum Wages and Employment: A Case Study of the Fast Food Industry in New Jersey and Pennsylvania. *The American Economic Review* 84(4): 772-793. [JSTOR](https://www.jstor.org/stable/2677856)
|
|
147
|
+
- de Haas S, Götz G, Heim S (2022) Measuring the effect of COVID‑19‑related night curfews in a bundled intervention within Germany. *Scientific Reports* 12: 19732. [10.1038/s41598-022-24086-9](https://doi.org/10.1038/s41598-022-24086-9)
|
|
148
|
+
- Goodman-Bacon A (2021) Difference-in-differences with variation in treatment timing. *Journal of Econometrics* 225(2): 254-277. [10.1016/j.jeconom.2021.03.014](https://doi.org/10.1016/j.jeconom.2021.03.014)
|
|
149
|
+
- Greene WH (2012) *Econometric Analysis*.
|
|
150
|
+
- Goldfarb A, Tucker C, Wang Y (2022) Conducting Research in Marketing with Quasi-Experiments. *Journal of Marketing* 86(3): 1-19. [10.1177/00222429221082977](https://doi.org/10.1177/00222429221082977)
|
|
151
|
+
- Isporhing IE, Lipfert M, Pestel N (2021) Does re-opening schools contribute to the spread of SARS-CoV-2? Evidence from staggered summer breaks in Germany. *Journal of Public Economics* 198: 104426. [10.1016/j.jpubeco.2021.104426](https://doi.org/10.1016/j.jpubeco.2021.104426)
|
|
152
|
+
- Li KT, Luo L, Pattabhiramaiah A (2024) Causal Inference with Quasi-Experimental Data. *IMPACT at JMR* November 13, 2024. [AMA](https://www.ama.org/marketing-news/causal-inference-with-quasi-experimental-data/)
|
|
153
|
+
- Olden A (2018) What do you buy when no one's watching? The effect of self-service checkouts on the composition of sales in retail. Discussion paper FOR 3/18, Norwegian School of Economics, Norway. [http://hdl.handle.net/11250/2490886](http://hdl.handle.net/11250/2490886)
|
|
154
|
+
- Olden A, Moen J (2022) The triple difference estimator. *The Econometrics Journal* 25(3): 531-553. [10.1093/ectj/utac010](https://doi.org/10.1093/ectj/utac010)
|
|
155
|
+
- Strassmann A, Çolak Y, Serra-Burriel M, Nordestgaard BG, Turk A, Afzal S, Puhan MA (2023) Nationwide indoor smoking ban and impact on smoking behaviour and lung function: a two-population natural experiment. *Thorax* 78(2): 144-150. [10.1136/thoraxjnl-2021-218436](https://doi.org/10.1136/thoraxjnl-2021-218436)
|
|
156
|
+
- Villa JM (2016) diff: Simplifying the estimation of difference-in-differences treatment effects. *The Stata Journal* 16(1): 52-71. [10.1177/1536867X1601600108](https://doi.org/10.1177/1536867X1601600108)
|
|
157
|
+
- von Bismarck-Osten C, Borusyak K, Schönberg U (2022) The role of schools in transmission of the SARS-CoV-2 virus: quasi-experimental evidence from Germany. *Economic Policy* 37(109): 87–130. [10.1093/epolic/eiac001](https://doi.org/10.1093/epolic/eiac001)
|
|
158
|
+
- Wieland T (2025) Assessing the effectiveness of non-pharmaceutical interventions in the SARS-CoV-2 pandemic: results of a natural experiment regarding Baden-Württemberg (Germany) and Switzerland in the second infection wave. *Journal of Public Health: From Theory to Practice* 33(11): 2497-2511. [10.1007/s10389-024-02218-x](https://doi.org/10.1007/s10389-024-02218-x)
|
|
159
|
+
- Wooldridge JM (2012) *Introductory Econometrics. A Modern Approach*.
|
|
141
160
|
|
|
142
|
-
|
|
143
|
-
|
|
161
|
+
|
|
162
|
+
## What's new (v2.2.5)
|
|
163
|
+
- Bugfixes:
|
|
164
|
+
- Incorrect import
|
|
165
|
+
- Other:
|
|
166
|
+
- Update README
|
|
@@ -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: 2025-12-
|
|
7
|
+
# Version: 2.2.2
|
|
8
|
+
# Last update: 2025-12-07 10:27
|
|
9
9
|
# Copyright (c) 2025 Thomas Wieland
|
|
10
10
|
#-----------------------------------------------------------------------
|
|
11
11
|
|
|
@@ -893,7 +893,7 @@ class DiffModel:
|
|
|
893
893
|
treatment_diagnostics = model_config["treatment_diagnostics"]
|
|
894
894
|
no_treatments = model_config["no_treatments"]
|
|
895
895
|
outcome_col = model_config["outcome_col"]
|
|
896
|
-
outcome_col_predicted = outcome_col
|
|
896
|
+
outcome_col_predicted = f"{outcome_col}{config.PREDICTED_SUFFIX}"
|
|
897
897
|
|
|
898
898
|
if TG_col is None and treatment is None:
|
|
899
899
|
if no_treatments == 1:
|
|
@@ -928,8 +928,7 @@ class DiffModel:
|
|
|
928
928
|
if ("TG" in plot_intervals_groups and "CG" not in plot_intervals_groups) or ("CG" in plot_intervals_groups and "TG" not in plot_intervals_groups):
|
|
929
929
|
lines_labels_required = lines_labels_required+1
|
|
930
930
|
if "TG" in plot_intervals_groups and "CG" in plot_intervals_groups:
|
|
931
|
-
lines_labels_required = lines_labels_required+2
|
|
932
|
-
|
|
931
|
+
lines_labels_required = lines_labels_required+2
|
|
933
932
|
assert len(lines_col) == lines_col_required, f"Parameter 'lines_col' must be a list with {lines_col_required} entries"
|
|
934
933
|
assert len(lines_style) == lines_style_required, f"Parameter 'lines_style' must be a list with {lines_col_required} entries"
|
|
935
934
|
assert len(lines_labels) == lines_labels_required, f"Parameter 'lines_labels' must be a list with {lines_labels_required} entries"
|
|
@@ -1392,6 +1391,13 @@ def did_analysis(
|
|
|
1392
1391
|
*treatment_col
|
|
1393
1392
|
]
|
|
1394
1393
|
|
|
1394
|
+
data = tools.panel_index(
|
|
1395
|
+
data = data,
|
|
1396
|
+
unit_col = unit_col,
|
|
1397
|
+
time_col = time_col,
|
|
1398
|
+
verbose = verbose
|
|
1399
|
+
)
|
|
1400
|
+
|
|
1395
1401
|
treatment_diagnostics_results = helper.treatment_diagnostics(
|
|
1396
1402
|
data = data,
|
|
1397
1403
|
unit_col=unit_col,
|
|
@@ -1731,6 +1737,7 @@ def did_analysis(
|
|
|
1731
1737
|
spillover = helper.create_spillover(
|
|
1732
1738
|
data=data,
|
|
1733
1739
|
unit_col=unit_col,
|
|
1740
|
+
time_col=time_col,
|
|
1734
1741
|
treatment_col=treatment_col,
|
|
1735
1742
|
spillover_treatment=spillover_treatment,
|
|
1736
1743
|
spillover_units=spillover_units
|
|
@@ -1928,7 +1935,14 @@ def ddd_analysis(
|
|
|
1928
1935
|
)
|
|
1929
1936
|
|
|
1930
1937
|
cols_relevant = cols_relevant + covariates
|
|
1931
|
-
|
|
1938
|
+
|
|
1939
|
+
data = tools.panel_index(
|
|
1940
|
+
data = data,
|
|
1941
|
+
unit_col = unit_col,
|
|
1942
|
+
time_col = time_col,
|
|
1943
|
+
verbose = verbose
|
|
1944
|
+
)
|
|
1945
|
+
|
|
1932
1946
|
treatment_diagnostics_results = helper.treatment_diagnostics(
|
|
1933
1947
|
data = data,
|
|
1934
1948
|
unit_col=unit_col,
|
|
@@ -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.0.
|
|
8
|
-
# Last update: 2025-12-
|
|
7
|
+
# Version: 1.0.5
|
|
8
|
+
# Last update: 2025-12-07 10:27
|
|
9
9
|
# Copyright (c) 2025 Thomas Wieland
|
|
10
10
|
#-----------------------------------------------------------------------
|
|
11
11
|
|
|
@@ -172,7 +172,9 @@ def create_specific_treatment_effects(
|
|
|
172
172
|
def create_spillover(
|
|
173
173
|
data: pd.DataFrame,
|
|
174
174
|
unit_col: str,
|
|
175
|
+
time_col: str,
|
|
175
176
|
treatment_col: list,
|
|
177
|
+
TT_col: str = None,
|
|
176
178
|
spillover_treatment: list = [],
|
|
177
179
|
spillover_units: list = [],
|
|
178
180
|
verbose: bool = config.VERBOSE
|
|
@@ -191,6 +193,19 @@ def create_spillover(
|
|
|
191
193
|
|
|
192
194
|
for treatment in treatment_col:
|
|
193
195
|
|
|
196
|
+
if TT_col is None:
|
|
197
|
+
|
|
198
|
+
TT_col = config.TT_COL
|
|
199
|
+
|
|
200
|
+
data = tools.treatment_time_col(
|
|
201
|
+
data = data,
|
|
202
|
+
unit_col = unit_col,
|
|
203
|
+
time_col = time_col,
|
|
204
|
+
treatment_col = treatment,
|
|
205
|
+
create_TT_col = TT_col,
|
|
206
|
+
verbose = verbose
|
|
207
|
+
)[0]
|
|
208
|
+
|
|
194
209
|
sp_unit_col = f"{config.SPILLOVER_UNIT_PREFIX}{config.DELIMITER}{treatment}"
|
|
195
210
|
sp_treatment_col = f"{config.SPILLOVER_PREFIX}{config.DELIMITER}{treatment}"
|
|
196
211
|
|
|
@@ -205,7 +220,7 @@ def create_spillover(
|
|
|
205
220
|
sp_unit_col
|
|
206
221
|
] = 1
|
|
207
222
|
|
|
208
|
-
data[sp_treatment_col] = data[sp_unit_col]*data[
|
|
223
|
+
data[sp_treatment_col] = data[sp_unit_col]*data[TT_col]
|
|
209
224
|
|
|
210
225
|
spillover_treatment_vars_join = ' + '.join(spillover_treatment_vars)
|
|
211
226
|
|
|
@@ -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.1.
|
|
8
|
-
# Last update: 2025-12-
|
|
7
|
+
# Version: 2.1.5
|
|
8
|
+
# Last update: 2025-12-07 10:27
|
|
9
9
|
# Copyright (c) 2025 Thomas Wieland
|
|
10
10
|
#-----------------------------------------------------------------------
|
|
11
11
|
|
|
@@ -478,7 +478,7 @@ class DiffData:
|
|
|
478
478
|
):
|
|
479
479
|
|
|
480
480
|
if unit_col is None and time_col is None:
|
|
481
|
-
raise ValueError("unit_col and/or time_col must be stated")
|
|
481
|
+
raise ValueError("Parameter 'unit_col' and/or 'time_col' must be stated")
|
|
482
482
|
|
|
483
483
|
if verbose:
|
|
484
484
|
if len(variables) > 0:
|
|
@@ -488,16 +488,16 @@ class DiffData:
|
|
|
488
488
|
|
|
489
489
|
did_modeldata = self.get_did_modeldata_df()
|
|
490
490
|
|
|
491
|
+
additional_df = tools.panel_index(
|
|
492
|
+
data=additional_df,
|
|
493
|
+
unit_col=unit_col,
|
|
494
|
+
time_col=time_col,
|
|
495
|
+
verbose=verbose
|
|
496
|
+
)
|
|
497
|
+
|
|
491
498
|
existing_variables = []
|
|
492
499
|
|
|
493
|
-
if unit_col is not None and time_col is not None:
|
|
494
|
-
|
|
495
|
-
additional_df = tools.panel_index(
|
|
496
|
-
data=additional_df,
|
|
497
|
-
unit_col=unit_col,
|
|
498
|
-
time_col=time_col,
|
|
499
|
-
verbose=verbose
|
|
500
|
-
)
|
|
500
|
+
if unit_col is not None and time_col is not None:
|
|
501
501
|
|
|
502
502
|
if variables is None:
|
|
503
503
|
|
|
@@ -659,15 +659,15 @@ class DiffData:
|
|
|
659
659
|
|
|
660
660
|
new_merge = tools.panel_index(
|
|
661
661
|
data=new_merge,
|
|
662
|
-
unit_col=
|
|
663
|
-
time_col=
|
|
662
|
+
unit_col=config.UNIT_COL,
|
|
663
|
+
time_col=config.TIME_COL,
|
|
664
664
|
verbose=verbose
|
|
665
665
|
)
|
|
666
666
|
|
|
667
667
|
did_modeldata_old = tools.panel_index(
|
|
668
668
|
data=did_modeldata_old,
|
|
669
|
-
unit_col=
|
|
670
|
-
time_col=
|
|
669
|
+
unit_col=config.UNIT_COL,
|
|
670
|
+
time_col=config.TIME_COL,
|
|
671
671
|
verbose=verbose
|
|
672
672
|
)
|
|
673
673
|
|
|
@@ -1055,24 +1055,12 @@ def merge_data(
|
|
|
1055
1055
|
treatment_data_df,
|
|
1056
1056
|
how = "cross"
|
|
1057
1057
|
)
|
|
1058
|
-
|
|
1059
|
-
if drop_missing or missing_replace_by_zero:
|
|
1060
|
-
modeldata_ismissing = tools.is_missing(
|
|
1061
|
-
data = did_modeldata,
|
|
1062
|
-
drop_missing = drop_missing,
|
|
1063
|
-
missing_replace_by_zero = missing_replace_by_zero,
|
|
1064
|
-
verbose = False
|
|
1065
|
-
)
|
|
1066
|
-
did_modeldata = modeldata_ismissing[2]
|
|
1067
1058
|
|
|
1068
1059
|
did_modeldata[treatment_name] = did_modeldata[TG_col] * did_modeldata[TT_col]
|
|
1069
1060
|
|
|
1070
1061
|
if treatment_config["after_treatment_period"]:
|
|
1071
1062
|
did_modeldata[after_treatment_name] = did_modeldata[TG_col] * did_modeldata[ATT_col]
|
|
1072
1063
|
|
|
1073
|
-
if np.dtype(did_modeldata[config.TIME_COL]) != np.dtype(outcome_data[time_col]):
|
|
1074
|
-
print(f"WARNING: Time columns of treatment data and outcome data differ: {str(np.dtype(did_modeldata[config.TIME_COL]))}, {str(np.dtype(outcome_data[time_col]))}. This might induce an error while building the model dataset.")
|
|
1075
|
-
|
|
1076
1064
|
did_modeldata = tools.panel_index(
|
|
1077
1065
|
data=did_modeldata,
|
|
1078
1066
|
unit_col=config.UNIT_COL,
|
|
@@ -1086,7 +1074,7 @@ def merge_data(
|
|
|
1086
1074
|
time_col=time_col,
|
|
1087
1075
|
verbose=verbose
|
|
1088
1076
|
)
|
|
1089
|
-
|
|
1077
|
+
|
|
1090
1078
|
if keep_columns:
|
|
1091
1079
|
outcome_data_short = outcome_data
|
|
1092
1080
|
else:
|
|
@@ -1097,6 +1085,15 @@ def merge_data(
|
|
|
1097
1085
|
on=config.UNIT_TIME_COL,
|
|
1098
1086
|
how="left"
|
|
1099
1087
|
)
|
|
1088
|
+
|
|
1089
|
+
if drop_missing or missing_replace_by_zero:
|
|
1090
|
+
modeldata_ismissing = tools.is_missing(
|
|
1091
|
+
data = did_modeldata,
|
|
1092
|
+
drop_missing = drop_missing,
|
|
1093
|
+
missing_replace_by_zero = missing_replace_by_zero,
|
|
1094
|
+
verbose = False
|
|
1095
|
+
)
|
|
1096
|
+
did_modeldata = modeldata_ismissing[2]
|
|
1100
1097
|
|
|
1101
1098
|
outcome_col_original = outcome_col
|
|
1102
1099
|
unit_time_col_original = unit_id_col, time_col
|
|
@@ -1230,7 +1227,7 @@ def create_counterfactual(
|
|
|
1230
1227
|
unit_col = unit_col,
|
|
1231
1228
|
time_col = time_col,
|
|
1232
1229
|
treatment_col = treatment_col
|
|
1233
|
-
)
|
|
1230
|
+
)[0]
|
|
1234
1231
|
units = tools.unique(units_tt[unit_col])
|
|
1235
1232
|
|
|
1236
1233
|
if not isnotreatment[0]:
|
|
@@ -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.1.
|
|
8
|
-
# Last update: 2025-12-
|
|
7
|
+
# Version: 2.1.4
|
|
8
|
+
# Last update: 2025-12-07 10:27
|
|
9
9
|
# Copyright (c) 2025 Thomas Wieland
|
|
10
10
|
#-----------------------------------------------------------------------
|
|
11
11
|
|
|
@@ -54,25 +54,39 @@ def panel_index(
|
|
|
54
54
|
):
|
|
55
55
|
|
|
56
56
|
to_str = []
|
|
57
|
-
unit_x_time = True
|
|
58
57
|
|
|
59
|
-
if
|
|
60
|
-
data[unit_col]
|
|
61
|
-
|
|
58
|
+
if unit_col is not None:
|
|
59
|
+
if data[unit_col].dtype != 'object':
|
|
60
|
+
data[unit_col] = data[unit_col].astype(str)
|
|
61
|
+
to_str.append(unit_col)
|
|
62
|
+
else:
|
|
63
|
+
if verbose:
|
|
64
|
+
print("NOTE: No unit column was stated")
|
|
65
|
+
|
|
66
|
+
if time_col is not None:
|
|
67
|
+
if data[time_col].dtype != 'object':
|
|
68
|
+
data[time_col] = data[time_col].astype(str)
|
|
69
|
+
to_str.append(time_col)
|
|
70
|
+
else:
|
|
71
|
+
if verbose:
|
|
72
|
+
print("NOTE: No time column was stated")
|
|
62
73
|
|
|
63
|
-
if
|
|
64
|
-
|
|
65
|
-
to_str.append(time_col)
|
|
74
|
+
if verbose and len(to_str) > 0:
|
|
75
|
+
print(f"NOTE: The following columns were converted to str: {', '.join(to_str)}.")
|
|
66
76
|
|
|
67
77
|
if config.UNIT_TIME_COL not in data.columns:
|
|
68
|
-
|
|
69
|
-
|
|
78
|
+
|
|
79
|
+
if unit_col is not None and time_col is not None:
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
data[config.UNIT_TIME_COL] = data[unit_col]+config.DELIMITER+data[time_col]
|
|
82
|
+
|
|
83
|
+
if verbose:
|
|
84
|
+
print(f"NOTE: The following unit-time-index column was created: {config.UNIT_TIME_COL}.")
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
|
|
88
|
+
if verbose:
|
|
89
|
+
print("No unit-time-index column was created.")
|
|
76
90
|
|
|
77
91
|
return data
|
|
78
92
|
|
|
@@ -170,8 +184,8 @@ def is_binary(
|
|
|
170
184
|
if verbose:
|
|
171
185
|
print("OK")
|
|
172
186
|
|
|
173
|
-
|
|
174
|
-
|
|
187
|
+
if not binary:
|
|
188
|
+
print(f"NOTE: treatment column '{treatment_col}' is not binary. Likely treatment format is: {treatment_format}.")
|
|
175
189
|
|
|
176
190
|
return [
|
|
177
191
|
binary,
|
|
@@ -268,8 +282,8 @@ def is_simultaneous(
|
|
|
268
282
|
if verbose:
|
|
269
283
|
print("OK")
|
|
270
284
|
|
|
271
|
-
|
|
272
|
-
|
|
285
|
+
if not simultaneous and data_isnotreatment[0]:
|
|
286
|
+
print(f"NOTE: treatment '{treatment_col}' is not simultaneous.")
|
|
273
287
|
|
|
274
288
|
if simultaneous and not data_isnotreatment[0]:
|
|
275
289
|
print(f"WARNING: treatment '{treatment_col}' is simultaneous and does not include a {config.NO_TREATMENT_CG_DESCRIPTION}")
|
|
@@ -303,8 +317,8 @@ def is_notreatment(
|
|
|
303
317
|
if verbose:
|
|
304
318
|
print("OK")
|
|
305
319
|
|
|
306
|
-
|
|
307
|
-
|
|
320
|
+
if not no_treatment:
|
|
321
|
+
print(f"NOTE: treatment '{treatment_col}' does not include a {config.NO_TREATMENT_CG_DESCRIPTION}.")
|
|
308
322
|
|
|
309
323
|
return [
|
|
310
324
|
no_treatment,
|
|
@@ -342,8 +356,8 @@ def treatment_group_col(
|
|
|
342
356
|
if verbose:
|
|
343
357
|
print("OK")
|
|
344
358
|
|
|
345
|
-
|
|
346
|
-
|
|
359
|
+
if create_TG_col_exists:
|
|
360
|
+
print(f"NOTE: Column {create_TG_col} already exists. Saved treatment group in column {config.TG_COL}{config.DELIMITER}{treatment_col}.")
|
|
347
361
|
|
|
348
362
|
return [
|
|
349
363
|
data,
|
|
@@ -351,6 +365,32 @@ def treatment_group_col(
|
|
|
351
365
|
create_TG_col
|
|
352
366
|
]
|
|
353
367
|
|
|
368
|
+
def treatment_time_col(
|
|
369
|
+
data: pd.DataFrame,
|
|
370
|
+
unit_col: str,
|
|
371
|
+
time_col: str,
|
|
372
|
+
treatment_col: str,
|
|
373
|
+
create_TT_col: str = "TT",
|
|
374
|
+
verbose: bool = config.VERBOSE
|
|
375
|
+
):
|
|
376
|
+
|
|
377
|
+
tt = treatment_times(
|
|
378
|
+
data = data,
|
|
379
|
+
unit_col = unit_col,
|
|
380
|
+
time_col = time_col,
|
|
381
|
+
treatment_col = treatment_col,
|
|
382
|
+
verbose = verbose
|
|
383
|
+
)[1]
|
|
384
|
+
|
|
385
|
+
data[create_TT_col] = 0
|
|
386
|
+
data.loc[data[time_col].isin(tt), create_TT_col] = 1
|
|
387
|
+
|
|
388
|
+
return [
|
|
389
|
+
data,
|
|
390
|
+
tt,
|
|
391
|
+
create_TT_col
|
|
392
|
+
]
|
|
393
|
+
|
|
354
394
|
def untreated_units(
|
|
355
395
|
data: pd.DataFrame,
|
|
356
396
|
unit_col: str,
|
|
@@ -460,10 +500,11 @@ def is_prepost(
|
|
|
460
500
|
if verbose:
|
|
461
501
|
print("OK")
|
|
462
502
|
|
|
463
|
-
if
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
503
|
+
if verbose:
|
|
504
|
+
if prepost:
|
|
505
|
+
print("NOTE: Panel data is pre-post.")
|
|
506
|
+
else:
|
|
507
|
+
print("NOTE: Panel data is multi-period panel data.")
|
|
467
508
|
|
|
468
509
|
return prepost
|
|
469
510
|
|
|
@@ -502,8 +543,8 @@ def is_multiple_treatment_period(
|
|
|
502
543
|
if verbose:
|
|
503
544
|
print("OK")
|
|
504
545
|
|
|
505
|
-
|
|
506
|
-
|
|
546
|
+
if units_multiple > 0:
|
|
547
|
+
print(f"NOTE: There are {units_multiple} observational units with multiple treatment periods with respect to treatment '{treatment_col}'.")
|
|
507
548
|
|
|
508
549
|
return [
|
|
509
550
|
multiple_treatment_period,
|
|
@@ -591,12 +632,22 @@ def treatment_times(
|
|
|
591
632
|
unit_col,
|
|
592
633
|
time_col,
|
|
593
634
|
treatment_col
|
|
594
|
-
]
|
|
635
|
+
],
|
|
636
|
+
verbose=verbose
|
|
595
637
|
)
|
|
596
638
|
|
|
639
|
+
is_multiple_treatment_period(
|
|
640
|
+
data = data,
|
|
641
|
+
unit_col = unit_col,
|
|
642
|
+
treatment_col = treatment_col,
|
|
643
|
+
verbose = verbose
|
|
644
|
+
)[0]
|
|
645
|
+
|
|
597
646
|
if verbose:
|
|
598
647
|
print(f"Identifying treatment times for treatment '{treatment_col}'", end = " ... ")
|
|
599
648
|
|
|
649
|
+
tt = list(unique(data.loc[data[treatment_col] == 1, time_col]))
|
|
650
|
+
|
|
600
651
|
units = unique(data[unit_col])
|
|
601
652
|
|
|
602
653
|
units_tt = pd.DataFrame(columns = [unit_col, "treatment_min", "treatment_max"])
|
|
@@ -628,7 +679,10 @@ def treatment_times(
|
|
|
628
679
|
if verbose:
|
|
629
680
|
print("OK")
|
|
630
681
|
|
|
631
|
-
return
|
|
682
|
+
return [
|
|
683
|
+
units_tt,
|
|
684
|
+
tt
|
|
685
|
+
]
|
|
632
686
|
|
|
633
687
|
def model_wrapper(
|
|
634
688
|
y,
|
|
@@ -833,8 +887,6 @@ def fit_metrics(
|
|
|
833
887
|
RSQ_ADJ = (1-(1-RSQ)*((observations-1)/(observations-indep_vars_no-1)))
|
|
834
888
|
|
|
835
889
|
else:
|
|
836
|
-
|
|
837
|
-
print("NOTE: As no number of independent vars was stated, no Adj. R-Squared is calculated.")
|
|
838
890
|
|
|
839
891
|
RSQ_ADJ = np.nan
|
|
840
892
|
|
|
@@ -854,8 +906,13 @@ def fit_metrics(
|
|
|
854
906
|
if verbose:
|
|
855
907
|
print("OK")
|
|
856
908
|
|
|
857
|
-
if
|
|
858
|
-
|
|
909
|
+
if verbose:
|
|
910
|
+
|
|
911
|
+
if RSQ_ADJ == np.nan:
|
|
912
|
+
print("NOTE: As no number of independent vars was stated, no Adj. R-Squared is calculated.")
|
|
913
|
+
|
|
914
|
+
if len(obs_exp_clean) < len(observed) or len(obs_exp_clean) < len(expected):
|
|
915
|
+
print("NOTE: Vectors 'observed' and/or 'expected' contain NaNs which were dropped.")
|
|
859
916
|
|
|
860
917
|
modelfit_results = [
|
|
861
918
|
model_residuals,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: diffindiff
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.5
|
|
4
4
|
Summary: diffindiff: Python library for convenient Difference-in-Differences Analyses
|
|
5
5
|
Author: Thomas Wieland
|
|
6
6
|
Author-email: geowieland@googlemail.com
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
|
|
9
|
-
# diffindiff:
|
|
9
|
+
# diffindiff: A Python library for convenient difference-in-differences analyses
|
|
10
10
|
|
|
11
11
|
This Python library is designed for performing Difference-in-Differences (DiD) analyses in a convenient way. It 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 setting and data diagnostics are incorporated.
|
|
12
12
|
|
|
@@ -16,9 +16,33 @@ This Python library is designed for performing Difference-in-Differences (DiD) a
|
|
|
16
16
|
Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geowieland@googlemail.com)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
## Availability
|
|
20
|
+
|
|
21
|
+
- 📦 PyPI: [diffindiff](https://pypi.org/project/diffindiff/)
|
|
22
|
+
- 💻 GitHub Repository: [diffindiff_official](https://github.com/geowieland/diffindiff_official)
|
|
23
|
+
- 📄 DOI (Zenodo): [10.5281/zenodo.18639559](https://doi.org/10.5281/zenodo.18639559)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Citation
|
|
27
|
+
|
|
28
|
+
If you use this software, please cite:
|
|
29
|
+
|
|
30
|
+
Wieland, T. (2026). diffindiff: A Python library for convenient difference-in-differences analyses (Version 2.2.4) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18656820
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
To install the package, use `pip`:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install diffindiff
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
To install the package from GitHub with `pip`:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install git+https://github.com/geowieland/diffindiff_official.git
|
|
45
|
+
```
|
|
22
46
|
|
|
23
47
|
|
|
24
48
|
## Features
|
|
@@ -54,25 +78,6 @@ Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geo
|
|
|
54
78
|
- Test for parallel trend assumption
|
|
55
79
|
|
|
56
80
|
|
|
57
|
-
## Literature
|
|
58
|
-
|
|
59
|
-
- Baker AC, Larcker DF, Wang CCY (2022) How much should we trust staggered difference-in-differences estimates? *Journal of Financial Economics* 144(2): 370-395. [10.1016/j.jfineco.2022.01.004](https://doi.org/10.1016/j.jfineco.2022.01.004)
|
|
60
|
-
- Card D, Krueger AD (1994) Minimum Wages and Employment: A Case Study of the Fast Food Industry in New Jersey and Pennsylvania. *The American Economic Review* 84(4): 772-793. [JSTOR](https://www.jstor.org/stable/2677856)
|
|
61
|
-
- de Haas S, Götz G, Heim S (2022) Measuring the effect of COVID‑19‑related night curfews in a bundled intervention within Germany. *Scientific Reports* 12: 19732. [10.1038/s41598-022-24086-9](https://doi.org/10.1038/s41598-022-24086-9)
|
|
62
|
-
- Goodman-Bacon A (2021) Difference-in-differences with variation in treatment timing. *Journal of Econometrics* 225(2): 254-277. [10.1016/j.jeconom.2021.03.014](https://doi.org/10.1016/j.jeconom.2021.03.014)
|
|
63
|
-
- Greene WH (2012) *Econometric Analysis*.
|
|
64
|
-
- Goldfarb A, Tucker C, Wang Y (2022) Conducting Research in Marketing with Quasi-Experiments. *Journal of Marketing* 86(3): 1-19. [10.1177/00222429221082977](https://doi.org/10.1177/00222429221082977)
|
|
65
|
-
- Isporhing IE, Lipfert M, Pestel N (2021) Does re-opening schools contribute to the spread of SARS-CoV-2? Evidence from staggered summer breaks in Germany. *Journal of Public Economics* 198: 104426. [10.1016/j.jpubeco.2021.104426](https://doi.org/10.1016/j.jpubeco.2021.104426)
|
|
66
|
-
- Li KT, Luo L, Pattabhiramaiah A (2024) Causal Inference with Quasi-Experimental Data. *IMPACT at JMR* November 13, 2024. [AMA](https://www.ama.org/marketing-news/causal-inference-with-quasi-experimental-data/)
|
|
67
|
-
- Olden A (2018) What do you buy when no one's watching? The effect of self-service checkouts on the composition of sales in retail. Discussion paper FOR 3/18, Norwegian School of Economics, Norway. [http://hdl.handle.net/11250/2490886](http://hdl.handle.net/11250/2490886)
|
|
68
|
-
- Olden A, Moen J (2022) The triple difference estimator. *The Econometrics Journal* 25(3): 531-553. [10.1093/ectj/utac010](https://doi.org/10.1093/ectj/utac010)
|
|
69
|
-
- Strassmann A, Çolak Y, Serra-Burriel M, Nordestgaard BG, Turk A, Afzal S, Puhan MA (2023) Nationwide indoor smoking ban and impact on smoking behaviour and lung function: a two-population natural experiment. *Thorax* 78(2): 144-150. [10.1136/thoraxjnl-2021-218436](https://doi.org/10.1136/thoraxjnl-2021-218436)
|
|
70
|
-
- Villa JM (2016) diff: Simplifying the estimation of difference-in-differences treatment effects. *The Stata Journal* 16(1): 52-71. [10.1177/1536867X1601600108](https://doi.org/10.1177/1536867X1601600108)
|
|
71
|
-
- von Bismarck-Osten C, Borusyak K, Schönberg U (2022) The role of schools in transmission of the SARS-CoV-2 virus: quasi-experimental evidence from Germany. *Economic Policy* 37(109): 87–130. [10.1093/epolic/eiac001](https://doi.org/10.1093/epolic/eiac001)
|
|
72
|
-
- Wieland T (2025) Assessing the effectiveness of non-pharmaceutical interventions in the SARS-CoV-2 pandemic: results of a natural experiment regarding Baden-Württemberg (Germany) and Switzerland in the second infection wave. *Journal of Public Health: From Theory to Practice* 33(11): 2497-2511. [10.1007/s10389-024-02218-x](https://doi.org/10.1007/s10389-024-02218-x)
|
|
73
|
-
- Wooldridge JM (2012) *Introductory Econometrics. A Modern Approach*.
|
|
74
|
-
|
|
75
|
-
|
|
76
81
|
## Examples
|
|
77
82
|
|
|
78
83
|
```python
|
|
@@ -143,9 +148,27 @@ curfew_model_withgroups.plot_group_treatment_effects(
|
|
|
143
148
|
See the /tests directory for usage examples of most of the included functions.
|
|
144
149
|
|
|
145
150
|
|
|
146
|
-
##
|
|
151
|
+
## Literature
|
|
147
152
|
|
|
148
|
-
|
|
153
|
+
- Baker AC, Larcker DF, Wang CCY (2022) How much should we trust staggered difference-in-differences estimates? *Journal of Financial Economics* 144(2): 370-395. [10.1016/j.jfineco.2022.01.004](https://doi.org/10.1016/j.jfineco.2022.01.004)
|
|
154
|
+
- Card D, Krueger AD (1994) Minimum Wages and Employment: A Case Study of the Fast Food Industry in New Jersey and Pennsylvania. *The American Economic Review* 84(4): 772-793. [JSTOR](https://www.jstor.org/stable/2677856)
|
|
155
|
+
- de Haas S, Götz G, Heim S (2022) Measuring the effect of COVID‑19‑related night curfews in a bundled intervention within Germany. *Scientific Reports* 12: 19732. [10.1038/s41598-022-24086-9](https://doi.org/10.1038/s41598-022-24086-9)
|
|
156
|
+
- Goodman-Bacon A (2021) Difference-in-differences with variation in treatment timing. *Journal of Econometrics* 225(2): 254-277. [10.1016/j.jeconom.2021.03.014](https://doi.org/10.1016/j.jeconom.2021.03.014)
|
|
157
|
+
- Greene WH (2012) *Econometric Analysis*.
|
|
158
|
+
- Goldfarb A, Tucker C, Wang Y (2022) Conducting Research in Marketing with Quasi-Experiments. *Journal of Marketing* 86(3): 1-19. [10.1177/00222429221082977](https://doi.org/10.1177/00222429221082977)
|
|
159
|
+
- Isporhing IE, Lipfert M, Pestel N (2021) Does re-opening schools contribute to the spread of SARS-CoV-2? Evidence from staggered summer breaks in Germany. *Journal of Public Economics* 198: 104426. [10.1016/j.jpubeco.2021.104426](https://doi.org/10.1016/j.jpubeco.2021.104426)
|
|
160
|
+
- Li KT, Luo L, Pattabhiramaiah A (2024) Causal Inference with Quasi-Experimental Data. *IMPACT at JMR* November 13, 2024. [AMA](https://www.ama.org/marketing-news/causal-inference-with-quasi-experimental-data/)
|
|
161
|
+
- Olden A (2018) What do you buy when no one's watching? The effect of self-service checkouts on the composition of sales in retail. Discussion paper FOR 3/18, Norwegian School of Economics, Norway. [http://hdl.handle.net/11250/2490886](http://hdl.handle.net/11250/2490886)
|
|
162
|
+
- Olden A, Moen J (2022) The triple difference estimator. *The Econometrics Journal* 25(3): 531-553. [10.1093/ectj/utac010](https://doi.org/10.1093/ectj/utac010)
|
|
163
|
+
- Strassmann A, Çolak Y, Serra-Burriel M, Nordestgaard BG, Turk A, Afzal S, Puhan MA (2023) Nationwide indoor smoking ban and impact on smoking behaviour and lung function: a two-population natural experiment. *Thorax* 78(2): 144-150. [10.1136/thoraxjnl-2021-218436](https://doi.org/10.1136/thoraxjnl-2021-218436)
|
|
164
|
+
- Villa JM (2016) diff: Simplifying the estimation of difference-in-differences treatment effects. *The Stata Journal* 16(1): 52-71. [10.1177/1536867X1601600108](https://doi.org/10.1177/1536867X1601600108)
|
|
165
|
+
- von Bismarck-Osten C, Borusyak K, Schönberg U (2022) The role of schools in transmission of the SARS-CoV-2 virus: quasi-experimental evidence from Germany. *Economic Policy* 37(109): 87–130. [10.1093/epolic/eiac001](https://doi.org/10.1093/epolic/eiac001)
|
|
166
|
+
- Wieland T (2025) Assessing the effectiveness of non-pharmaceutical interventions in the SARS-CoV-2 pandemic: results of a natural experiment regarding Baden-Württemberg (Germany) and Switzerland in the second infection wave. *Journal of Public Health: From Theory to Practice* 33(11): 2497-2511. [10.1007/s10389-024-02218-x](https://doi.org/10.1007/s10389-024-02218-x)
|
|
167
|
+
- Wooldridge JM (2012) *Introductory Econometrics. A Modern Approach*.
|
|
149
168
|
|
|
150
|
-
|
|
151
|
-
|
|
169
|
+
|
|
170
|
+
## What's new (v2.2.5)
|
|
171
|
+
- Bugfixes:
|
|
172
|
+
- Incorrect import
|
|
173
|
+
- Other:
|
|
174
|
+
- Update README
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from setuptools import setup, find_packages
|
|
2
2
|
import os
|
|
3
|
-
import diffindiff.config as config
|
|
4
3
|
|
|
5
4
|
def read_README():
|
|
6
5
|
with open(os.path.join(os.path.dirname(__file__), 'README.md'), encoding='utf-8') as f:
|
|
@@ -8,7 +7,7 @@ def read_README():
|
|
|
8
7
|
|
|
9
8
|
setup(
|
|
10
9
|
name='diffindiff',
|
|
11
|
-
version='2.2.
|
|
10
|
+
version='2.2.5',
|
|
12
11
|
description='diffindiff: Python library for convenient Difference-in-Differences Analyses',
|
|
13
12
|
packages=find_packages(include=["diffindiff", "diffindiff.tests"]),
|
|
14
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
|
|
File without changes
|