pyAgrum-nightly 2.3.0.9.dev202512061764412981__cp310-abi3-macosx_11_0_arm64.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.
- pyagrum/__init__.py +165 -0
- pyagrum/_pyagrum.so +0 -0
- pyagrum/bnmixture/BNMInference.py +268 -0
- pyagrum/bnmixture/BNMLearning.py +376 -0
- pyagrum/bnmixture/BNMixture.py +464 -0
- pyagrum/bnmixture/__init__.py +60 -0
- pyagrum/bnmixture/notebook.py +1058 -0
- pyagrum/causal/_CausalFormula.py +280 -0
- pyagrum/causal/_CausalModel.py +436 -0
- pyagrum/causal/__init__.py +81 -0
- pyagrum/causal/_causalImpact.py +356 -0
- pyagrum/causal/_dSeparation.py +598 -0
- pyagrum/causal/_doAST.py +761 -0
- pyagrum/causal/_doCalculus.py +361 -0
- pyagrum/causal/_doorCriteria.py +374 -0
- pyagrum/causal/_exceptions.py +95 -0
- pyagrum/causal/_types.py +61 -0
- pyagrum/causal/causalEffectEstimation/_CausalEffectEstimation.py +1175 -0
- pyagrum/causal/causalEffectEstimation/_IVEstimators.py +718 -0
- pyagrum/causal/causalEffectEstimation/_RCTEstimators.py +132 -0
- pyagrum/causal/causalEffectEstimation/__init__.py +46 -0
- pyagrum/causal/causalEffectEstimation/_backdoorEstimators.py +774 -0
- pyagrum/causal/causalEffectEstimation/_causalBNEstimator.py +324 -0
- pyagrum/causal/causalEffectEstimation/_frontdoorEstimators.py +396 -0
- pyagrum/causal/causalEffectEstimation/_learners.py +118 -0
- pyagrum/causal/causalEffectEstimation/_utils.py +466 -0
- pyagrum/causal/notebook.py +171 -0
- pyagrum/clg/CLG.py +658 -0
- pyagrum/clg/GaussianVariable.py +111 -0
- pyagrum/clg/SEM.py +312 -0
- pyagrum/clg/__init__.py +63 -0
- pyagrum/clg/canonicalForm.py +408 -0
- pyagrum/clg/constants.py +54 -0
- pyagrum/clg/forwardSampling.py +202 -0
- pyagrum/clg/learning.py +776 -0
- pyagrum/clg/notebook.py +480 -0
- pyagrum/clg/variableElimination.py +271 -0
- pyagrum/common.py +60 -0
- pyagrum/config.py +319 -0
- pyagrum/ctbn/CIM.py +513 -0
- pyagrum/ctbn/CTBN.py +573 -0
- pyagrum/ctbn/CTBNGenerator.py +216 -0
- pyagrum/ctbn/CTBNInference.py +459 -0
- pyagrum/ctbn/CTBNLearner.py +161 -0
- pyagrum/ctbn/SamplesStats.py +671 -0
- pyagrum/ctbn/StatsIndepTest.py +355 -0
- pyagrum/ctbn/__init__.py +79 -0
- pyagrum/ctbn/constants.py +54 -0
- pyagrum/ctbn/notebook.py +264 -0
- pyagrum/defaults.ini +199 -0
- pyagrum/deprecated.py +95 -0
- pyagrum/explain/_ComputationCausal.py +75 -0
- pyagrum/explain/_ComputationConditional.py +48 -0
- pyagrum/explain/_ComputationMarginal.py +48 -0
- pyagrum/explain/_CustomShapleyCache.py +110 -0
- pyagrum/explain/_Explainer.py +176 -0
- pyagrum/explain/_Explanation.py +70 -0
- pyagrum/explain/_FIFOCache.py +54 -0
- pyagrum/explain/_ShallCausalValues.py +204 -0
- pyagrum/explain/_ShallConditionalValues.py +155 -0
- pyagrum/explain/_ShallMarginalValues.py +155 -0
- pyagrum/explain/_ShallValues.py +296 -0
- pyagrum/explain/_ShapCausalValues.py +208 -0
- pyagrum/explain/_ShapConditionalValues.py +126 -0
- pyagrum/explain/_ShapMarginalValues.py +191 -0
- pyagrum/explain/_ShapleyValues.py +298 -0
- pyagrum/explain/__init__.py +81 -0
- pyagrum/explain/_explGeneralizedMarkovBlanket.py +152 -0
- pyagrum/explain/_explIndependenceListForPairs.py +146 -0
- pyagrum/explain/_explInformationGraph.py +264 -0
- pyagrum/explain/notebook/__init__.py +54 -0
- pyagrum/explain/notebook/_bar.py +142 -0
- pyagrum/explain/notebook/_beeswarm.py +174 -0
- pyagrum/explain/notebook/_showShapValues.py +97 -0
- pyagrum/explain/notebook/_waterfall.py +220 -0
- pyagrum/explain/shapley.py +225 -0
- pyagrum/lib/__init__.py +46 -0
- pyagrum/lib/_colors.py +390 -0
- pyagrum/lib/bn2graph.py +299 -0
- pyagrum/lib/bn2roc.py +1026 -0
- pyagrum/lib/bn2scores.py +217 -0
- pyagrum/lib/bn_vs_bn.py +605 -0
- pyagrum/lib/cn2graph.py +305 -0
- pyagrum/lib/discreteTypeProcessor.py +1102 -0
- pyagrum/lib/discretizer.py +58 -0
- pyagrum/lib/dynamicBN.py +390 -0
- pyagrum/lib/explain.py +57 -0
- pyagrum/lib/export.py +84 -0
- pyagrum/lib/id2graph.py +258 -0
- pyagrum/lib/image.py +387 -0
- pyagrum/lib/ipython.py +307 -0
- pyagrum/lib/mrf2graph.py +471 -0
- pyagrum/lib/notebook.py +1821 -0
- pyagrum/lib/proba_histogram.py +552 -0
- pyagrum/lib/utils.py +138 -0
- pyagrum/pyagrum.py +31495 -0
- pyagrum/skbn/_MBCalcul.py +242 -0
- pyagrum/skbn/__init__.py +49 -0
- pyagrum/skbn/_learningMethods.py +282 -0
- pyagrum/skbn/_utils.py +297 -0
- pyagrum/skbn/bnclassifier.py +1014 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/LICENSE.md +12 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/LICENSES/LGPL-3.0-or-later.txt +304 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/LICENSES/MIT.txt +18 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/METADATA +145 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/RECORD +107 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
# This file is part of the aGrUM/pyAgrum library. #
|
|
3
|
+
# #
|
|
4
|
+
# Copyright (c) 2005-2025 by #
|
|
5
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
6
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
7
|
+
# #
|
|
8
|
+
# The aGrUM/pyAgrum library is free software; you can redistribute it #
|
|
9
|
+
# and/or modify it under the terms of either : #
|
|
10
|
+
# #
|
|
11
|
+
# - the GNU Lesser General Public License as published by #
|
|
12
|
+
# the Free Software Foundation, either version 3 of the License, #
|
|
13
|
+
# or (at your option) any later version, #
|
|
14
|
+
# - the MIT license (MIT), #
|
|
15
|
+
# - or both in dual license, as here. #
|
|
16
|
+
# #
|
|
17
|
+
# (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) #
|
|
18
|
+
# #
|
|
19
|
+
# This aGrUM/pyAgrum library is distributed in the hope that it will be #
|
|
20
|
+
# useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, #
|
|
21
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS #
|
|
22
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
|
|
23
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
|
|
24
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, #
|
|
25
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #
|
|
26
|
+
# OTHER DEALINGS IN THE SOFTWARE. #
|
|
27
|
+
# #
|
|
28
|
+
# See LICENCES for more details. #
|
|
29
|
+
# #
|
|
30
|
+
# SPDX-FileCopyrightText: Copyright 2005-2025 #
|
|
31
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
32
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
33
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later OR MIT #
|
|
34
|
+
# #
|
|
35
|
+
# Contact : info_at_agrum_dot_org #
|
|
36
|
+
# homepage : http://agrum.gitlab.io #
|
|
37
|
+
# gitlab : https://gitlab.com/agrumery/agrum #
|
|
38
|
+
# #
|
|
39
|
+
############################################################################
|
|
40
|
+
|
|
41
|
+
import pandas as pd
|
|
42
|
+
import numpy as np
|
|
43
|
+
|
|
44
|
+
from typing import Any
|
|
45
|
+
|
|
46
|
+
from sklearn.base import clone
|
|
47
|
+
|
|
48
|
+
from ._learners import learnerFromString
|
|
49
|
+
|
|
50
|
+
from scipy.special import expit
|
|
51
|
+
from scipy.optimize import minimize
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Wald:
|
|
55
|
+
"""
|
|
56
|
+
An implementation of the Wald estimator which computes the
|
|
57
|
+
Local Average Causal Effect (LACE), also know as
|
|
58
|
+
the Local Average Treatment Effect (LATE).
|
|
59
|
+
Only Supports binary instruments.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self) -> None:
|
|
63
|
+
"""
|
|
64
|
+
Initialize an Wald estimator.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def fit(
|
|
68
|
+
self,
|
|
69
|
+
X: np.matrix | np.ndarray | pd.DataFrame,
|
|
70
|
+
treatment: np.ndarray | pd.Series,
|
|
71
|
+
y: np.ndarray | pd.Series,
|
|
72
|
+
w: np.matrix | np.ndarray | pd.DataFrame,
|
|
73
|
+
) -> None:
|
|
74
|
+
"""
|
|
75
|
+
Fit the inference model.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
80
|
+
The covariate matrix.
|
|
81
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
82
|
+
The instrument variable.
|
|
83
|
+
treatment: np.ndarray or pd.Series
|
|
84
|
+
The treatment assignment vector.
|
|
85
|
+
y: np.ndarray or pd.Series,
|
|
86
|
+
The outcome vector.
|
|
87
|
+
"""
|
|
88
|
+
if not X.empty:
|
|
89
|
+
raise ValueError(
|
|
90
|
+
"The Wald estimator does not support confounders, consider using `WaldIPW`, `NormalizedWaldIPW` or `TSLS`."
|
|
91
|
+
)
|
|
92
|
+
if set(np.unique(w.to_numpy())) != {0, 1}:
|
|
93
|
+
raise ValueError("Instrument must be binary with values 0 and 1 for LACE estimation.")
|
|
94
|
+
|
|
95
|
+
def predict(
|
|
96
|
+
self,
|
|
97
|
+
X: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
98
|
+
treatment: np.ndarray | pd.Series = None,
|
|
99
|
+
y: np.ndarray | pd.Series = None,
|
|
100
|
+
w: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
101
|
+
) -> np.ndarray:
|
|
102
|
+
"""
|
|
103
|
+
Predict the Individual Causal Effect (ICE).
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
108
|
+
The covariate matrix.
|
|
109
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
110
|
+
The instrument variable.
|
|
111
|
+
treatment: np.ndarray or pd.Series, optional
|
|
112
|
+
The vector of treatment assignments.
|
|
113
|
+
y: np.ndarray or pd.Series, optional
|
|
114
|
+
The vector of outcomes.
|
|
115
|
+
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
np.ndarray
|
|
119
|
+
An array containing the predicted ICE.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
w = w.iloc[:, 0] if isinstance(w, pd.DataFrame) else w
|
|
123
|
+
return [(y[w == 1].mean() - y[w == 0].mean()) / (treatment[w == 1].mean() - treatment[w == 0].mean())] * len(w)
|
|
124
|
+
|
|
125
|
+
def estimate_ate(
|
|
126
|
+
self,
|
|
127
|
+
X: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
128
|
+
treatment: np.ndarray | pd.Series = None,
|
|
129
|
+
y: np.ndarray | pd.Series = None,
|
|
130
|
+
w: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
131
|
+
pretrain: bool = True,
|
|
132
|
+
) -> float:
|
|
133
|
+
"""
|
|
134
|
+
Predicts the Local Average Causal Effect (LACE),
|
|
135
|
+
also referred to as the Local Average Treatment Effect (LATE).
|
|
136
|
+
(The term "Average Treatment Effect" (ATE) is used in
|
|
137
|
+
the method name for compatibility purposes.)
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
142
|
+
The covariate matrix.
|
|
143
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
144
|
+
The instrument variable.
|
|
145
|
+
treatment: np.ndarray or pd.Series, optional
|
|
146
|
+
The vector of treatment assignments.
|
|
147
|
+
y: np.ndarray or pd.Series, optional
|
|
148
|
+
The vector of outcomes.
|
|
149
|
+
|
|
150
|
+
Returns
|
|
151
|
+
-------
|
|
152
|
+
float
|
|
153
|
+
The value of the ACE.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
w = w.iloc[:, 0] if isinstance(w, pd.DataFrame) else w
|
|
157
|
+
return (y[w == 1].mean() - y[w == 0].mean()) / (treatment[w == 1].mean() - treatment[w == 0].mean())
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class WaldIPW:
|
|
161
|
+
"""
|
|
162
|
+
A basic implementation of the Wald estimator with Inverse Propensity
|
|
163
|
+
Score Weighting which computes the Local Average Causal Effect (LACE).
|
|
164
|
+
(see https://www.ncbi.nlm.nih.gov/pmc/articles/PMC8021190/).
|
|
165
|
+
Only Supports binary instruments.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
def __init__(self, iv_probability_learner: str | Any | None = None) -> None:
|
|
169
|
+
"""
|
|
170
|
+
Initialize an Wald IPW estimator for LACE.
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
iv_probability_learner: str or Any, optional
|
|
175
|
+
Estimator for instrumental variable probability.
|
|
176
|
+
If not provided, defaults to LogisticRegression.
|
|
177
|
+
"""
|
|
178
|
+
if iv_probability_learner is None:
|
|
179
|
+
self.iv_probability_learner = learnerFromString("LogisticRegression")
|
|
180
|
+
elif isinstance(iv_probability_learner, str):
|
|
181
|
+
self.iv_probability_learner = learnerFromString(iv_probability_learner)
|
|
182
|
+
else:
|
|
183
|
+
self.iv_probability_learner = clone(iv_probability_learner)
|
|
184
|
+
|
|
185
|
+
def fit(
|
|
186
|
+
self,
|
|
187
|
+
X: np.matrix | np.ndarray | pd.DataFrame,
|
|
188
|
+
treatment: np.ndarray | pd.Series,
|
|
189
|
+
y: np.ndarray | pd.Series,
|
|
190
|
+
w: np.matrix | np.ndarray | pd.DataFrame,
|
|
191
|
+
) -> None:
|
|
192
|
+
"""
|
|
193
|
+
Fit the inference model.
|
|
194
|
+
|
|
195
|
+
Parameters
|
|
196
|
+
----------
|
|
197
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
198
|
+
The covariate matrix.
|
|
199
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
200
|
+
The instrument variable.
|
|
201
|
+
treatment: np.ndarray or pd.Series, optional
|
|
202
|
+
The vector of treatment assignments.
|
|
203
|
+
y: np.ndarray or pd.Series, optional
|
|
204
|
+
The vector of outcomes.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
if set(np.unique(w.to_numpy())) != {0, 1}:
|
|
208
|
+
raise ValueError("Instrument must be binary with values 0 and 1 for LACE estimation.")
|
|
209
|
+
|
|
210
|
+
self.iv_probability_learner.fit(X=X, y=np.ravel(w))
|
|
211
|
+
# print(w, np.ravel(w))
|
|
212
|
+
|
|
213
|
+
def predict(
|
|
214
|
+
self,
|
|
215
|
+
X: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
216
|
+
treatment: np.ndarray | pd.Series = None,
|
|
217
|
+
y: np.ndarray | pd.Series = None,
|
|
218
|
+
w: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
219
|
+
) -> np.ndarray:
|
|
220
|
+
"""
|
|
221
|
+
Predict the Individual Causal Effect (ICE).
|
|
222
|
+
|
|
223
|
+
Parameters
|
|
224
|
+
----------
|
|
225
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
226
|
+
The covariate matrix.
|
|
227
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
228
|
+
The instrument variable.
|
|
229
|
+
treatment: np.ndarray or pd.Series, optional
|
|
230
|
+
The vector of treatment assignments.
|
|
231
|
+
y: np.ndarray or pd.Series, optional
|
|
232
|
+
The vector of outcomes.
|
|
233
|
+
|
|
234
|
+
Returns
|
|
235
|
+
-------
|
|
236
|
+
np.ndarray
|
|
237
|
+
An array containing the predicted ICE.
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
w = w.iloc[:, 0] if isinstance(w, pd.DataFrame) else w
|
|
241
|
+
|
|
242
|
+
e = self.iv_probability_learner.predict_proba(X)[:, 1]
|
|
243
|
+
|
|
244
|
+
v_func = np.vectorize(lambda e, w, d: (w / e - (1 - w) / (1 - e)) * d)
|
|
245
|
+
|
|
246
|
+
return v_func(e, w, y) / v_func(e, w, treatment).mean()
|
|
247
|
+
|
|
248
|
+
def estimate_ate(
|
|
249
|
+
self,
|
|
250
|
+
X: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
251
|
+
treatment: np.ndarray | pd.Series = None,
|
|
252
|
+
y: np.ndarray | pd.Series = None,
|
|
253
|
+
w: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
254
|
+
pretrain: bool = True,
|
|
255
|
+
) -> float:
|
|
256
|
+
"""
|
|
257
|
+
Predicts the Local Average Causal Effect (LACE),
|
|
258
|
+
also referred to as the Local Average Treatment Effect (LATE).
|
|
259
|
+
(The term "Average Treatment Effect" (ATE) is used in
|
|
260
|
+
the method name for compatibility purposes.)
|
|
261
|
+
|
|
262
|
+
Parameters
|
|
263
|
+
----------
|
|
264
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
265
|
+
The covariate matrix.
|
|
266
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
267
|
+
The instrument variable.
|
|
268
|
+
treatment: np.ndarray or pd.Series, optional
|
|
269
|
+
The vector of treatment assignments.
|
|
270
|
+
y: np.ndarray or pd.Series, optional
|
|
271
|
+
The vector of outcomes.
|
|
272
|
+
|
|
273
|
+
Returns
|
|
274
|
+
-------
|
|
275
|
+
float
|
|
276
|
+
The value of the ACE.
|
|
277
|
+
"""
|
|
278
|
+
|
|
279
|
+
return self.predict(X, treatment, y, w).mean()
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class NormalizedWaldIPW:
|
|
283
|
+
"""
|
|
284
|
+
A basic implementation of the normalized Wald estimator with Inverse Propensity
|
|
285
|
+
Score Weighting which computes the Local Average Causal Effect (LACE).
|
|
286
|
+
(see https://www.ncbi.nlm.nih.gov/pmc/articles/PMC8021190/).
|
|
287
|
+
Only Supports binary instruments.
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
def __init__(self, iv_probability_learner: str | Any | None = None) -> None:
|
|
291
|
+
"""
|
|
292
|
+
Initialize an Wald IPW estimator for LACE.
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
iv_probability_learner: str or Any, optional
|
|
297
|
+
Estimator for instrumental probability score.
|
|
298
|
+
If not provided, defaults to LogisticRegression.
|
|
299
|
+
"""
|
|
300
|
+
if iv_probability_learner is None:
|
|
301
|
+
self.iv_probability_learner = learnerFromString("LogisticRegression")
|
|
302
|
+
elif isinstance(iv_probability_learner, str):
|
|
303
|
+
self.iv_probability_learner = learnerFromString(iv_probability_learner)
|
|
304
|
+
else:
|
|
305
|
+
self.iv_probability_learner = clone(iv_probability_learner)
|
|
306
|
+
|
|
307
|
+
def fit(
|
|
308
|
+
self,
|
|
309
|
+
X: np.matrix | np.ndarray | pd.DataFrame,
|
|
310
|
+
treatment: np.ndarray | pd.Series,
|
|
311
|
+
y: np.ndarray | pd.Series,
|
|
312
|
+
w: np.matrix | np.ndarray | pd.DataFrame,
|
|
313
|
+
) -> None:
|
|
314
|
+
"""
|
|
315
|
+
Fit the inference model.
|
|
316
|
+
|
|
317
|
+
Parameters
|
|
318
|
+
----------
|
|
319
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
320
|
+
The covariate matrix.
|
|
321
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
322
|
+
The instrument variable.
|
|
323
|
+
treatment: np.ndarray or pd.Series, optional
|
|
324
|
+
The vector of treatment assignments.
|
|
325
|
+
y: np.ndarray or pd.Series, optional
|
|
326
|
+
The vector of outcomes.
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
if set(np.unique(w.to_numpy())) != {0, 1}:
|
|
330
|
+
raise ValueError("Instrument must be binary with values 0 and 1 for LACE estimation.")
|
|
331
|
+
|
|
332
|
+
self.iv_probability_learner.fit(X=X, y=np.ravel(w))
|
|
333
|
+
|
|
334
|
+
def predict(
|
|
335
|
+
self,
|
|
336
|
+
X: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
337
|
+
treatment: np.ndarray | pd.Series = None,
|
|
338
|
+
y: np.ndarray | pd.Series = None,
|
|
339
|
+
w: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
340
|
+
) -> np.ndarray:
|
|
341
|
+
"""
|
|
342
|
+
Predict the Individual Causal Effect (ICE).
|
|
343
|
+
|
|
344
|
+
Parameters
|
|
345
|
+
----------
|
|
346
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
347
|
+
The covariate matrix.
|
|
348
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
349
|
+
The instrument variable.
|
|
350
|
+
treatment: np.ndarray or pd.Series, optional
|
|
351
|
+
The vector of treatment assignments.
|
|
352
|
+
y: np.ndarray or pd.Series, optional
|
|
353
|
+
The vector of outcomes.
|
|
354
|
+
|
|
355
|
+
Returns
|
|
356
|
+
-------
|
|
357
|
+
np.ndarray
|
|
358
|
+
An array containing the predicted ICE.
|
|
359
|
+
"""
|
|
360
|
+
|
|
361
|
+
w = w.iloc[:, 0] if isinstance(w, pd.DataFrame) else w
|
|
362
|
+
|
|
363
|
+
e = self.iv_probability_learner.predict_proba(X)[:, 1]
|
|
364
|
+
|
|
365
|
+
v_func = np.vectorize(lambda e, w, d: w * d / e)
|
|
366
|
+
|
|
367
|
+
return ((v_func(e, w, y) / v_func(e, w, 1).mean()) - (v_func(1 - e, 1 - w, y) / v_func(1 - e, 1 - w, 1).mean())) / (
|
|
368
|
+
(v_func(e, w, treatment) / v_func(e, w, 1).mean())
|
|
369
|
+
- (v_func(1 - e, 1 - w, treatment) / v_func(1 - e, 1 - w, 1).mean())
|
|
370
|
+
).mean()
|
|
371
|
+
|
|
372
|
+
def estimate_ate(
|
|
373
|
+
self,
|
|
374
|
+
X: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
375
|
+
treatment: np.ndarray | pd.Series = None,
|
|
376
|
+
y: np.ndarray | pd.Series = None,
|
|
377
|
+
w: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
378
|
+
pretrain: bool = True,
|
|
379
|
+
) -> float:
|
|
380
|
+
"""
|
|
381
|
+
Predicts the Local Average Causal Effect (LACE),
|
|
382
|
+
also referred to as the Local Average Treatment Effect (LATE).
|
|
383
|
+
(The term "Average Treatment Effect" (ATE) is used in
|
|
384
|
+
the method name for compatibility purposes.)
|
|
385
|
+
|
|
386
|
+
Parameters
|
|
387
|
+
----------
|
|
388
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
389
|
+
The covariate matrix.
|
|
390
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
391
|
+
The instrument variable.
|
|
392
|
+
treatment: np.ndarray or pd.Series, optional
|
|
393
|
+
The vector of treatment assignments.
|
|
394
|
+
y: np.ndarray or pd.Series, optional
|
|
395
|
+
The vector of outcomes.
|
|
396
|
+
|
|
397
|
+
Returns
|
|
398
|
+
-------
|
|
399
|
+
float
|
|
400
|
+
The value of the ACE.
|
|
401
|
+
"""
|
|
402
|
+
|
|
403
|
+
return self.predict(X, treatment, y, w).mean()
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
class TSLS:
|
|
407
|
+
"""
|
|
408
|
+
A basic implementation of the Two Stage Least-Squares Estimator.
|
|
409
|
+
(see https://www.ncss.com/wp-content/themes/ncss/pdf/Procedures/NCSS/Two-Stage_Least_Squares.pdf)
|
|
410
|
+
"""
|
|
411
|
+
|
|
412
|
+
def __init__(
|
|
413
|
+
self,
|
|
414
|
+
learner: str | Any | None = None,
|
|
415
|
+
treatment_learner: str | Any | None = None,
|
|
416
|
+
outcome_learner: str | Any | None = None,
|
|
417
|
+
) -> None:
|
|
418
|
+
"""
|
|
419
|
+
Initialize an TSLS estimator.
|
|
420
|
+
Only supports Linear Models, must have `.coef_` attribute.
|
|
421
|
+
|
|
422
|
+
Parameters
|
|
423
|
+
----------
|
|
424
|
+
learner: str or Any, optional
|
|
425
|
+
Base estimator for all learners.
|
|
426
|
+
If not provided, defaults to LinearRegression.
|
|
427
|
+
treatment_learner: str or Any, optional
|
|
428
|
+
Estimator for treatment assignment.
|
|
429
|
+
Overrides `learner` if specified.
|
|
430
|
+
outcome_learner: str or Any, optional
|
|
431
|
+
Estimator for outcome.
|
|
432
|
+
Overrides `learner` if specified.
|
|
433
|
+
"""
|
|
434
|
+
|
|
435
|
+
if learner is None:
|
|
436
|
+
self.treatment_learner = learnerFromString("LinearRegression")
|
|
437
|
+
self.outcome_learner = learnerFromString("LinearRegression")
|
|
438
|
+
elif isinstance(learner, str):
|
|
439
|
+
self.treatment_learner = learnerFromString(learner)
|
|
440
|
+
self.outcome_learner = learnerFromString(learner)
|
|
441
|
+
else:
|
|
442
|
+
self.treatment_learner = clone(learner)
|
|
443
|
+
self.outcome_learner = clone(learner)
|
|
444
|
+
|
|
445
|
+
if isinstance(treatment_learner, str):
|
|
446
|
+
self.treatment_learner = learnerFromString(treatment_learner)
|
|
447
|
+
elif treatment_learner is not None:
|
|
448
|
+
self.treatment_learner = clone(treatment_learner)
|
|
449
|
+
|
|
450
|
+
if isinstance(outcome_learner, str):
|
|
451
|
+
self.outcome_learner = learnerFromString(outcome_learner)
|
|
452
|
+
elif outcome_learner is not None:
|
|
453
|
+
self.outcome_learner = clone(outcome_learner)
|
|
454
|
+
|
|
455
|
+
def fit(
|
|
456
|
+
self,
|
|
457
|
+
X: np.matrix | np.ndarray | pd.DataFrame,
|
|
458
|
+
treatment: np.ndarray | pd.Series,
|
|
459
|
+
y: np.ndarray | pd.Series,
|
|
460
|
+
w: np.matrix | np.ndarray | pd.DataFrame,
|
|
461
|
+
) -> None:
|
|
462
|
+
"""
|
|
463
|
+
Fit the inference model.
|
|
464
|
+
|
|
465
|
+
Parameters
|
|
466
|
+
----------
|
|
467
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
468
|
+
The covariate matrix.
|
|
469
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
470
|
+
The instrument variable.
|
|
471
|
+
treatment: np.ndarray or pd.Series, optional
|
|
472
|
+
The vector of treatment assignments.
|
|
473
|
+
y: np.ndarray or pd.Series, optional
|
|
474
|
+
The vector of outcomes.
|
|
475
|
+
"""
|
|
476
|
+
|
|
477
|
+
self.treatment_learner.fit(X=pd.concat([pd.DataFrame(X), pd.DataFrame(w)], axis=1), y=treatment)
|
|
478
|
+
|
|
479
|
+
def predict(
|
|
480
|
+
self,
|
|
481
|
+
X: np.matrix | np.ndarray | pd.DataFrame,
|
|
482
|
+
treatment: np.ndarray | pd.Series,
|
|
483
|
+
y: np.ndarray | pd.Series,
|
|
484
|
+
w: np.matrix | np.ndarray | pd.DataFrame,
|
|
485
|
+
) -> np.ndarray:
|
|
486
|
+
"""
|
|
487
|
+
Predict the Individual Causal Effect (ICE).
|
|
488
|
+
|
|
489
|
+
Parameters
|
|
490
|
+
----------
|
|
491
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
492
|
+
The covariate matrix.
|
|
493
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
494
|
+
The instrument variable.
|
|
495
|
+
treatment: np.ndarray or pd.Series, optional
|
|
496
|
+
The vector of treatment assignments.
|
|
497
|
+
y: np.ndarray or pd.Series, optional
|
|
498
|
+
The vector of outcomes.
|
|
499
|
+
|
|
500
|
+
Returns
|
|
501
|
+
-------
|
|
502
|
+
np.ndarray
|
|
503
|
+
An array containing the predicted ICE.
|
|
504
|
+
"""
|
|
505
|
+
|
|
506
|
+
T_hat = self.treatment_learner.predict(X=pd.concat([pd.DataFrame(X), pd.DataFrame(w)], axis=1))
|
|
507
|
+
self.outcome_learner.fit(X=pd.concat([pd.DataFrame(X), pd.DataFrame({"T": T_hat})], axis=1), y=y)
|
|
508
|
+
|
|
509
|
+
return np.repeat(self.outcome_learner.coef_, len(X))
|
|
510
|
+
|
|
511
|
+
def estimate_ate(
|
|
512
|
+
self,
|
|
513
|
+
X: np.matrix | np.ndarray | pd.DataFrame,
|
|
514
|
+
treatment: np.ndarray | pd.Series,
|
|
515
|
+
y: np.ndarray | pd.Series = None,
|
|
516
|
+
w: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
517
|
+
pretrain: bool = True,
|
|
518
|
+
) -> float:
|
|
519
|
+
"""
|
|
520
|
+
Predicts the Average Causal Effect (ACE),
|
|
521
|
+
also referred to as the Local Average Treatment Effect (LATE).
|
|
522
|
+
(The term ATE is used in the method name for compatibility purposes.)
|
|
523
|
+
|
|
524
|
+
Parameters
|
|
525
|
+
----------
|
|
526
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
527
|
+
The covariate matrix.
|
|
528
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
529
|
+
The instrument variable.
|
|
530
|
+
treatment: np.ndarray or pd.Series, optional
|
|
531
|
+
The vector of treatment assignments.
|
|
532
|
+
y: np.ndarray or pd.Series, optional
|
|
533
|
+
The vector of outcomes.
|
|
534
|
+
|
|
535
|
+
Returns
|
|
536
|
+
-------
|
|
537
|
+
float
|
|
538
|
+
The value of the ACE.
|
|
539
|
+
"""
|
|
540
|
+
|
|
541
|
+
return self.predict(X=X, treatment=treatment, y=y, w=w).mean()
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
# WORK IN PROGRESS
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
class _ICSW:
|
|
548
|
+
"""
|
|
549
|
+
A basic implementation of the Inverse Complier Score Weighting
|
|
550
|
+
(ICSW) estimator which computes the Average Causal Effect (ACE),
|
|
551
|
+
under the Latent ignorability of compliance assumption.
|
|
552
|
+
Based on Aronow et al. (2013)
|
|
553
|
+
(see https://www.jstor.org/stable/24572676?seq=1).
|
|
554
|
+
Only Supports binary instruments.
|
|
555
|
+
"""
|
|
556
|
+
|
|
557
|
+
def __init__(
|
|
558
|
+
self,
|
|
559
|
+
learner: str | Any | None = None,
|
|
560
|
+
taker_or_complier_learner: str | Any | None = None,
|
|
561
|
+
conditional_taker_learner: str | Any | None = None,
|
|
562
|
+
) -> None:
|
|
563
|
+
"""
|
|
564
|
+
Initialize an Wald IPW estimator for LACE.
|
|
565
|
+
|
|
566
|
+
Parameters
|
|
567
|
+
----------
|
|
568
|
+
iv_probability_learner: str or Any, optional
|
|
569
|
+
Estimator for instrumental variable probability..
|
|
570
|
+
If not provided, defaults to LogisticRegression.
|
|
571
|
+
"""
|
|
572
|
+
if learner is None:
|
|
573
|
+
self.taker_or_complier_learner = learnerFromString("LogisticRegression")
|
|
574
|
+
self.conditional_taker_learner = learnerFromString("LogisticRegression")
|
|
575
|
+
elif isinstance(learner, str):
|
|
576
|
+
self.taker_or_complier_learner = learnerFromString(learner)
|
|
577
|
+
self.conditional_taker_learner = learnerFromString(learner)
|
|
578
|
+
else:
|
|
579
|
+
self.taker_or_complier_learner = clone(learner)
|
|
580
|
+
self.conditional_taker_learner = clone(learner)
|
|
581
|
+
|
|
582
|
+
if taker_or_complier_learner is None:
|
|
583
|
+
self.taker_or_complier_learner = learnerFromString("LogisticRegression")
|
|
584
|
+
elif isinstance(taker_or_complier_learner, str):
|
|
585
|
+
self.taker_or_complier_learner = learnerFromString(taker_or_complier_learner)
|
|
586
|
+
else:
|
|
587
|
+
self.taker_or_complier_learner = clone(taker_or_complier_learner)
|
|
588
|
+
|
|
589
|
+
if conditional_taker_learner is None:
|
|
590
|
+
self.conditional_taker_learner = learnerFromString("LogisticRegression")
|
|
591
|
+
elif isinstance(conditional_taker_learner, str):
|
|
592
|
+
self.conditional_taker_learner = learnerFromString(conditional_taker_learner)
|
|
593
|
+
else:
|
|
594
|
+
self.conditional_taker_learner = clone(conditional_taker_learner)
|
|
595
|
+
|
|
596
|
+
self.link_func = expit
|
|
597
|
+
self.theta_A = None
|
|
598
|
+
self.theta_AC = None
|
|
599
|
+
|
|
600
|
+
def fit(
|
|
601
|
+
self,
|
|
602
|
+
X: np.matrix | np.ndarray | pd.DataFrame,
|
|
603
|
+
treatment: np.ndarray | pd.Series,
|
|
604
|
+
y: np.ndarray | pd.Series,
|
|
605
|
+
w: np.matrix | np.ndarray | pd.DataFrame,
|
|
606
|
+
) -> None:
|
|
607
|
+
"""
|
|
608
|
+
Fit the inference model.
|
|
609
|
+
|
|
610
|
+
Parameters
|
|
611
|
+
----------
|
|
612
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
613
|
+
The covariate matrix.
|
|
614
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
615
|
+
The instrument variable.
|
|
616
|
+
treatment: np.ndarray or pd.Series, optional
|
|
617
|
+
The vector of treatment assignments.
|
|
618
|
+
y: np.ndarray or pd.Series, optional
|
|
619
|
+
The vector of outcomes.
|
|
620
|
+
"""
|
|
621
|
+
|
|
622
|
+
if set(np.unique(w.to_numpy())) != {0, 1}:
|
|
623
|
+
raise ValueError("Instrument must be binary with values 0 and 1 for ACE estimation.")
|
|
624
|
+
|
|
625
|
+
x = X.to_numpy() if not isinstance(X, np.ndarray) else X
|
|
626
|
+
t = treatment.to_numpy() if not isinstance(treatment, np.ndarray) else treatment
|
|
627
|
+
z = w.to_numpy() if not isinstance(w, np.ndarray) else w
|
|
628
|
+
|
|
629
|
+
def Likelihood(theta):
|
|
630
|
+
theta_A = theta[: X.shape[1]]
|
|
631
|
+
theta_AC = theta[X.shape[1] :]
|
|
632
|
+
|
|
633
|
+
def binomial(t, z, *args):
|
|
634
|
+
x = list(args)
|
|
635
|
+
p = self.link_func(x @ theta_AC) * (1 - self.link_func(x @ theta_A)) * z + self.link_func(
|
|
636
|
+
x @ theta_AC
|
|
637
|
+
) * self.link_func(x @ theta_AC)
|
|
638
|
+
return -np.log(p**t * (1 - p) ** (1 - t))
|
|
639
|
+
## NOT FINISHED
|
|
640
|
+
|
|
641
|
+
return
|
|
642
|
+
|
|
643
|
+
print(Likelihood(np.zeros(2 * X.shape[1])))
|
|
644
|
+
|
|
645
|
+
res = minimize(fun=Likelihood, x0=np.zeros(2 * X.shape[1]))
|
|
646
|
+
print(res.x)
|
|
647
|
+
return res.x
|
|
648
|
+
|
|
649
|
+
def predict(
|
|
650
|
+
self,
|
|
651
|
+
X: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
652
|
+
treatment: np.ndarray | pd.Series = None,
|
|
653
|
+
y: np.ndarray | pd.Series = None,
|
|
654
|
+
w: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
655
|
+
) -> np.ndarray:
|
|
656
|
+
"""
|
|
657
|
+
Predict the Individual Treatment Effect (ITE).
|
|
658
|
+
|
|
659
|
+
Parameters
|
|
660
|
+
----------
|
|
661
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
662
|
+
The covariate matrix.
|
|
663
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
664
|
+
The instrument variable.
|
|
665
|
+
treatment: np.ndarray or pd.Series, optional
|
|
666
|
+
The vector of treatment assignments.
|
|
667
|
+
y: np.ndarray or pd.Series, optional
|
|
668
|
+
The vector of outcomes.
|
|
669
|
+
|
|
670
|
+
Returns
|
|
671
|
+
-------
|
|
672
|
+
np.ndarray
|
|
673
|
+
An array containing the predicted ITE.
|
|
674
|
+
"""
|
|
675
|
+
|
|
676
|
+
w = w.iloc[:, 0] if isinstance(w, pd.DataFrame) else w
|
|
677
|
+
|
|
678
|
+
e = self.iv_probability_learner.predict_proba(X)[:, 1]
|
|
679
|
+
|
|
680
|
+
v_func = np.vectorize(lambda e, w, d: w * d / e)
|
|
681
|
+
|
|
682
|
+
return ((v_func(e, w, y) / v_func(e, w, 1).mean()) - (v_func(1 - e, 1 - w, y) / v_func(1 - e, 1 - w, 1).mean())) / (
|
|
683
|
+
(v_func(e, w, treatment) / v_func(e, w, 1).mean())
|
|
684
|
+
- (v_func(1 - e, 1 - w, treatment) / v_func(1 - e, 1 - w, 1).mean())
|
|
685
|
+
).mean()
|
|
686
|
+
|
|
687
|
+
def estimate_ate(
|
|
688
|
+
self,
|
|
689
|
+
X: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
690
|
+
treatment: np.ndarray | pd.Series = None,
|
|
691
|
+
y: np.ndarray | pd.Series = None,
|
|
692
|
+
w: np.matrix | np.ndarray | pd.DataFrame = None,
|
|
693
|
+
pretrain: bool = True,
|
|
694
|
+
) -> float:
|
|
695
|
+
"""
|
|
696
|
+
Predicts the Local Average Causal Effect (LACE),
|
|
697
|
+
also referred to as the Local Average Treatment Effect (LATE).
|
|
698
|
+
(The term "Average Treatment Effect" (ATE) is used in
|
|
699
|
+
the method name for compatibility purposes.)
|
|
700
|
+
|
|
701
|
+
Parameters
|
|
702
|
+
----------
|
|
703
|
+
X: np.matrix or np.ndarray or pd.DataFrame
|
|
704
|
+
The covariate matrix.
|
|
705
|
+
w: np.matrix or np.ndarray or pd.DataFrame
|
|
706
|
+
The instrument variable.
|
|
707
|
+
treatment: np.ndarray or pd.Series, optional
|
|
708
|
+
The vector of treatment assignments.
|
|
709
|
+
y: np.ndarray or pd.Series, optional
|
|
710
|
+
The vector of outcomes.
|
|
711
|
+
|
|
712
|
+
Returns
|
|
713
|
+
-------
|
|
714
|
+
float
|
|
715
|
+
The value of the ACE.
|
|
716
|
+
"""
|
|
717
|
+
|
|
718
|
+
return self.predict(X, treatment, y, w).mean()
|