causarray 0.0.4__tar.gz → 0.0.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.
- {causarray-0.0.4 → causarray-0.0.5}/PKG-INFO +1 -1
- {causarray-0.0.4 → causarray-0.0.5}/causarray/DR_estimation.py +84 -34
- {causarray-0.0.4 → causarray-0.0.5}/causarray/DR_learner.py +5 -7
- causarray-0.0.5/causarray/__about__.py +1 -0
- {causarray-0.0.4 → causarray-0.0.5}/causarray/gcate.py +2 -5
- {causarray-0.0.4 → causarray-0.0.5}/causarray/gcate_opt.py +1 -1
- causarray-0.0.4/causarray/__about__.py +0 -1
- {causarray-0.0.4 → causarray-0.0.5}/.gitignore +0 -0
- {causarray-0.0.4 → causarray-0.0.5}/LICENSE +0 -0
- {causarray-0.0.4 → causarray-0.0.5}/README.md +0 -0
- {causarray-0.0.4 → causarray-0.0.5}/causarray/DR_inference.py +0 -0
- {causarray-0.0.4 → causarray-0.0.5}/causarray/__init__.py +0 -0
- {causarray-0.0.4 → causarray-0.0.5}/causarray/gcate_glm.py +0 -0
- {causarray-0.0.4 → causarray-0.0.5}/causarray/gcate_likelihood.py +0 -0
- {causarray-0.0.4 → causarray-0.0.5}/causarray/utils.py +0 -0
- {causarray-0.0.4 → causarray-0.0.5}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: causarray
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: causarray is a Python module for simultaneous causal inference with an array of outcomes.
|
|
5
5
|
Author-email: Jin-Hong Du <jinhongd@andrew.cmu.com>, Maya Shen <myshen@andrew.cmu.edu>, Hansruedi Mathys <mathysh@pitt.edu>, Kathryn Roeder <jinhongd@andrew.cmu.com>
|
|
6
6
|
Maintainer-email: Jin-Hong Du <jinhongd@andrew.cmu.com>
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from sklearn.linear_model import LogisticRegression
|
|
3
|
-
from sklearn.
|
|
3
|
+
from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
|
|
4
|
+
from sklearn_ensemble_cv import reset_random_seeds, Ensemble, ECV
|
|
4
5
|
from causarray.gcate_glm import fit_glm
|
|
5
6
|
from causarray.utils import *
|
|
6
7
|
from causarray.utils import _filter_params
|
|
8
|
+
from joblib import Parallel, delayed
|
|
9
|
+
from tqdm import tqdm
|
|
7
10
|
import pprint
|
|
8
11
|
|
|
9
12
|
from sklearn.model_selection import KFold, ShuffleSplit
|
|
@@ -82,10 +85,15 @@ def cross_fitting(
|
|
|
82
85
|
pprint.pprint(params_ps)
|
|
83
86
|
pprint.pprint(params_glm)
|
|
84
87
|
|
|
85
|
-
if K>1:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
if K > 1:
|
|
89
|
+
n_samples = X.shape[0]
|
|
90
|
+
if K >= n_samples:
|
|
91
|
+
# Use Leave-One-Out Cross-Validation
|
|
92
|
+
folds = [([i for i in range(n_samples) if i != j], [j]) for j in range(n_samples)]
|
|
93
|
+
else:
|
|
94
|
+
# Initialize KFold cross-validator
|
|
95
|
+
kf = KFold(n_splits=int(K), random_state=0, shuffle=True)
|
|
96
|
+
folds = kf.split(X)
|
|
89
97
|
else:
|
|
90
98
|
folds = [(np.arange(X.shape[0]), np.arange(X.shape[0]))]
|
|
91
99
|
|
|
@@ -95,6 +103,16 @@ def cross_fitting(
|
|
|
95
103
|
fit_Y = True if Y_hat is None else False
|
|
96
104
|
Y_hat = np.zeros((Y.shape[0],Y.shape[1],A.shape[1],2), dtype=float) if fit_Y else Y_hat
|
|
97
105
|
|
|
106
|
+
# perform ECV at once
|
|
107
|
+
if fit_pi and ps_model == 'random_forest_cv':
|
|
108
|
+
info_ecv = run_ecv(X_A, A, **params_ps)
|
|
109
|
+
func_ps, params_ps = _get_func_ps(ps_model, verbose=False, ecv=False,
|
|
110
|
+
kwargs_ensemble=info_ecv['best_params_ensemble'], kwargs_regr=info_ecv['best_params_regr'])
|
|
111
|
+
pprint.pprint('Best parameters for the regression model:')
|
|
112
|
+
pprint.pprint(info_ecv['best_params_regr'])
|
|
113
|
+
pprint.pprint('Best parameters for the ensemble model:')
|
|
114
|
+
pprint.pprint(info_ecv['best_params_ensemble'])
|
|
115
|
+
|
|
98
116
|
# Perform cross-fitting
|
|
99
117
|
for train_index, test_index in folds:
|
|
100
118
|
# Split data
|
|
@@ -178,8 +196,6 @@ def AIPW_mean(Y, A, mu, pi, positive=False):
|
|
|
178
196
|
tau = np.mean(pseudo_y, axis=0)
|
|
179
197
|
|
|
180
198
|
return tau, pseudo_y
|
|
181
|
-
|
|
182
|
-
|
|
183
199
|
|
|
184
200
|
|
|
185
201
|
|
|
@@ -188,51 +204,89 @@ def AIPW_mean(Y, A, mu, pi, positive=False):
|
|
|
188
204
|
|
|
189
205
|
|
|
190
206
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
from sklearn_ensemble_cv import reset_random_seeds, Ensemble, ECV
|
|
194
|
-
from sklearn.tree import DecisionTreeRegressor
|
|
195
|
-
|
|
196
|
-
def fit_rf(X, y, X_test=None, sample_weight=None, M=100, M_max=1000,
|
|
207
|
+
def run_ecv(
|
|
208
|
+
X, y, M=200, M_max=1000,
|
|
197
209
|
# fixed parameters for bagging regressor
|
|
198
|
-
kwargs_ensemble={
|
|
210
|
+
kwargs_ensemble={},
|
|
199
211
|
# fixed parameters for decision tree
|
|
200
|
-
kwargs_regr={
|
|
212
|
+
kwargs_regr={},
|
|
201
213
|
# grid search parameters
|
|
202
|
-
grid_regr
|
|
203
|
-
grid_ensemble
|
|
204
|
-
|
|
214
|
+
grid_regr={},
|
|
215
|
+
grid_ensemble={}
|
|
216
|
+
):
|
|
217
|
+
"""
|
|
218
|
+
Runs Ensemble Cross-Validation (ECV) to find the best hyperparameters.
|
|
219
|
+
"""
|
|
220
|
+
kwargs_ensemble = {**{'verbose': 1, 'bootstrap': True}, **kwargs_ensemble}
|
|
221
|
+
kwargs_regr = {**{'min_samples_split': 20, 'min_samples_leaf': 10, 'max_features': 'sqrt', 'ccp_alpha': 0.02, 'class_weight': 'balanced'}, **kwargs_regr}
|
|
222
|
+
grid_regr = {**{'max_depth': [3, 5, 7]}, **grid_regr}
|
|
223
|
+
grid_ensemble = {**{'random_state': 0, 'max_samples': [0.4, 0.6, 0.8, 1.]}, **grid_ensemble}
|
|
205
224
|
|
|
206
225
|
# Validate integer parameters
|
|
207
226
|
M = int(M)
|
|
208
227
|
M_max = int(M_max)
|
|
209
|
-
# for kwargs in [kwargs_regr, kwargs_ensemble, grid_regr, grid_ensemble]:
|
|
210
|
-
# for param in kwargs:
|
|
211
|
-
# if param in ['max_depth', 'random_state', 'max_leaf_nodes'] and isinstance(kwargs[param], float):
|
|
212
|
-
# kwargs[param] = int(kwargs[param])
|
|
213
228
|
|
|
214
229
|
# Make sure y is 2D
|
|
215
230
|
y = y.reshape(-1, 1) if y.ndim == 1 else y
|
|
216
231
|
|
|
217
232
|
# Run ECV
|
|
218
|
-
|
|
219
|
-
X, y,
|
|
220
|
-
kwargs_regr, kwargs_ensemble,
|
|
233
|
+
_, info_ecv = ECV(
|
|
234
|
+
X, y, DecisionTreeClassifier, grid_regr, grid_ensemble,
|
|
235
|
+
kwargs_regr, kwargs_ensemble,
|
|
221
236
|
M=M, M0=M, M_max=M_max, return_df=True
|
|
222
237
|
)
|
|
223
238
|
|
|
224
239
|
# Replace the in-sample best parameter for 'n_estimators' with extrapolated best parameter
|
|
225
240
|
info_ecv['best_params_ensemble']['n_estimators'] = info_ecv['best_n_estimators_extrapolate']
|
|
226
241
|
|
|
242
|
+
return info_ecv
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def fit_rf(
|
|
246
|
+
X, y, X_test=None, M=100, M_max=1000, ecv=True,
|
|
247
|
+
# fixed parameters for bagging regressor
|
|
248
|
+
kwargs_ensemble={},
|
|
249
|
+
# fixed parameters for decision tree
|
|
250
|
+
kwargs_regr={},
|
|
251
|
+
# grid search parameters
|
|
252
|
+
grid_regr={},
|
|
253
|
+
grid_ensemble={}
|
|
254
|
+
):
|
|
255
|
+
"""
|
|
256
|
+
Fits a Random Forest model using parameters found by ECV.
|
|
257
|
+
"""
|
|
258
|
+
|
|
259
|
+
kwargs_ensemble = {**{'verbose': 1, 'bootstrap': True}, **kwargs_ensemble}
|
|
260
|
+
kwargs_regr = {**{'min_samples_split': 20, 'min_samples_leaf': 10, 'max_features': 'sqrt', 'ccp_alpha': 0.02, 'class_weight': 'balanced'}, **kwargs_regr}
|
|
261
|
+
grid_regr = {**{'max_depth': [3, 5, 7]}, **grid_regr}
|
|
262
|
+
grid_ensemble = {**{'random_state': 0, 'max_samples': [0.4, 0.6, 0.8, 1.]}, **grid_ensemble}
|
|
263
|
+
|
|
264
|
+
# Make sure y is 2D
|
|
265
|
+
y_2d = y.reshape(-1, 1) if y.ndim == 1 else y
|
|
266
|
+
|
|
267
|
+
if ecv:
|
|
268
|
+
# Get best parameters from ECV
|
|
269
|
+
info_ecv = run_ecv(
|
|
270
|
+
X, y_2d, M=M, M_max=M_max,
|
|
271
|
+
kwargs_ensemble=kwargs_ensemble,
|
|
272
|
+
kwargs_regr=kwargs_regr,
|
|
273
|
+
grid_regr=grid_regr,
|
|
274
|
+
grid_ensemble=grid_ensemble
|
|
275
|
+
)
|
|
276
|
+
params_regr = info_ecv['best_params_regr']
|
|
277
|
+
params_ensemble = info_ecv['best_params_ensemble']
|
|
278
|
+
else:
|
|
279
|
+
params_regr = kwargs_regr
|
|
280
|
+
params_ensemble = kwargs_ensemble
|
|
281
|
+
|
|
227
282
|
# Fit the ensemble with the best CV parameters
|
|
228
283
|
regr = Ensemble(
|
|
229
|
-
estimator=
|
|
230
|
-
|
|
231
|
-
|
|
284
|
+
estimator=DecisionTreeClassifier(**params_regr), **params_ensemble).fit(X, y_2d)
|
|
285
|
+
|
|
232
286
|
# Predict
|
|
233
287
|
if X_test is None:
|
|
234
288
|
X_test = X
|
|
235
|
-
return regr.predict(X_test).reshape(-1,
|
|
289
|
+
return regr.predict(X_test).reshape(-1, y_2d.shape[1])
|
|
236
290
|
|
|
237
291
|
|
|
238
292
|
|
|
@@ -252,11 +306,7 @@ def fit_rf_ind_ps(X, Y, *args, **kwargs):
|
|
|
252
306
|
def _fit(X, y, i_ctrl, *args, **kwargs):
|
|
253
307
|
i_case = (y == 1.)
|
|
254
308
|
i_cells = i_ctrl | i_case
|
|
255
|
-
|
|
256
|
-
class_weight = len(y) / (2 * np.bincount(y.astype(int)))
|
|
257
|
-
for a in range(2):
|
|
258
|
-
sample_weight[y == a] = class_weight[a]
|
|
259
|
-
return fit_rf(X[i_cells], y[i_cells], sample_weight=sample_weight[i_cells], *args, **kwargs)
|
|
309
|
+
return fit_rf(X[i_cells], y[i_cells], *args, **kwargs)
|
|
260
310
|
|
|
261
311
|
Y_hat = Parallel(n_jobs=-1)(delayed(_fit)(X, Y[:,j], i_ctrl, *args, **kwargs)
|
|
262
312
|
for j in tqdm(range(Y.shape[1])))
|
|
@@ -74,7 +74,8 @@ def compute_causal_estimand(
|
|
|
74
74
|
Y = Y.astype('float')
|
|
75
75
|
n, p = Y.shape
|
|
76
76
|
|
|
77
|
-
if A.
|
|
77
|
+
if len(A.shape) == 1:
|
|
78
|
+
A = A.reshape(-1,1)
|
|
78
79
|
if isinstance(A, pd.DataFrame):
|
|
79
80
|
trt_names = A.columns
|
|
80
81
|
A = A.values
|
|
@@ -169,7 +170,7 @@ def compute_causal_estimand(
|
|
|
169
170
|
def LFC(
|
|
170
171
|
Y, W, A, W_A=None, family='nb', offset=False,
|
|
171
172
|
Y_hat=None, pi_hat=None, cross_est=False, mask=None, usevar='pooled',
|
|
172
|
-
thres_min=1e-
|
|
173
|
+
thres_min=1e-2, thres_diff=1e-2, eps_var=1e-4,
|
|
173
174
|
fdx=False, fdx_alpha=0.05, fdx_c=0.1,
|
|
174
175
|
verbose=False, **kwargs):
|
|
175
176
|
'''
|
|
@@ -200,9 +201,6 @@ def LFC(
|
|
|
200
201
|
Boolean mask of shape (n, a) for the treatment, indicating which samples are used for
|
|
201
202
|
the estimation of the estimand. This does not affect the estimation of pseudo-outcomes
|
|
202
203
|
and propensity scores.
|
|
203
|
-
usevar : str
|
|
204
|
-
The method to use for estimating the variance of treatment effects.
|
|
205
|
-
Options are 'pooled' (default) or 'unequal'.
|
|
206
204
|
|
|
207
205
|
thres_min : float
|
|
208
206
|
The minimum threshold for the treatment effect.
|
|
@@ -246,12 +244,12 @@ def LFC(
|
|
|
246
244
|
var_1 = np.var(eta_est[A==1], axis=0, ddof=1)
|
|
247
245
|
n_0 = np.sum(A==0)
|
|
248
246
|
n_1 = np.sum(A==1)
|
|
249
|
-
var_est = (var_0 + eps_var) / n_0 + (var_1 + eps_var) / n_1
|
|
247
|
+
var_est = ((var_0 + eps_var) / n_0 + (var_1 + eps_var) / n_1) / 2
|
|
250
248
|
else:
|
|
251
249
|
raise ValueError('usevar must be either "pooled" or "unequal"')
|
|
252
250
|
|
|
253
251
|
# filter out low-expressed genes
|
|
254
|
-
idx = (np.maximum(tau_0,tau_1)<thres_min)
|
|
252
|
+
idx = (np.maximum(np.abs(tau_0),np.abs(tau_1))<thres_min) | (np.abs(tau_1-tau_0)<thres_diff)
|
|
255
253
|
tau_est[idx] = 0.; eta_est[:,idx] = 0.; var_est[idx] = np.inf
|
|
256
254
|
|
|
257
255
|
return eta_est, tau_est, var_est
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.5"
|
|
@@ -82,8 +82,7 @@ def fit_gcate(Y, X, A, r, family='nb', disp_glm=None, disp_family=None, offset=T
|
|
|
82
82
|
kwargs : dict
|
|
83
83
|
Additional keyword arguments.
|
|
84
84
|
'''
|
|
85
|
-
|
|
86
|
-
if A.ndim == 1: A = A[:, None]
|
|
85
|
+
|
|
87
86
|
X = np.hstack((X, A))
|
|
88
87
|
a = A.shape[1]
|
|
89
88
|
Y, kwargs_glm, lam1 = _check_input(Y, X, family, disp_glm, disp_family, offset, c1, **kwargs)
|
|
@@ -196,10 +195,8 @@ def estimate_r(Y, X, A, r_max, c=1.,
|
|
|
196
195
|
df_r : DataFrame
|
|
197
196
|
Results of the number of latent factors.
|
|
198
197
|
'''
|
|
199
|
-
if X.ndim == 1: X = X[:, None]
|
|
200
|
-
if A.ndim == 1: A = A[:, None]
|
|
201
198
|
a, d = A.shape[1], X.shape[1]
|
|
202
|
-
X = np.hstack((X, A))
|
|
199
|
+
X = np.hstack((X, A))
|
|
203
200
|
n, p = Y.shape
|
|
204
201
|
|
|
205
202
|
Y, kwargs_glm, _ = _check_input(Y, X, family, disp_glm, disp_family, offset, None, **kwargs)
|
|
@@ -261,7 +261,7 @@ def alter_min(
|
|
|
261
261
|
kwargs_ls['alpha'] = kwargs_ls['alpha']
|
|
262
262
|
if verbose:
|
|
263
263
|
pprint.pprint({'kwargs_glm':kwargs_glm,'kwargs_ls':kwargs_ls,'kwargs_es':kwargs_es}, compact=True)
|
|
264
|
-
pprint.pprint(f'Fitting GCATE (step {
|
|
264
|
+
pprint.pprint(f'Fitting GCATE (step {2 if P1 is None else 1})...')
|
|
265
265
|
hist = [func_val_pre]
|
|
266
266
|
es = Early_Stopping(**kwargs_es)
|
|
267
267
|
with tqdm(np.arange(kwargs_es['max_iters']), disable=not verbose) as pbar:
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.0.4"
|
|
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
|