drdid 1.1.6__py3-none-any.whl
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.
- drdid/__init__.py +0 -0
- drdid/_version_.py +1 -0
- drdid/drdid.py +275 -0
- drdid/ipwd_did.py +160 -0
- drdid/reg_did.py +196 -0
- drdid/utils.py +94 -0
- drdid-1.1.6.dist-info/METADATA +26 -0
- drdid-1.1.6.dist-info/RECORD +11 -0
- drdid-1.1.6.dist-info/WHEEL +5 -0
- drdid-1.1.6.dist-info/licenses/LICENSE +21 -0
- drdid-1.1.6.dist-info/top_level.txt +1 -0
drdid/__init__.py
ADDED
|
File without changes
|
drdid/_version_.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = '1.1.6'
|
drdid/drdid.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numpy import ndarray
|
|
3
|
+
import statsmodels.api as sm
|
|
4
|
+
from .utils import *
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import statsmodels.api as sm
|
|
8
|
+
def drdid_rc(y, post, D, covariates, i_weights=None):
|
|
9
|
+
|
|
10
|
+
# Convert inputs to numpy arrays
|
|
11
|
+
D = np.asarray(D).flatten()
|
|
12
|
+
n = len(D)
|
|
13
|
+
y = np.asarray(y).flatten()
|
|
14
|
+
post = np.asarray(post).flatten()
|
|
15
|
+
|
|
16
|
+
# Add constant to covariate matrix
|
|
17
|
+
if covariates is None:
|
|
18
|
+
int_cov = np.ones((n, 1))
|
|
19
|
+
else:
|
|
20
|
+
covariates = np.asarray(covariates)
|
|
21
|
+
if np.all(covariates[:, 0] == 1):
|
|
22
|
+
int_cov = covariates
|
|
23
|
+
else:
|
|
24
|
+
int_cov = np.column_stack((np.ones(n), covariates))
|
|
25
|
+
|
|
26
|
+
# Normalize weights
|
|
27
|
+
if i_weights is None:
|
|
28
|
+
i_weights = np.ones(n)
|
|
29
|
+
i_weights = np.asanyarray(i_weights).flatten()
|
|
30
|
+
i_weights = i_weights / np.mean(i_weights)
|
|
31
|
+
|
|
32
|
+
# Compute the Pscore by MLE
|
|
33
|
+
pscore_model = sm.GLM(D, int_cov, family=sm.families.Binomial(), freq_weights=i_weights)
|
|
34
|
+
pscore_results = pscore_model.fit()
|
|
35
|
+
if not pscore_results.converged:
|
|
36
|
+
print("Warning: glm algorithm did not converge")
|
|
37
|
+
if np.any(np.isnan(pscore_results.params)):
|
|
38
|
+
raise ValueError("Propensity score model coefficients have NA components. \n Multicollinearity (or lack of variation) of covariates is a likely reason.")
|
|
39
|
+
ps_fit = pscore_results.predict()
|
|
40
|
+
ps_fit = np.minimum(ps_fit, 1 - 1e-16)
|
|
41
|
+
|
|
42
|
+
# Compute the Outcome regression for the control group at the pre-treatment period
|
|
43
|
+
mask_cont_pre = (D == 0) & (post == 0)
|
|
44
|
+
reg_cont_pre = sm.WLS(y[mask_cont_pre], int_cov[mask_cont_pre], weights=i_weights[mask_cont_pre]).fit()
|
|
45
|
+
if np.any(np.isnan(reg_cont_pre.params)):
|
|
46
|
+
raise ValueError("Outcome regression model coefficients have NA components. \n Multicollinearity (or lack of variation) of covariates is a likely reason.")
|
|
47
|
+
out_y_cont_pre = np.dot(int_cov, reg_cont_pre.params)
|
|
48
|
+
|
|
49
|
+
# Compute the Outcome regression for the control group at the post-treatment period
|
|
50
|
+
mask_cont_post = (D == 0) & (post == 1)
|
|
51
|
+
reg_cont_post = sm.WLS(y[mask_cont_post], int_cov[mask_cont_post], weights=i_weights[mask_cont_post]).fit()
|
|
52
|
+
if np.any(np.isnan(reg_cont_post.params)):
|
|
53
|
+
raise ValueError("Outcome regression model coefficients have NA components. \n Multicollinearity (or lack of variation) of covariates is a likely reason.")
|
|
54
|
+
out_y_cont_post = np.dot(int_cov, reg_cont_post.params)
|
|
55
|
+
|
|
56
|
+
# Combine the ORs for control group
|
|
57
|
+
out_y_cont = post * out_y_cont_post + (1 - post) * out_y_cont_pre
|
|
58
|
+
|
|
59
|
+
# Compute the Outcome regression for the treated group at the pre-treatment period
|
|
60
|
+
mask_treat_pre = (D == 1) & (post == 0)
|
|
61
|
+
reg_treat_pre = sm.WLS(y[mask_treat_pre], int_cov[mask_treat_pre], weights=i_weights[mask_treat_pre]).fit()
|
|
62
|
+
out_y_treat_pre = np.dot(int_cov, reg_treat_pre.params)
|
|
63
|
+
|
|
64
|
+
# Compute the Outcome regression for the treated group at the post-treatment period
|
|
65
|
+
mask_treat_post = (D == 1) & (post == 1)
|
|
66
|
+
reg_treat_post = sm.WLS(y[mask_treat_post], int_cov[mask_treat_post], weights=i_weights[mask_treat_post]).fit()
|
|
67
|
+
out_y_treat_post = np.dot(int_cov, reg_treat_post.params)
|
|
68
|
+
|
|
69
|
+
# Compute weights
|
|
70
|
+
w_treat_pre = i_weights * D * (1 - post)
|
|
71
|
+
w_treat_post = i_weights * D * post
|
|
72
|
+
w_cont_pre = i_weights * ps_fit * (1 - D) * (1 - post) / (1 - ps_fit)
|
|
73
|
+
w_cont_post = i_weights * ps_fit * (1 - D) * post / (1 - ps_fit)
|
|
74
|
+
w_d = i_weights * D
|
|
75
|
+
w_dt1 = i_weights * D * post
|
|
76
|
+
w_dt0 = i_weights * D * (1 - post)
|
|
77
|
+
|
|
78
|
+
# Elements of the influence function (summands)
|
|
79
|
+
eta_treat_pre = w_treat_pre * (y - out_y_cont) / np.mean(w_treat_pre)
|
|
80
|
+
eta_treat_post = w_treat_post * (y - out_y_cont) / np.mean(w_treat_post)
|
|
81
|
+
eta_cont_pre = w_cont_pre * (y - out_y_cont) / np.mean(w_cont_pre)
|
|
82
|
+
eta_cont_post = w_cont_post * (y - out_y_cont) / np.mean(w_cont_post)
|
|
83
|
+
|
|
84
|
+
# Extra elements for the locally efficient DRDID
|
|
85
|
+
eta_d_post = w_d * (out_y_treat_post - out_y_cont_post) / np.mean(w_d)
|
|
86
|
+
eta_dt1_post = w_dt1 * (out_y_treat_post - out_y_cont_post) / np.mean(w_dt1)
|
|
87
|
+
eta_d_pre = w_d * (out_y_treat_pre - out_y_cont_pre) / np.mean(w_d)
|
|
88
|
+
eta_dt0_pre = w_dt0 * (out_y_treat_pre - out_y_cont_pre) / np.mean(w_dt0)
|
|
89
|
+
|
|
90
|
+
# Estimator of each component
|
|
91
|
+
att_treat_pre = np.mean(eta_treat_pre)
|
|
92
|
+
att_treat_post = np.mean(eta_treat_post)
|
|
93
|
+
att_cont_pre = np.mean(eta_cont_pre)
|
|
94
|
+
att_cont_post = np.mean(eta_cont_post)
|
|
95
|
+
att_d_post = np.mean(eta_d_post)
|
|
96
|
+
att_dt1_post = np.mean(eta_dt1_post)
|
|
97
|
+
att_d_pre = np.mean(eta_d_pre)
|
|
98
|
+
att_dt0_pre = np.mean(eta_dt0_pre)
|
|
99
|
+
|
|
100
|
+
# ATT estimator
|
|
101
|
+
dr_att = (att_treat_post - att_treat_pre) - (att_cont_post - att_cont_pre) + \
|
|
102
|
+
(att_d_post - att_dt1_post) - (att_d_pre - att_dt0_pre)
|
|
103
|
+
|
|
104
|
+
# Compute influence functions
|
|
105
|
+
weights_ols_pre = i_weights * (1 - D) * (1 - post)
|
|
106
|
+
weights_ols_pre = np.array(weights_ols_pre)
|
|
107
|
+
wols_x_pre = weights_ols_pre[:, np.newaxis] * int_cov
|
|
108
|
+
wols_eX_pre = weights_ols_pre[:, np.newaxis] * (y - out_y_cont_pre)[:, np.newaxis] * int_cov
|
|
109
|
+
XpX_inv_pre = np.linalg.inv(np.dot(wols_x_pre.T, int_cov) / n)
|
|
110
|
+
asy_lin_rep_ols_pre = np.dot(wols_eX_pre, XpX_inv_pre)
|
|
111
|
+
|
|
112
|
+
weights_ols_post = i_weights * (1 - D) * post
|
|
113
|
+
|
|
114
|
+
wols_x_post = weights_ols_post[:, np.newaxis] * int_cov
|
|
115
|
+
wols_eX_post = weights_ols_post[:, np.newaxis] * (y - out_y_cont_post)[:, np.newaxis] * int_cov
|
|
116
|
+
XpX_inv_post = np.linalg.inv(np.dot(wols_x_post.T, int_cov) / n)
|
|
117
|
+
asy_lin_rep_ols_post = np.dot(wols_eX_post, XpX_inv_post)
|
|
118
|
+
|
|
119
|
+
weights_ols_pre_treat = i_weights * D * (1 - post)
|
|
120
|
+
wols_x_pre_treat = weights_ols_pre_treat[:, np.newaxis] * int_cov
|
|
121
|
+
wols_eX_pre_treat = weights_ols_pre_treat[:, np.newaxis] * (y - out_y_treat_pre)[:, np.newaxis] * int_cov
|
|
122
|
+
XpX_inv_pre_treat = np.linalg.inv(np.dot(wols_x_pre_treat.T, int_cov) / n)
|
|
123
|
+
asy_lin_rep_ols_pre_treat = np.dot(wols_eX_pre_treat, XpX_inv_pre_treat)
|
|
124
|
+
|
|
125
|
+
weights_ols_post_treat = i_weights * D * post
|
|
126
|
+
wols_x_post_treat = weights_ols_post_treat[:, np.newaxis] * int_cov
|
|
127
|
+
wols_eX_post_treat = weights_ols_post_treat[:, np.newaxis] * (y - out_y_treat_post)[:, np.newaxis] * int_cov
|
|
128
|
+
XpX_inv_post_treat = np.linalg.inv(np.dot(wols_x_post_treat.T, int_cov) / n)
|
|
129
|
+
asy_lin_rep_ols_post_treat = np.dot(wols_eX_post_treat, XpX_inv_post_treat)
|
|
130
|
+
|
|
131
|
+
score_ps = i_weights[:, np.newaxis] * (D - ps_fit)[:, np.newaxis] * int_cov
|
|
132
|
+
Hessian_ps = pscore_results.cov_params() * n
|
|
133
|
+
asy_lin_rep_ps = np.dot(score_ps, Hessian_ps)
|
|
134
|
+
|
|
135
|
+
# Influence function components
|
|
136
|
+
inf_treat_pre = eta_treat_pre - w_treat_pre * att_treat_pre / np.mean(w_treat_pre)
|
|
137
|
+
inf_treat_post = eta_treat_post - w_treat_post * att_treat_post / np.mean(w_treat_post)
|
|
138
|
+
|
|
139
|
+
M1_post = -np.mean(w_treat_post[:, np.newaxis] * post[:, np.newaxis] * int_cov, axis=0) / np.mean(w_treat_post)
|
|
140
|
+
M1_pre = -np.mean(w_treat_pre[:, np.newaxis] * (1 - post)[:, np.newaxis] * int_cov, axis=0) / np.mean(w_treat_pre)
|
|
141
|
+
|
|
142
|
+
inf_treat_or_post = np.dot(asy_lin_rep_ols_post, M1_post)
|
|
143
|
+
inf_treat_or_pre = np.dot(asy_lin_rep_ols_pre, M1_pre)
|
|
144
|
+
inf_treat_or = inf_treat_or_post + inf_treat_or_pre
|
|
145
|
+
|
|
146
|
+
inf_treat = inf_treat_post - inf_treat_pre + inf_treat_or
|
|
147
|
+
|
|
148
|
+
inf_cont_pre = eta_cont_pre - w_cont_pre * att_cont_pre / np.mean(w_cont_pre)
|
|
149
|
+
inf_cont_post = eta_cont_post - w_cont_post * att_cont_post / np.mean(w_cont_post)
|
|
150
|
+
|
|
151
|
+
M2_pre = np.mean(w_cont_pre[:, np.newaxis] * (y - out_y_cont - att_cont_pre)[:, np.newaxis] * int_cov, axis=0) / np.mean(w_cont_pre)
|
|
152
|
+
M2_post = np.mean(w_cont_post[:, np.newaxis] * (y - out_y_cont - att_cont_post)[:, np.newaxis] * int_cov, axis=0) / np.mean(w_cont_post)
|
|
153
|
+
|
|
154
|
+
inf_cont_ps = np.dot(asy_lin_rep_ps, M2_post - M2_pre)
|
|
155
|
+
|
|
156
|
+
M3_post = -np.mean(w_cont_post[:, np.newaxis] * post[:, np.newaxis] * int_cov, axis=0) / np.mean(w_cont_post)
|
|
157
|
+
M3_pre = -np.mean(w_cont_pre[:, np.newaxis] * (1 - post)[:, np.newaxis] * int_cov, axis=0) / np.mean(w_cont_pre)
|
|
158
|
+
|
|
159
|
+
inf_cont_or_post = np.dot(asy_lin_rep_ols_post, M3_post)
|
|
160
|
+
inf_cont_or_pre = np.dot(asy_lin_rep_ols_pre, M3_pre)
|
|
161
|
+
inf_cont_or = inf_cont_or_post + inf_cont_or_pre
|
|
162
|
+
|
|
163
|
+
inf_cont = inf_cont_post - inf_cont_pre + inf_cont_ps + inf_cont_or
|
|
164
|
+
|
|
165
|
+
dr_att_inf_func1 = inf_treat - inf_cont
|
|
166
|
+
|
|
167
|
+
inf_eff1 = eta_d_post - w_d * att_d_post / np.mean(w_d)
|
|
168
|
+
inf_eff2 = eta_dt1_post - w_dt1 * att_dt1_post / np.mean(w_dt1)
|
|
169
|
+
inf_eff3 = eta_d_pre - w_d * att_d_pre / np.mean(w_d)
|
|
170
|
+
inf_eff4 = eta_dt0_pre - w_dt0 * att_dt0_pre / np.mean(w_dt0)
|
|
171
|
+
inf_eff = (inf_eff1 - inf_eff2) - (inf_eff3 - inf_eff4)
|
|
172
|
+
|
|
173
|
+
mom_post = np.mean((w_d[:, np.newaxis] / np.mean(w_d) - w_dt1[:, np.newaxis] / np.mean(w_dt1)) * int_cov, axis=0)
|
|
174
|
+
mom_pre = np.mean((w_d[:, np.newaxis] / np.mean(w_d) - w_dt0[:, np.newaxis] / np.mean(w_dt0)) * int_cov, axis=0)
|
|
175
|
+
inf_or_post = np.dot(asy_lin_rep_ols_post_treat - asy_lin_rep_ols_post, mom_post)
|
|
176
|
+
inf_or_pre = np.dot(asy_lin_rep_ols_pre_treat - asy_lin_rep_ols_pre, mom_pre)
|
|
177
|
+
inf_or = inf_or_post - inf_or_pre
|
|
178
|
+
|
|
179
|
+
dr_att_inf_func = dr_att_inf_func1 + inf_eff + inf_or
|
|
180
|
+
se = np.std(dr_att_inf_func, ddof=1) / np.sqrt(n)
|
|
181
|
+
#print(f"att: {dr_att} \t se: {se}")
|
|
182
|
+
return dr_att, dr_att_inf_func
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def drdid_panel(y1, y0, D, covariates, i_weights=None):
|
|
187
|
+
|
|
188
|
+
# Convert inputs to numpy arrays
|
|
189
|
+
D = np.asarray(D).flatten()
|
|
190
|
+
n = len(D)
|
|
191
|
+
deltaY = np.asarray(y1 - y0).flatten()
|
|
192
|
+
i_weights = np.asarray(i_weights).flatten()
|
|
193
|
+
|
|
194
|
+
# Add constant to covariate matrix
|
|
195
|
+
if covariates is None:
|
|
196
|
+
int_cov = np.ones((n, 1))
|
|
197
|
+
else:
|
|
198
|
+
covariates = np.asarray(covariates)
|
|
199
|
+
if np.all(covariates[:, 0] == 1):
|
|
200
|
+
int_cov = covariates
|
|
201
|
+
else:
|
|
202
|
+
int_cov = np.column_stack((np.ones(n), covariates))
|
|
203
|
+
|
|
204
|
+
# Weights
|
|
205
|
+
if i_weights is None:
|
|
206
|
+
i_weights = np.ones(n)
|
|
207
|
+
elif np.min(i_weights) < 0:
|
|
208
|
+
raise ValueError("i_weights must be non-negative")
|
|
209
|
+
|
|
210
|
+
# Normalize weights
|
|
211
|
+
i_weights = i_weights / np.mean(i_weights)
|
|
212
|
+
# print(D.mean())
|
|
213
|
+
|
|
214
|
+
# Compute the Pscore by MLE
|
|
215
|
+
pscore_model = sm.GLM(D, int_cov, family=sm.families.Binomial(), freq_weights=i_weights)
|
|
216
|
+
pscore_results = pscore_model.fit()
|
|
217
|
+
# print(D.mean())
|
|
218
|
+
# print(pscore_results.summary2())
|
|
219
|
+
if not pscore_results.converged:
|
|
220
|
+
print("Warning: glm algorithm did not converge")
|
|
221
|
+
if np.any(np.isnan(pscore_results.params)):
|
|
222
|
+
raise ValueError("Propensity score model coefficients have NA components. \n Multicollinearity (or lack of variation) of covariates is a likely reason.")
|
|
223
|
+
ps_fit = pscore_results.predict()
|
|
224
|
+
ps_fit = np.minimum(ps_fit, 1 - 1e-16)
|
|
225
|
+
# print(ps_fit)
|
|
226
|
+
|
|
227
|
+
# Compute the Outcome regression for the control group using wols
|
|
228
|
+
mask = D == 0
|
|
229
|
+
reg_model = sm.WLS(deltaY[mask], int_cov[mask], weights=i_weights[mask])
|
|
230
|
+
reg_results = reg_model.fit()
|
|
231
|
+
if np.any(np.isnan(reg_results.params)):
|
|
232
|
+
raise ValueError("Outcome regression model coefficients have NA components. \n Multicollinearity (or lack of variation) of covariates is a likely reason.")
|
|
233
|
+
out_delta = np.dot(int_cov, reg_results.params)
|
|
234
|
+
|
|
235
|
+
# Compute Traditional Doubly Robust DiD estimators
|
|
236
|
+
w_treat = i_weights * D
|
|
237
|
+
w_cont = i_weights * ps_fit * (1 - D) / (1 - ps_fit)
|
|
238
|
+
dr_att_treat = w_treat * (deltaY - out_delta)
|
|
239
|
+
dr_att_cont = w_cont * (deltaY - out_delta)
|
|
240
|
+
|
|
241
|
+
eta_treat = np.mean(dr_att_treat) / np.mean(w_treat)
|
|
242
|
+
eta_cont = np.mean(dr_att_cont) / np.mean(w_cont)
|
|
243
|
+
|
|
244
|
+
dr_att = eta_treat - eta_cont
|
|
245
|
+
|
|
246
|
+
# Compute influence function
|
|
247
|
+
weights_ols = i_weights * (1 - D)
|
|
248
|
+
wols_x = weights_ols[:, np.newaxis] * int_cov
|
|
249
|
+
wols_eX = weights_ols[:, np.newaxis] * (deltaY - out_delta)[:, np.newaxis] * int_cov
|
|
250
|
+
XpX_inv = np.linalg.inv(np.dot(wols_x.T, int_cov) / n)
|
|
251
|
+
asy_lin_rep_wols = np.dot(wols_eX, XpX_inv)
|
|
252
|
+
|
|
253
|
+
score_ps = i_weights[:, np.newaxis] * (D - ps_fit)[:, np.newaxis] * int_cov
|
|
254
|
+
Hessian_ps = pscore_results.cov_params() * n
|
|
255
|
+
asy_lin_rep_ps = np.dot(score_ps, Hessian_ps)
|
|
256
|
+
|
|
257
|
+
inf_treat_1 = dr_att_treat - w_treat * eta_treat
|
|
258
|
+
M1 = np.mean(w_treat[:, np.newaxis] * int_cov, axis=0)
|
|
259
|
+
inf_treat_2 = np.dot(asy_lin_rep_wols, M1)
|
|
260
|
+
inf_treat = (inf_treat_1 - inf_treat_2) / np.mean(w_treat)
|
|
261
|
+
|
|
262
|
+
inf_cont_1 = dr_att_cont - w_cont * eta_cont
|
|
263
|
+
M2 = np.mean(w_cont[:, np.newaxis] * (deltaY - out_delta - eta_cont)[:, np.newaxis] * int_cov, axis=0)
|
|
264
|
+
inf_cont_2 = np.dot(asy_lin_rep_ps, M2)
|
|
265
|
+
M3 = np.mean(w_cont[:, np.newaxis] * int_cov, axis=0)
|
|
266
|
+
inf_cont_3 = np.dot(asy_lin_rep_wols, M3)
|
|
267
|
+
inf_control = (inf_cont_1 + inf_cont_2 - inf_cont_3) / np.mean(w_cont)
|
|
268
|
+
|
|
269
|
+
dr_att_inf_func = inf_treat - inf_control
|
|
270
|
+
se = np.std(dr_att_inf_func, ddof=1) / np.sqrt(n)
|
|
271
|
+
# print(f"att: {dr_att} \t se: {se}")
|
|
272
|
+
return dr_att, dr_att_inf_func
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
|
drdid/ipwd_did.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
from .utils import *
|
|
2
|
+
|
|
3
|
+
def std_ipw_did_panel(y1, y0, D, covariates, i_weights = None):
|
|
4
|
+
# print("ipw: panel")
|
|
5
|
+
D = np.asarray(D).flatten()
|
|
6
|
+
n = len(D)
|
|
7
|
+
delta_y = np.asarray(y1 - y0).flatten()
|
|
8
|
+
int_cov = np.ones((n, 1))
|
|
9
|
+
i_weights = np.asarray(i_weights).flatten()
|
|
10
|
+
if covariates is not None:
|
|
11
|
+
covariates = np.asarray(covariates)
|
|
12
|
+
if np.all(covariates[:, 0] == 1):
|
|
13
|
+
int_cov = covariates
|
|
14
|
+
else:
|
|
15
|
+
int_cov = np.column_stack((np.ones(n), covariates))
|
|
16
|
+
|
|
17
|
+
if i_weights is None:
|
|
18
|
+
i_weights = np.ones(n)
|
|
19
|
+
elif np.min(i_weights) < 0:
|
|
20
|
+
raise ValueError("i_weights must be non-negative")
|
|
21
|
+
|
|
22
|
+
i_weights = i_weights / np.mean(i_weights)
|
|
23
|
+
pscore_model = sm.GLM(D, int_cov, family=sm.families.Binomial(), freq_weights=i_weights)
|
|
24
|
+
pscore_results = pscore_model.fit()
|
|
25
|
+
# print(D.mean())
|
|
26
|
+
# print(pscore_results.summary2())
|
|
27
|
+
if not pscore_results.converged:
|
|
28
|
+
print("Warning: glm algorithm did not converge")
|
|
29
|
+
if np.any(np.isnan(pscore_results.params)):
|
|
30
|
+
raise ValueError("Propensity score model coefficients have NA components. \n Multicollinearity (or lack of variation) of covariates is a likely reason.")
|
|
31
|
+
ps_fit = pscore_results.predict()
|
|
32
|
+
ps_fit = np.minimum(ps_fit, 1 - 1e-16)
|
|
33
|
+
|
|
34
|
+
w_treat = i_weights * D
|
|
35
|
+
w_cont = i_weights * ps_fit * (1 - D) / (1 - ps_fit)
|
|
36
|
+
|
|
37
|
+
att_treat = w_treat * delta_y
|
|
38
|
+
att_cont = w_cont * delta_y
|
|
39
|
+
|
|
40
|
+
eta_treat = mean(att_treat) / mean(w_treat)
|
|
41
|
+
eta_cont = mean(att_cont) / mean(w_cont)
|
|
42
|
+
|
|
43
|
+
ipw_att = eta_treat - eta_cont
|
|
44
|
+
|
|
45
|
+
score_ps = i_weights[:, np.newaxis] * (D - ps_fit)[:, np.newaxis] * int_cov
|
|
46
|
+
Hessian_ps = pscore_results.cov_params() * n
|
|
47
|
+
asy_lin_rep_ps = np.dot(score_ps, Hessian_ps)
|
|
48
|
+
|
|
49
|
+
inf_treat = (att_treat - w_treat * eta_treat) / mean(w_treat)
|
|
50
|
+
inf_cont_1 = att_cont - w_cont * eta_cont
|
|
51
|
+
pre_m2 = w_cont * (delta_y - eta_cont)
|
|
52
|
+
M2 = np.mean(pre_m2[:, np.newaxis] * int_cov, axis = 0)
|
|
53
|
+
# print(M2)
|
|
54
|
+
inf_cont_2 = np.dot(asy_lin_rep_ps, M2)
|
|
55
|
+
|
|
56
|
+
inf_control = (inf_cont_1 + inf_cont_2) / np.mean(w_cont)
|
|
57
|
+
att_inf_func = inf_treat - inf_control
|
|
58
|
+
se = np.std(att_inf_func, ddof=1) / np.sqrt(n)
|
|
59
|
+
# print(f"att: {ipw_att} \t se: {se}")
|
|
60
|
+
# print(np.std(att_inf_func) / np.sqrt(n))
|
|
61
|
+
return ipw_att, att_inf_func
|
|
62
|
+
|
|
63
|
+
def std_ipw_did_rc(y, post, D, covariates, i_weights = None):
|
|
64
|
+
# print("ipw: rc")
|
|
65
|
+
D = np.asarray(D).flatten()
|
|
66
|
+
y = np.asarray(y).flatten()
|
|
67
|
+
post = np.asarray(post).flatten()
|
|
68
|
+
n = len(D)
|
|
69
|
+
if covariates is None:
|
|
70
|
+
int_cov = np.ones((n, 1))
|
|
71
|
+
else:
|
|
72
|
+
covariates = np.asarray(covariates)
|
|
73
|
+
if np.all(covariates[:, 0] == 1):
|
|
74
|
+
int_cov = covariates
|
|
75
|
+
else:
|
|
76
|
+
int_cov = np.column_stack((np.ones(n), covariates))
|
|
77
|
+
|
|
78
|
+
# Pesos
|
|
79
|
+
if i_weights is None:
|
|
80
|
+
i_weights = np.ones(n)
|
|
81
|
+
else:
|
|
82
|
+
i_weights = np.asarray(i_weights)
|
|
83
|
+
if np.min(i_weights) < 0:
|
|
84
|
+
raise ValueError("i_weights must be non-negative")
|
|
85
|
+
|
|
86
|
+
# Normalizar pesos
|
|
87
|
+
i_weights = np.asarray(i_weights).flatten()
|
|
88
|
+
i_weights = i_weights / np.mean(i_weights)
|
|
89
|
+
|
|
90
|
+
pscore_model = sm.GLM(D, int_cov, family=sm.families.Binomial(), freq_weights=i_weights)
|
|
91
|
+
pscore_results = pscore_model.fit()
|
|
92
|
+
if not pscore_results.converged:
|
|
93
|
+
print("Warning: glm algorithm did not converge")
|
|
94
|
+
if np.any(np.isnan(pscore_results.params)):
|
|
95
|
+
raise ValueError("Propensity score model coefficients have NA components. \n Multicollinearity (or lack of variation) of covariates is a likely reason.")
|
|
96
|
+
ps_fit = pscore_results.predict()
|
|
97
|
+
ps_fit = np.minimum(ps_fit, 1 - 1e-16)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
w_treat_pre = i_weights * D * (1 - post)
|
|
101
|
+
w_treat_post = i_weights * D * post
|
|
102
|
+
# print(np.mean(w_treat_pre))
|
|
103
|
+
|
|
104
|
+
w_cont_pre = i_weights * ps_fit * (1 - D) * (1 - post)/(1 - ps_fit)
|
|
105
|
+
w_cont_post = i_weights * ps_fit * (1 - D) * post/(1 - ps_fit)
|
|
106
|
+
|
|
107
|
+
# Elements of the influence function (summands)
|
|
108
|
+
eta_treat_pre = w_treat_pre * y / np.mean(w_treat_pre)
|
|
109
|
+
eta_treat_post = w_treat_post * y / np.mean(w_treat_post)
|
|
110
|
+
# print(eta_treat_pre)
|
|
111
|
+
|
|
112
|
+
eta_cont_pre = w_cont_pre * y / np.mean(w_cont_pre)
|
|
113
|
+
eta_cont_post = w_cont_post * y / np.mean(w_cont_post)
|
|
114
|
+
|
|
115
|
+
# Estimator of each component
|
|
116
|
+
att_treat_pre = np.mean(eta_treat_pre)
|
|
117
|
+
att_treat_post = np.mean(eta_treat_post)
|
|
118
|
+
att_cont_pre = np.mean(eta_cont_pre)
|
|
119
|
+
att_cont_post = np.mean(eta_cont_post)
|
|
120
|
+
ipw_att = (att_treat_post - att_treat_pre) - (att_cont_post - att_cont_pre)
|
|
121
|
+
|
|
122
|
+
score_ps = (i_weights * (D - ps_fit))[:, np.newaxis] * int_cov
|
|
123
|
+
Hessian_ps = pscore_results.cov_params() * n
|
|
124
|
+
asy_lyn_rep_ps = np.dot(score_ps, Hessian_ps)
|
|
125
|
+
|
|
126
|
+
inf_treat_pre = eta_treat_pre - w_treat_pre * att_treat_pre/np.mean(w_treat_pre)
|
|
127
|
+
inf_treat_post = eta_treat_post - w_treat_post * att_treat_post/np.mean(w_treat_post)
|
|
128
|
+
inf_treat = inf_treat_post - inf_treat_pre
|
|
129
|
+
# Now, get the influence function of control component
|
|
130
|
+
# Leading term of the influence function: no estimation effect
|
|
131
|
+
inf_cont_pre = eta_cont_pre - w_cont_pre * att_cont_pre/np.mean(w_cont_pre)
|
|
132
|
+
inf_cont_post = eta_cont_post - w_cont_post * att_cont_post/np.mean(w_cont_post)
|
|
133
|
+
inf_cont = inf_cont_post - inf_cont_pre
|
|
134
|
+
|
|
135
|
+
# Estimation effect from gamma hat (pscore)
|
|
136
|
+
# Derivative matrix (k x 1 vector)
|
|
137
|
+
|
|
138
|
+
M2_pre = np.mean((w_cont_pre *(y - att_cont_pre))[:, np.newaxis] * int_cov, axis = 0)/np.mean(w_cont_pre)
|
|
139
|
+
M2_post = np.mean((w_cont_post *(y - att_cont_post))[:, np.newaxis] * int_cov, axis = 0)/np.mean(w_cont_post)
|
|
140
|
+
|
|
141
|
+
# Now the influence function related to estimation effect of pscores
|
|
142
|
+
M2 = M2_post - M2_pre
|
|
143
|
+
# print()
|
|
144
|
+
|
|
145
|
+
inf_cont_ps = np.dot(asy_lyn_rep_ps, M2)
|
|
146
|
+
|
|
147
|
+
# Influence function for the control component
|
|
148
|
+
inf_cont = inf_cont + inf_cont_ps
|
|
149
|
+
|
|
150
|
+
#get the influence function of the DR estimator (put all pieces together)
|
|
151
|
+
att_inf_func = inf_treat - inf_cont
|
|
152
|
+
# print(np.std(att_inf_func) / np.sqrt(n))
|
|
153
|
+
se = np.std(att_inf_func, ddof=1) / np.sqrt(n)
|
|
154
|
+
# print(f"att: {ipw_att} \t se: {se}")
|
|
155
|
+
return ipw_att, att_inf_func
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
drdid/reg_did.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import statsmodels.api as sm
|
|
3
|
+
from numpy import ndarray
|
|
4
|
+
|
|
5
|
+
from .utils import *
|
|
6
|
+
import numpy as np
|
|
7
|
+
import statsmodels.api as sm
|
|
8
|
+
from numpy import ndarray
|
|
9
|
+
|
|
10
|
+
from .utils import *
|
|
11
|
+
|
|
12
|
+
def asy_lin_wols(d, post, y, out_y, int_cov, i_w, n):
|
|
13
|
+
weigths_ols = i_w * d * post
|
|
14
|
+
# weigths_ols_pre
|
|
15
|
+
wols_x = weigths_ols[:, n_x] * int_cov
|
|
16
|
+
wols_ex = (weigths_ols * (y - out_y))[:, n_x] * int_cov
|
|
17
|
+
cr = np.dot(wols_x.T, int_cov) / n
|
|
18
|
+
xpx_inv = qr_solver(cr)
|
|
19
|
+
asy_lin_rep_ols = np.dot(wols_ex, xpx_inv)
|
|
20
|
+
return asy_lin_rep_ols
|
|
21
|
+
|
|
22
|
+
import numpy as np
|
|
23
|
+
import statsmodels.api as sm
|
|
24
|
+
|
|
25
|
+
def reg_did_panel(y1, y0, D, covariates=None, i_weights=None):
|
|
26
|
+
# Convert inputs to numpy arrays
|
|
27
|
+
# print("reg: panel")
|
|
28
|
+
y1 = np.array(y1)
|
|
29
|
+
y0 = np.array(y0)
|
|
30
|
+
D = np.array(D)
|
|
31
|
+
|
|
32
|
+
# Sample size
|
|
33
|
+
n = len(D)
|
|
34
|
+
|
|
35
|
+
# Generate deltaY
|
|
36
|
+
deltaY = y1 - y0
|
|
37
|
+
|
|
38
|
+
# Add constant to covariate vector
|
|
39
|
+
if covariates is None:
|
|
40
|
+
int_cov = np.ones((n, 1))
|
|
41
|
+
else:
|
|
42
|
+
covariates = np.asarray(covariates)
|
|
43
|
+
if np.all(covariates[:, 0] == 1):
|
|
44
|
+
int_cov = covariates
|
|
45
|
+
else:
|
|
46
|
+
int_cov = np.column_stack((np.ones(n), covariates))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Weights
|
|
50
|
+
if i_weights is None:
|
|
51
|
+
i_weights = np.ones(n)
|
|
52
|
+
else:
|
|
53
|
+
i_weights = np.array(i_weights)
|
|
54
|
+
if np.min(i_weights) < 0:
|
|
55
|
+
raise ValueError("i_weights must be non-negative")
|
|
56
|
+
|
|
57
|
+
# Normalize weights
|
|
58
|
+
i_weights = i_weights / np.mean(i_weights)
|
|
59
|
+
|
|
60
|
+
# Compute the Outcome regression for the control group using OLS
|
|
61
|
+
model = sm.WLS(deltaY[D == 0], int_cov[D == 0], weights=i_weights[D == 0])
|
|
62
|
+
results = model.fit()
|
|
63
|
+
reg_coeff = results.params
|
|
64
|
+
# print(reg_coeff)
|
|
65
|
+
if np.any(np.isnan(reg_coeff)):
|
|
66
|
+
raise ValueError("Outcome regression model coefficients have NA components. Multicollinearity (or lack of variation) of covariates is probably the reason for it.")
|
|
67
|
+
|
|
68
|
+
out_delta = np.dot(int_cov, reg_coeff)
|
|
69
|
+
# print(out_delta)
|
|
70
|
+
|
|
71
|
+
# Compute the OR-DiD estimator
|
|
72
|
+
w_treat = i_weights * D
|
|
73
|
+
w_cont = i_weights * D
|
|
74
|
+
reg_att_treat = w_treat * deltaY
|
|
75
|
+
reg_att_cont = w_cont * out_delta
|
|
76
|
+
eta_treat = np.mean(reg_att_treat) / np.mean(w_treat)
|
|
77
|
+
eta_cont = np.mean(reg_att_cont) / np.mean(w_cont)
|
|
78
|
+
# print(np.mean(reg_att_cont), np.mean(w_cont))
|
|
79
|
+
reg_att = eta_treat - eta_cont
|
|
80
|
+
|
|
81
|
+
# Compute influence function
|
|
82
|
+
weights_ols = i_weights * (1 - D)
|
|
83
|
+
wols_x = weights_ols[:, np.newaxis] * int_cov
|
|
84
|
+
wols_eX = weights_ols[:, np.newaxis] * (deltaY - out_delta)[:, np.newaxis] * int_cov
|
|
85
|
+
XpX_inv = np.linalg.inv(np.dot(wols_x.T, int_cov) / n)
|
|
86
|
+
asy_lin_rep_ols = np.dot(wols_eX, XpX_inv)
|
|
87
|
+
|
|
88
|
+
inf_treat = (reg_att_treat - w_treat * eta_treat) / np.mean(w_treat)
|
|
89
|
+
|
|
90
|
+
inf_cont_1 = reg_att_cont - w_cont * eta_cont
|
|
91
|
+
M1 = np.mean(w_cont[:, np.newaxis] * int_cov, axis=0)
|
|
92
|
+
inf_cont_2 = np.dot(asy_lin_rep_ols, M1)
|
|
93
|
+
inf_control = (inf_cont_1 + inf_cont_2) / np.mean(w_cont)
|
|
94
|
+
|
|
95
|
+
reg_att_inf_func = inf_treat - inf_control
|
|
96
|
+
se = np.std(reg_att_inf_func, ddof=1) / np.sqrt(n)
|
|
97
|
+
# print(f"att: {reg_att} \t se: {se}")
|
|
98
|
+
|
|
99
|
+
return reg_att, reg_att_inf_func
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def reg_did_rc(y, post, D, covariates, i_weights=None):
|
|
105
|
+
# print("reg_rc")
|
|
106
|
+
D = np.asarray(D).flatten()
|
|
107
|
+
post = np.asarray(post).flatten()
|
|
108
|
+
n = len(D)
|
|
109
|
+
y = np.asarray(y).flatten()
|
|
110
|
+
i_weights = np.asarray(i_weights).flatten()
|
|
111
|
+
int_cov = np.ones((n, 1))
|
|
112
|
+
|
|
113
|
+
if covariates is not None:
|
|
114
|
+
covariates = np.asarray(covariates)
|
|
115
|
+
if np.all(covariates[:, 0] == 1):
|
|
116
|
+
int_cov = covariates
|
|
117
|
+
else:
|
|
118
|
+
int_cov = np.column_stack((np.ones(n), covariates))
|
|
119
|
+
|
|
120
|
+
if i_weights is None:
|
|
121
|
+
i_weights = np.ones(n)
|
|
122
|
+
elif np.min(i_weights) < 0:
|
|
123
|
+
raise ValueError("i_weights must be non-negative")
|
|
124
|
+
|
|
125
|
+
i_weights = i_weights / np.mean(i_weights)
|
|
126
|
+
|
|
127
|
+
# Pre-treatment regression
|
|
128
|
+
mask_pre = (D == 0) & (post == 0)
|
|
129
|
+
X_pre = int_cov[mask_pre]
|
|
130
|
+
y_pre = y[mask_pre]
|
|
131
|
+
w_pre = i_weights[mask_pre]
|
|
132
|
+
model_pre = sm.WLS(y_pre, X_pre, weights=w_pre)
|
|
133
|
+
results_pre = model_pre.fit()
|
|
134
|
+
reg_coeff_pre = results_pre.params
|
|
135
|
+
|
|
136
|
+
if np.any(np.isnan(reg_coeff_pre)):
|
|
137
|
+
raise ValueError("Outcome regression model coefficients have NA components. \n Multicollinearity of covariates is probably the reason for it.")
|
|
138
|
+
|
|
139
|
+
out_y_pre = np.dot(int_cov, reg_coeff_pre)
|
|
140
|
+
|
|
141
|
+
# Post-treatment regression
|
|
142
|
+
mask_post = (D == 0) & (post == 1)
|
|
143
|
+
X_post = int_cov[mask_post]
|
|
144
|
+
y_post = y[mask_post]
|
|
145
|
+
w_post = i_weights[mask_post]
|
|
146
|
+
model_post = sm.WLS(y_post, X_post, weights=w_post)
|
|
147
|
+
results_post = model_post.fit()
|
|
148
|
+
reg_coeff_post = results_post.params
|
|
149
|
+
|
|
150
|
+
if np.any(np.isnan(reg_coeff_post)):
|
|
151
|
+
raise ValueError("Outcome regression model coefficients have NA components. \n Multicollinearity (or lack of variation) of covariates is probably the reason for it.")
|
|
152
|
+
|
|
153
|
+
out_y_post = np.dot(int_cov, reg_coeff_post)
|
|
154
|
+
|
|
155
|
+
w_treat_pre = i_weights * D * (1 - post)
|
|
156
|
+
w_treat_post = i_weights * D * post
|
|
157
|
+
w_cont = i_weights * D
|
|
158
|
+
reg_att_treat_pre = w_treat_pre * y
|
|
159
|
+
reg_att_treat_post = w_treat_post * y
|
|
160
|
+
reg_att_cont = w_cont * (out_y_post - out_y_pre)
|
|
161
|
+
eta_treat_pre = np.mean(reg_att_treat_pre) / np.mean(w_treat_pre)
|
|
162
|
+
eta_treat_post = np.mean(reg_att_treat_post) / np.mean(w_treat_post)
|
|
163
|
+
eta_cont = np.mean(reg_att_cont) / np.mean(w_cont)
|
|
164
|
+
reg_att = (eta_treat_post - eta_treat_pre) - eta_cont
|
|
165
|
+
|
|
166
|
+
weights_ols_pre = i_weights * (1 - D) * (1 - post)
|
|
167
|
+
# print(weights_ols_pre.reshape((n, 1)))
|
|
168
|
+
wols_x_pre = weights_ols_pre[:, np.newaxis] * int_cov
|
|
169
|
+
wols_eX_pre = weights_ols_pre[:, np.newaxis] * (y - out_y_pre)[:, np.newaxis] * int_cov
|
|
170
|
+
XpX_inv_pre = np.linalg.inv(np.dot(wols_x_pre.T, int_cov) / n)
|
|
171
|
+
asy_lin_rep_ols_pre = np.dot(wols_eX_pre, XpX_inv_pre)
|
|
172
|
+
|
|
173
|
+
weights_ols_post = i_weights * (1 - D) * post
|
|
174
|
+
wols_x_post = weights_ols_post[:, np.newaxis] * int_cov
|
|
175
|
+
wols_eX_post = weights_ols_post[:, np.newaxis] * (y - out_y_post)[:, np.newaxis] * int_cov
|
|
176
|
+
XpX_inv_post = np.linalg.inv(np.dot(wols_x_post.T, int_cov) / n)
|
|
177
|
+
asy_lin_rep_ols_post = np.dot(wols_eX_post, XpX_inv_post)
|
|
178
|
+
|
|
179
|
+
inf_treat_pre = (reg_att_treat_pre - w_treat_pre * eta_treat_pre) / np.mean(w_treat_pre)
|
|
180
|
+
inf_treat_post = (reg_att_treat_post - w_treat_post * eta_treat_post) / np.mean(w_treat_post)
|
|
181
|
+
inf_treat = inf_treat_post - inf_treat_pre
|
|
182
|
+
|
|
183
|
+
inf_cont_1 = (reg_att_cont - w_cont * eta_cont)
|
|
184
|
+
M1 = np.mean(w_cont[:, np.newaxis] * int_cov, axis=0)
|
|
185
|
+
inf_cont_2_post = np.dot(asy_lin_rep_ols_post, M1)
|
|
186
|
+
inf_cont_2_pre = np.dot(asy_lin_rep_ols_pre, M1)
|
|
187
|
+
inf_control = (inf_cont_1 + inf_cont_2_post - inf_cont_2_pre) / np.mean(w_cont)
|
|
188
|
+
|
|
189
|
+
reg_att_inf_func = (inf_treat - inf_control)
|
|
190
|
+
# se_reg_att = np.std(reg_att_inf_func) / np.sqrt(n)
|
|
191
|
+
se = np.std(reg_att_inf_func, ddof=1) / np.sqrt(n)
|
|
192
|
+
# print(f"att: {reg_att} \t se: {se}")
|
|
193
|
+
|
|
194
|
+
return reg_att, reg_att_inf_func
|
|
195
|
+
|
|
196
|
+
|
drdid/utils.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import statsmodels.api as sm
|
|
3
|
+
|
|
4
|
+
lm = sm.WLS
|
|
5
|
+
glm = sm.GLM
|
|
6
|
+
n_x = np.newaxis
|
|
7
|
+
qr_solver = np.linalg.pinv
|
|
8
|
+
binomial = sm.families.Binomial()
|
|
9
|
+
mean = np.mean
|
|
10
|
+
|
|
11
|
+
def has_intercept(covariates, n):
|
|
12
|
+
int_cov = np.ones(n)
|
|
13
|
+
if covariates is not None:
|
|
14
|
+
if np.all(covariates[:, 0] == int_cov):
|
|
15
|
+
int_cov = covariates
|
|
16
|
+
else:
|
|
17
|
+
int_cov = np.concatenate((np.ones((n, 1)), covariates), axis=1)
|
|
18
|
+
|
|
19
|
+
return int_cov
|
|
20
|
+
|
|
21
|
+
def has_weights(weights, n):
|
|
22
|
+
if weights is None:
|
|
23
|
+
weights = np.ones(n)
|
|
24
|
+
elif np.min(weights) < 0:
|
|
25
|
+
raise ValueError('weights must be non-negative')
|
|
26
|
+
|
|
27
|
+
def asy_lin_wols(w, d, x, y, out_y, pst = 1):
|
|
28
|
+
n = len(y)
|
|
29
|
+
w_ols = w * d * pst
|
|
30
|
+
wols_x = w_ols[:, n_x] * x
|
|
31
|
+
w_diff = w_ols * (y - out_y)
|
|
32
|
+
wols_ex = w_diff[:, n_x] * x
|
|
33
|
+
cr = np.dot(wols_x.T, x) / n
|
|
34
|
+
xpx_inv = qr_solver(cr)
|
|
35
|
+
asy_lin = np.dot(wols_ex, xpx_inv)
|
|
36
|
+
return asy_lin
|
|
37
|
+
|
|
38
|
+
def out_wols(y, d, x, rows, wgts, pst=1):
|
|
39
|
+
ols_coef = lm(
|
|
40
|
+
y[rows], x[rows], weights=wgts[rows]
|
|
41
|
+
).fit().params
|
|
42
|
+
out_y = np.dor(ols_coef, x.T)
|
|
43
|
+
|
|
44
|
+
### asy_lin_rep
|
|
45
|
+
asy_lin_rep = asy_lin_wols(
|
|
46
|
+
wgts, d, x, y, out_y, pst
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
return out_y, asy_lin_rep
|
|
51
|
+
|
|
52
|
+
def w_tc_val(w, d, pst=1):
|
|
53
|
+
return w * d * pst
|
|
54
|
+
|
|
55
|
+
def eta_val(att, w_tc=1, y=None):
|
|
56
|
+
if y is None:
|
|
57
|
+
eta_r = mean(att) / mean(w_tc)
|
|
58
|
+
else:
|
|
59
|
+
eta_r = att * y / mean(att)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def inf_treat_f(att, w, eta):
|
|
63
|
+
inf_f_u = att - w * eta
|
|
64
|
+
inf_f = inf_f_u / mean(w)
|
|
65
|
+
return inf_f
|
|
66
|
+
|
|
67
|
+
def bstrap_se():
|
|
68
|
+
# replace with mboot
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
def fit_ps(d, x, w, post = 1):
|
|
72
|
+
n = len(d)
|
|
73
|
+
glm_fit = glm(
|
|
74
|
+
d, x, family=binomial, freq_weights=w
|
|
75
|
+
).fit()
|
|
76
|
+
ps_fit = glm_fit.fittedvalues
|
|
77
|
+
ps_fit = np.minimum(ps_fit, 1 - 1e-16)
|
|
78
|
+
|
|
79
|
+
rest_cont = w * ps_fit * (1 - d)
|
|
80
|
+
post1 = (1 - post)
|
|
81
|
+
ps_fit1 = (1 - ps_fit)
|
|
82
|
+
# Rc
|
|
83
|
+
w_cont_pre = rest_cont * post1 / ps_fit1
|
|
84
|
+
w_cont_post = rest_cont * post / ps_fit
|
|
85
|
+
# panel
|
|
86
|
+
w_cont = rest_cont / ps_fit1
|
|
87
|
+
|
|
88
|
+
w_ps = w * (d - ps_fit)
|
|
89
|
+
score_ps = w_ps[:, n_x] * x
|
|
90
|
+
hessian = glm_fit.cov_params() * n
|
|
91
|
+
|
|
92
|
+
asy_lin_ps = np.dot(score_ps, hessian)
|
|
93
|
+
|
|
94
|
+
return (ps_fit, w_cont_pre, w_cont, w_cont_post, 0, asy_lin_ps)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: drdid
|
|
3
|
+
Version: 1.1.6
|
|
4
|
+
Summary: Double Robust Difference in Difference y python
|
|
5
|
+
Home-page: https://github.com/d2cml-ai/drdid
|
|
6
|
+
Author: Jhon Flores
|
|
7
|
+
Author-email: fr.jhonk@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: pandas
|
|
11
|
+
Requires-Dist: numpy
|
|
12
|
+
Requires-Dist: statsmodels
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: description
|
|
16
|
+
Dynamic: home-page
|
|
17
|
+
Dynamic: license
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
Dynamic: requires-dist
|
|
20
|
+
Dynamic: summary
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
Implementation of drdid for Python, R-like syntax with return destructuring using optimized Python code.
|
|
24
|
+
|
|
25
|
+
See the original [R package](https://github.com/pedrohcgs/DRDID/tree/master)
|
|
26
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
drdid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
drdid/_version_.py,sha256=dh8J4El9b4iqphBgXLgnpBBahiEA-LUwKZYLjWS-x3c,17
|
|
3
|
+
drdid/drdid.py,sha256=Ii5qQ0lInYHLSsGXEEpobhlMq1BFUK3LiZP73lHBifw,13156
|
|
4
|
+
drdid/ipwd_did.py,sha256=vrnEyCg9jsZabsjcp_S8KzPuRt-eaXOlF_72TFavZcE,6273
|
|
5
|
+
drdid/reg_did.py,sha256=IX5x7MLXsL7qhCtcBL3bVDPuPemN5QEVW50soEQ3G5k,7039
|
|
6
|
+
drdid/utils.py,sha256=DLz3zfaJBtkQH72t_Ks5dUeZCqUf5HAz--iDkPIh5OY,2154
|
|
7
|
+
drdid-1.1.6.dist-info/licenses/LICENSE,sha256=074-9zp7UvJPkvubrgLWw_JB4EgZiYCjAtzpgi9es_I,1142
|
|
8
|
+
drdid-1.1.6.dist-info/METADATA,sha256=nT8P01tCuh9uwMNrDABO1pVTcCt3VaAnO6etBj8GlfY,690
|
|
9
|
+
drdid-1.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
drdid-1.1.6.dist-info/top_level.txt,sha256=xRabzMBAiGz5Aks1Wcebo_WtauCRbYgkHYo3DQ-G1eM,6
|
|
11
|
+
drdid-1.1.6.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Pedro Sant’Anna, Carlos Guevara, Jhon Flores, Alexander Quispe
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
drdid
|