tigramite-fast 5.2.10.1__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.
- tigramite/__init__.py +0 -0
- tigramite/causal_effects.py +1525 -0
- tigramite/causal_mediation.py +1592 -0
- tigramite/data_processing.py +1574 -0
- tigramite/graphs.py +1509 -0
- tigramite/independence_tests/LBFGS.py +1114 -0
- tigramite/independence_tests/__init__.py +0 -0
- tigramite/independence_tests/cmiknn.py +661 -0
- tigramite/independence_tests/cmiknn_mixed.py +1397 -0
- tigramite/independence_tests/cmisymb.py +286 -0
- tigramite/independence_tests/gpdc.py +664 -0
- tigramite/independence_tests/gpdc_torch.py +820 -0
- tigramite/independence_tests/gsquared.py +190 -0
- tigramite/independence_tests/independence_tests_base.py +1310 -0
- tigramite/independence_tests/oracle_conditional_independence.py +1582 -0
- tigramite/independence_tests/pairwise_CI.py +383 -0
- tigramite/independence_tests/parcorr.py +369 -0
- tigramite/independence_tests/parcorr_mult.py +485 -0
- tigramite/independence_tests/parcorr_wls.py +451 -0
- tigramite/independence_tests/regressionCI.py +403 -0
- tigramite/independence_tests/robust_parcorr.py +403 -0
- tigramite/jpcmciplus.py +966 -0
- tigramite/lpcmci.py +3649 -0
- tigramite/models.py +2257 -0
- tigramite/pcmci.py +3935 -0
- tigramite/pcmci_base.py +1218 -0
- tigramite/plotting.py +4735 -0
- tigramite/rpcmci.py +467 -0
- tigramite/toymodels/__init__.py +0 -0
- tigramite/toymodels/context_model.py +261 -0
- tigramite/toymodels/non_additive.py +1231 -0
- tigramite/toymodels/structural_causal_processes.py +1201 -0
- tigramite/toymodels/surrogate_generator.py +319 -0
- tigramite_fast-5.2.10.1.dist-info/METADATA +182 -0
- tigramite_fast-5.2.10.1.dist-info/RECORD +38 -0
- tigramite_fast-5.2.10.1.dist-info/WHEEL +5 -0
- tigramite_fast-5.2.10.1.dist-info/licenses/license.txt +621 -0
- tigramite_fast-5.2.10.1.dist-info/top_level.txt +1 -0
tigramite/models.py
ADDED
|
@@ -0,0 +1,2257 @@
|
|
|
1
|
+
"""Tigramite causal inference for time series."""
|
|
2
|
+
|
|
3
|
+
# Author: Jakob Runge <jakob@jakob-runge.com>
|
|
4
|
+
#
|
|
5
|
+
# License: GNU General Public License v3.0
|
|
6
|
+
|
|
7
|
+
from __future__ import print_function
|
|
8
|
+
from copy import deepcopy
|
|
9
|
+
import json, warnings, os, pathlib
|
|
10
|
+
import numpy as np
|
|
11
|
+
import sklearn
|
|
12
|
+
import sklearn.linear_model
|
|
13
|
+
import networkx
|
|
14
|
+
from tigramite.data_processing import DataFrame
|
|
15
|
+
from tigramite.pcmci import PCMCI
|
|
16
|
+
|
|
17
|
+
class Models():
|
|
18
|
+
"""Base class for time series models.
|
|
19
|
+
|
|
20
|
+
Allows to fit any model from sklearn to the parents of a target variable.
|
|
21
|
+
Also takes care of missing values, masking and preprocessing. If the
|
|
22
|
+
target variable is multivariate, a model that supports multi-output
|
|
23
|
+
regression must be used. Note that
|
|
24
|
+
sklearn.multioutput.MultiOutputRegressor allows to extend single-output
|
|
25
|
+
models.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
dataframe : data object
|
|
30
|
+
Tigramite dataframe object. It must have the attributes dataframe.values
|
|
31
|
+
yielding a numpy array of shape (observations T, variables N) and
|
|
32
|
+
optionally a mask of the same shape and a missing values flag.
|
|
33
|
+
model : sklearn model object
|
|
34
|
+
For example, sklearn.linear_model.LinearRegression() for a linear
|
|
35
|
+
regression model.
|
|
36
|
+
conditional_model : sklearn model object, optional (default: None)
|
|
37
|
+
Used to fit conditional causal effects in nested regression.
|
|
38
|
+
If None, model is used.
|
|
39
|
+
data_transform : sklearn preprocessing object, optional (default: None)
|
|
40
|
+
Used to transform data prior to fitting. For example,
|
|
41
|
+
sklearn.preprocessing.StandardScaler for simple standardization. The
|
|
42
|
+
fitted parameters are stored. Note that the inverse_transform is then
|
|
43
|
+
applied to the predicted data.
|
|
44
|
+
mask_type : {None, 'y','x','z','xy','xz','yz','xyz'}
|
|
45
|
+
Masking mode: Indicators for which variables in the dependence
|
|
46
|
+
measure I(X; Y | Z) the samples should be masked. If None, the mask
|
|
47
|
+
is not used. Explained in tutorial on masking and missing values.
|
|
48
|
+
verbosity : int, optional (default: 0)
|
|
49
|
+
Level of verbosity.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self,
|
|
53
|
+
dataframe,
|
|
54
|
+
model,
|
|
55
|
+
conditional_model=None,
|
|
56
|
+
data_transform=None,
|
|
57
|
+
mask_type=None,
|
|
58
|
+
verbosity=0):
|
|
59
|
+
# Set the mask type and dataframe object
|
|
60
|
+
self.mask_type = mask_type
|
|
61
|
+
self.dataframe = dataframe
|
|
62
|
+
# Get the number of nodes and length for this dataset
|
|
63
|
+
self.N = self.dataframe.N
|
|
64
|
+
self.T = self.dataframe.T
|
|
65
|
+
# Set the model to be used
|
|
66
|
+
self.model = model
|
|
67
|
+
if conditional_model is None:
|
|
68
|
+
self.conditional_model = model
|
|
69
|
+
else:
|
|
70
|
+
self.conditional_model = conditional_model
|
|
71
|
+
# Set the data_transform object and verbosity
|
|
72
|
+
self.data_transform = data_transform
|
|
73
|
+
self.verbosity = verbosity
|
|
74
|
+
# Initialize the object that will be set later
|
|
75
|
+
self.all_parents = None
|
|
76
|
+
self.selected_variables = None
|
|
77
|
+
self.tau_max = None
|
|
78
|
+
self.fit_results = None
|
|
79
|
+
|
|
80
|
+
# @profile
|
|
81
|
+
def get_general_fitted_model(self,
|
|
82
|
+
Y, X, Z=None,
|
|
83
|
+
conditions=None,
|
|
84
|
+
tau_max=None,
|
|
85
|
+
cut_off='max_lag_or_tau_max',
|
|
86
|
+
empty_predictors_function=np.mean,
|
|
87
|
+
return_data=False):
|
|
88
|
+
"""Fit time series model.
|
|
89
|
+
|
|
90
|
+
For each variable in selected_variables, the sklearn model is fitted
|
|
91
|
+
with :math:`y` given by the target variable(s), and :math:`X` given by its
|
|
92
|
+
parents. The fitted model class is returned for later use.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
X, Y, Z : lists of tuples
|
|
97
|
+
List of variables for estimating model Y = f(X,Z)
|
|
98
|
+
conditions : list of tuples.
|
|
99
|
+
Conditions for estimating conditional causal effects.
|
|
100
|
+
tau_max : int, optional (default: None)
|
|
101
|
+
Maximum time lag. If None, the maximum lag in all_parents is used.
|
|
102
|
+
cut_off : {'max_lag_or_tau_max', '2xtau_max', 'max_lag'}
|
|
103
|
+
How many samples to cutoff at the beginning. The default is
|
|
104
|
+
'max_lag_or_tau_max', which uses the maximum of tau_max and the
|
|
105
|
+
conditions. This is useful to compare multiple models on the same
|
|
106
|
+
sample. Other options are '2xtau_max', which guarantees that MCI
|
|
107
|
+
tests are all conducted on the same samples. Last, 'max_lag' uses
|
|
108
|
+
as much samples as possible.
|
|
109
|
+
empty_predictors_function : function
|
|
110
|
+
Function to apply to y if no predictors are given.
|
|
111
|
+
return_data : bool, optional (default: False)
|
|
112
|
+
Whether to save the data array.
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
fit_results : dictionary of sklearn model objects
|
|
117
|
+
Returns the sklearn model after fitting. Also returns the data
|
|
118
|
+
transformation parameters.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
def get_vectorized_length(W):
|
|
122
|
+
return sum([len(self.dataframe.vector_vars[w[0]]) for w in W])
|
|
123
|
+
|
|
124
|
+
self.X = X
|
|
125
|
+
self.Y = Y
|
|
126
|
+
|
|
127
|
+
if conditions is None:
|
|
128
|
+
conditions = []
|
|
129
|
+
self.conditions = conditions
|
|
130
|
+
|
|
131
|
+
if Z is not None:
|
|
132
|
+
Z = [z for z in Z if z not in conditions]
|
|
133
|
+
|
|
134
|
+
self.Z = Z
|
|
135
|
+
|
|
136
|
+
# lenX = len(self.X)
|
|
137
|
+
# lenS = len(self.conditions)
|
|
138
|
+
self.lenX = get_vectorized_length(self.X)
|
|
139
|
+
self.lenS = get_vectorized_length(self.conditions)
|
|
140
|
+
|
|
141
|
+
self.cut_off = cut_off
|
|
142
|
+
|
|
143
|
+
# Find the maximal conditions lag
|
|
144
|
+
max_lag = 0
|
|
145
|
+
for y in self.Y:
|
|
146
|
+
this_lag = np.abs(np.array(self.X + self.Z + self.conditions)[:, 1]).max()
|
|
147
|
+
max_lag = max(max_lag, this_lag)
|
|
148
|
+
# Set the default tau max and check if it should be overwritten
|
|
149
|
+
if tau_max is None:
|
|
150
|
+
self.tau_max = max_lag
|
|
151
|
+
else:
|
|
152
|
+
self.tau_max = tau_max
|
|
153
|
+
if self.tau_max < max_lag:
|
|
154
|
+
raise ValueError("tau_max = %d, but must be at least "
|
|
155
|
+
" max_lag = %d"
|
|
156
|
+
"" % (self.tau_max, max_lag))
|
|
157
|
+
|
|
158
|
+
# Construct array of shape (var, time)
|
|
159
|
+
array, xyz, _ = \
|
|
160
|
+
self.dataframe.construct_array(X=self.X, Y=self.Y,
|
|
161
|
+
Z=self.conditions,
|
|
162
|
+
extraZ=self.Z,
|
|
163
|
+
tau_max=self.tau_max,
|
|
164
|
+
mask_type=self.mask_type,
|
|
165
|
+
cut_off=self.cut_off,
|
|
166
|
+
remove_overlaps=True,
|
|
167
|
+
verbosity=self.verbosity)
|
|
168
|
+
|
|
169
|
+
# Transform the data if needed
|
|
170
|
+
self.fitted_data_transform = None
|
|
171
|
+
if self.data_transform is not None:
|
|
172
|
+
# Fit only X, Y, and S for later use in transforming input
|
|
173
|
+
X_transform = deepcopy(self.data_transform)
|
|
174
|
+
x_indices = list(np.where(xyz==0)[0])
|
|
175
|
+
X_transform.fit(array[x_indices, :].T)
|
|
176
|
+
self.fitted_data_transform = {'X': X_transform}
|
|
177
|
+
Y_transform = deepcopy(self.data_transform)
|
|
178
|
+
y_indices = list(np.where(xyz==1)[0])
|
|
179
|
+
Y_transform.fit(array[y_indices, :].T)
|
|
180
|
+
self.fitted_data_transform['Y'] = Y_transform
|
|
181
|
+
if len(self.conditions) > 0:
|
|
182
|
+
S_transform = deepcopy(self.data_transform)
|
|
183
|
+
s_indices = list(np.where(xyz==2)[0])
|
|
184
|
+
S_transform.fit(array[s_indices, :].T)
|
|
185
|
+
self.fitted_data_transform['S'] = S_transform
|
|
186
|
+
|
|
187
|
+
# Now transform whole array
|
|
188
|
+
# TODO: Rather concatenate transformed arrays
|
|
189
|
+
all_transform = deepcopy(self.data_transform)
|
|
190
|
+
array = all_transform.fit_transform(X=array.T).T
|
|
191
|
+
|
|
192
|
+
# Fit the model
|
|
193
|
+
# Copy and fit the model
|
|
194
|
+
a_model = deepcopy(self.model)
|
|
195
|
+
|
|
196
|
+
predictor_indices = list(np.where(xyz==0)[0]) \
|
|
197
|
+
+ list(np.where(xyz==3)[0]) \
|
|
198
|
+
+ list(np.where(xyz==2)[0])
|
|
199
|
+
predictor_array = array[predictor_indices, :].T
|
|
200
|
+
target_array = array[np.where(xyz==1)[0], :].T
|
|
201
|
+
|
|
202
|
+
if predictor_array.size == 0:
|
|
203
|
+
# Just fit default (eg, mean)
|
|
204
|
+
class EmptyPredictorModel:
|
|
205
|
+
def fit(self, X, y):
|
|
206
|
+
if y.ndim == 1:
|
|
207
|
+
self.result = empty_predictors_function(y)
|
|
208
|
+
else:
|
|
209
|
+
self.result = empty_predictors_function(y, axis=0)
|
|
210
|
+
def predict(self, X):
|
|
211
|
+
return self.result
|
|
212
|
+
a_model = EmptyPredictorModel()
|
|
213
|
+
|
|
214
|
+
a_model.fit(X=predictor_array, y=target_array)
|
|
215
|
+
|
|
216
|
+
# Cache the results
|
|
217
|
+
fit_results = {}
|
|
218
|
+
fit_results['observation_array'] = array
|
|
219
|
+
fit_results['xyz'] = xyz
|
|
220
|
+
fit_results['model'] = a_model
|
|
221
|
+
# Cache the data transform
|
|
222
|
+
fit_results['fitted_data_transform'] = self.fitted_data_transform
|
|
223
|
+
|
|
224
|
+
# Cache and return the fit results
|
|
225
|
+
self.fit_results = fit_results
|
|
226
|
+
return fit_results
|
|
227
|
+
|
|
228
|
+
# @profile
|
|
229
|
+
def get_general_prediction(self,
|
|
230
|
+
intervention_data,
|
|
231
|
+
conditions_data=None,
|
|
232
|
+
pred_params=None,
|
|
233
|
+
transform_interventions_and_prediction=False,
|
|
234
|
+
return_further_pred_results=False,
|
|
235
|
+
aggregation_func=np.mean,
|
|
236
|
+
intervention_type='hard',
|
|
237
|
+
):
|
|
238
|
+
r"""Predict effect of intervention with fitted model.
|
|
239
|
+
|
|
240
|
+
Uses the model.predict() function of the sklearn model.
|
|
241
|
+
|
|
242
|
+
Parameters
|
|
243
|
+
----------
|
|
244
|
+
intervention_data : numpy array
|
|
245
|
+
Numpy array of shape (n_interventions, len(X)) that contains the do(X) values.
|
|
246
|
+
conditions_data : data object, optional
|
|
247
|
+
Numpy array of shape (n_interventions, len(S)) that contains the S=s values.
|
|
248
|
+
pred_params : dict, optional
|
|
249
|
+
Optional parameters passed on to sklearn prediction function (model and
|
|
250
|
+
conditional_model).
|
|
251
|
+
transform_interventions_and_prediction : bool (default: False)
|
|
252
|
+
Whether to perform the inverse data_transform on prediction results.
|
|
253
|
+
return_further_pred_results : bool, optional (default: False)
|
|
254
|
+
In case the predictor class returns more than just the expected value,
|
|
255
|
+
the entire results can be returned.
|
|
256
|
+
aggregation_func : callable
|
|
257
|
+
Callable applied to output of 'predict'. Default is 'np.mean'.
|
|
258
|
+
intervention_type : {'hard', 'soft'}
|
|
259
|
+
Specify whether intervention is 'hard' (set value) or 'soft'
|
|
260
|
+
(add value to observed data).
|
|
261
|
+
|
|
262
|
+
Returns
|
|
263
|
+
-------
|
|
264
|
+
Results from prediction.
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
n_interventions, _ = intervention_data.shape
|
|
268
|
+
|
|
269
|
+
if intervention_data.shape[1] != self.lenX:
|
|
270
|
+
raise ValueError("intervention_data.shape[1] must be len(X).")
|
|
271
|
+
|
|
272
|
+
if conditions_data is not None:
|
|
273
|
+
if conditions_data.shape[1] != len(self.conditions):
|
|
274
|
+
raise ValueError("conditions_data.shape[1] must be len(S).")
|
|
275
|
+
if conditions_data.shape[0] != intervention_data.shape[0]:
|
|
276
|
+
raise ValueError("conditions_data.shape[0] must match intervention_data.shape[0].")
|
|
277
|
+
|
|
278
|
+
# Print message
|
|
279
|
+
if self.verbosity > 1:
|
|
280
|
+
print("\n## Predicting target %s" % str(self.Y))
|
|
281
|
+
if pred_params is not None:
|
|
282
|
+
for key in list(pred_params):
|
|
283
|
+
print("%s = %s" % (key, pred_params[key]))
|
|
284
|
+
|
|
285
|
+
# Default value for pred_params
|
|
286
|
+
if pred_params is None:
|
|
287
|
+
pred_params = {}
|
|
288
|
+
|
|
289
|
+
# Check the model is fitted.
|
|
290
|
+
if self.fit_results is None:
|
|
291
|
+
raise ValueError("Model not yet fitted.")
|
|
292
|
+
|
|
293
|
+
# Transform the data if needed
|
|
294
|
+
fitted_data_transform = self.fit_results['fitted_data_transform']
|
|
295
|
+
if transform_interventions_and_prediction and fitted_data_transform is not None:
|
|
296
|
+
intervention_data = fitted_data_transform['X'].transform(X=intervention_data)
|
|
297
|
+
if self.conditions is not None and conditions_data is not None:
|
|
298
|
+
conditions_data = fitted_data_transform['S'].transform(X=conditions_data)
|
|
299
|
+
|
|
300
|
+
# Extract observational Z from stored array
|
|
301
|
+
z_indices = list(np.where(self.fit_results['xyz']==3)[0])
|
|
302
|
+
z_array = self.fit_results['observation_array'][z_indices, :].T
|
|
303
|
+
Tobs = len(self.fit_results['observation_array'].T)
|
|
304
|
+
|
|
305
|
+
if intervention_type == 'soft':
|
|
306
|
+
x_indices = list(np.where(self.fit_results['xyz']==0)[0])
|
|
307
|
+
x_array = self.fit_results['observation_array'][x_indices, :].T
|
|
308
|
+
|
|
309
|
+
if self.conditions is not None and conditions_data is not None:
|
|
310
|
+
s_indices = list(np.where(self.fit_results['xyz']==2)[0])
|
|
311
|
+
s_array = self.fit_results['observation_array'][s_indices, :].T
|
|
312
|
+
|
|
313
|
+
pred_dict = {}
|
|
314
|
+
|
|
315
|
+
# Now iterate through interventions (and potentially S)
|
|
316
|
+
for index, dox_vals in enumerate(intervention_data):
|
|
317
|
+
# Construct XZS-array
|
|
318
|
+
intervention_array = dox_vals.reshape(1, self.lenX) * np.ones((Tobs, self.lenX))
|
|
319
|
+
if intervention_type == 'soft':
|
|
320
|
+
intervention_array += x_array
|
|
321
|
+
|
|
322
|
+
predictor_array = intervention_array
|
|
323
|
+
|
|
324
|
+
if len(self.Z) > 0:
|
|
325
|
+
predictor_array = np.hstack((predictor_array, z_array))
|
|
326
|
+
|
|
327
|
+
if self.conditions is not None and conditions_data is not None:
|
|
328
|
+
conditions_array = conditions_data[index].reshape(1, self.lenS) * np.ones((Tobs, self.lenS))
|
|
329
|
+
predictor_array = np.hstack((predictor_array, conditions_array))
|
|
330
|
+
|
|
331
|
+
predicted_vals = self.fit_results['model'].predict(
|
|
332
|
+
X=predictor_array, **pred_params)
|
|
333
|
+
|
|
334
|
+
if self.conditions is not None and conditions_data is not None:
|
|
335
|
+
|
|
336
|
+
a_conditional_model = deepcopy(self.conditional_model)
|
|
337
|
+
|
|
338
|
+
if type(predicted_vals) is tuple:
|
|
339
|
+
predicted_vals_here = predicted_vals[0]
|
|
340
|
+
else:
|
|
341
|
+
predicted_vals_here = predicted_vals
|
|
342
|
+
|
|
343
|
+
a_conditional_model.fit(X=s_array, y=predicted_vals_here)
|
|
344
|
+
self.fit_results['conditional_model'] = a_conditional_model
|
|
345
|
+
|
|
346
|
+
predicted_vals = a_conditional_model.predict(
|
|
347
|
+
X=conditions_data[index].reshape(1, self.lenS), **pred_params) # was conditions_data before
|
|
348
|
+
|
|
349
|
+
if transform_interventions_and_prediction and fitted_data_transform is not None:
|
|
350
|
+
predicted_vals = fitted_data_transform['Y'].inverse_transform(X=predicted_vals).squeeze()
|
|
351
|
+
|
|
352
|
+
pred_dict[index] = predicted_vals
|
|
353
|
+
|
|
354
|
+
# Apply aggregation function
|
|
355
|
+
if type(predicted_vals) is tuple:
|
|
356
|
+
aggregated_pred = aggregation_func(predicted_vals[0], axis=0)
|
|
357
|
+
else:
|
|
358
|
+
aggregated_pred = aggregation_func(predicted_vals, axis=0)
|
|
359
|
+
|
|
360
|
+
aggregated_pred = aggregated_pred.squeeze()
|
|
361
|
+
|
|
362
|
+
if index == 0:
|
|
363
|
+
predicted_array = np.zeros((n_interventions, ) + aggregated_pred.shape,
|
|
364
|
+
dtype=aggregated_pred.dtype)
|
|
365
|
+
|
|
366
|
+
predicted_array[index] = aggregated_pred
|
|
367
|
+
|
|
368
|
+
# if fitted_data_transform is not None:
|
|
369
|
+
# rescaled = fitted_data_transform['Y'].inverse_transform(X=predicted_array[index, iy].reshape(-1, 1))
|
|
370
|
+
# predicted_array[index, iy] = rescaled.squeeze()
|
|
371
|
+
|
|
372
|
+
if return_further_pred_results:
|
|
373
|
+
return predicted_array, pred_dict
|
|
374
|
+
else:
|
|
375
|
+
return predicted_array
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def fit_full_model(self, all_parents,
|
|
379
|
+
selected_variables=None,
|
|
380
|
+
tau_max=None,
|
|
381
|
+
cut_off='max_lag_or_tau_max',
|
|
382
|
+
empty_predictors_function=np.mean,
|
|
383
|
+
return_data=False):
|
|
384
|
+
"""Fit time series model.
|
|
385
|
+
|
|
386
|
+
For each variable in selected_variables, the sklearn model is fitted
|
|
387
|
+
with :math:`y` given by the target variable, and :math:`X` given by its
|
|
388
|
+
parents. The fitted model class is returned for later use.
|
|
389
|
+
|
|
390
|
+
Parameters
|
|
391
|
+
----------
|
|
392
|
+
all_parents : dictionary
|
|
393
|
+
Dictionary of form {0:[(0, -1), (3, 0), ...], 1:[], ...} containing
|
|
394
|
+
the parents estimated with PCMCI.
|
|
395
|
+
selected_variables : list of integers, optional (default: range(N))
|
|
396
|
+
Specify to estimate parents only for selected variables. If None is
|
|
397
|
+
passed, parents are estimated for all variables.
|
|
398
|
+
tau_max : int, optional (default: None)
|
|
399
|
+
Maximum time lag. If None, the maximum lag in all_parents is used.
|
|
400
|
+
cut_off : {'max_lag_or_tau_max', '2xtau_max', 'max_lag'}
|
|
401
|
+
How many samples to cutoff at the beginning. The default is
|
|
402
|
+
'max_lag_or_tau_max', which uses the maximum of tau_max and the
|
|
403
|
+
conditions. This is useful to compare multiple models on the same
|
|
404
|
+
sample. Other options are '2xtau_max', which guarantees that MCI
|
|
405
|
+
tests are all conducted on the same samples. Last, 'max_lag' uses
|
|
406
|
+
as much samples as possible.
|
|
407
|
+
empty_predictors_function : function
|
|
408
|
+
Function to apply to y if no predictors are given.
|
|
409
|
+
return_data : bool, optional (default: False)
|
|
410
|
+
Whether to save the data array.
|
|
411
|
+
|
|
412
|
+
Returns
|
|
413
|
+
-------
|
|
414
|
+
fit_results : dictionary of sklearn model objects for each variable
|
|
415
|
+
Returns the sklearn model after fitting. Also returns the data
|
|
416
|
+
transformation parameters.
|
|
417
|
+
"""
|
|
418
|
+
# Initialize the fit by setting the instance's all_parents attribute
|
|
419
|
+
self.all_parents = all_parents
|
|
420
|
+
# Set the default selected variables to all variables and check if this
|
|
421
|
+
# should be overwritten
|
|
422
|
+
self.selected_variables = range(self.N)
|
|
423
|
+
if selected_variables is not None:
|
|
424
|
+
self.selected_variables = selected_variables
|
|
425
|
+
# Find the maximal parents lag
|
|
426
|
+
max_parents_lag = 0
|
|
427
|
+
for j in self.selected_variables:
|
|
428
|
+
if all_parents[j]:
|
|
429
|
+
this_parent_lag = np.abs(np.array(all_parents[j])[:, 1]).max()
|
|
430
|
+
max_parents_lag = max(max_parents_lag, this_parent_lag)
|
|
431
|
+
# Set the default tau_max and check if it should be overwritten
|
|
432
|
+
self.tau_max = max_parents_lag
|
|
433
|
+
if tau_max is not None:
|
|
434
|
+
self.tau_max = tau_max
|
|
435
|
+
if self.tau_max < max_parents_lag:
|
|
436
|
+
raise ValueError("tau_max = %d, but must be at least "
|
|
437
|
+
" max_parents_lag = %d"
|
|
438
|
+
"" % (self.tau_max, max_parents_lag))
|
|
439
|
+
# Initialize the fit results
|
|
440
|
+
fit_results = {}
|
|
441
|
+
for j in self.selected_variables:
|
|
442
|
+
Y = [(j, 0)]
|
|
443
|
+
X = [(j, 0)] # dummy
|
|
444
|
+
Z = self.all_parents[j]
|
|
445
|
+
array, xyz, _ = \
|
|
446
|
+
self.dataframe.construct_array(X, Y, Z,
|
|
447
|
+
tau_max=self.tau_max,
|
|
448
|
+
mask_type=self.mask_type,
|
|
449
|
+
cut_off=cut_off,
|
|
450
|
+
remove_overlaps=True,
|
|
451
|
+
verbosity=self.verbosity)
|
|
452
|
+
# Get the dimensions out of the constructed array
|
|
453
|
+
dim, T = array.shape
|
|
454
|
+
dim_z = dim - 2
|
|
455
|
+
# Transform the data if needed
|
|
456
|
+
if self.data_transform is not None:
|
|
457
|
+
array = self.data_transform.fit_transform(X=array.T).T
|
|
458
|
+
# Cache the results
|
|
459
|
+
fit_results[j] = {}
|
|
460
|
+
# Cache the data transform
|
|
461
|
+
fit_results[j]['data_transform'] = deepcopy(self.data_transform)
|
|
462
|
+
|
|
463
|
+
if return_data:
|
|
464
|
+
# Cache the data if needed
|
|
465
|
+
fit_results[j]['data'] = array
|
|
466
|
+
fit_results[j]['used_indices'] = self.dataframe.use_indices_dataset_dict
|
|
467
|
+
# Copy and fit the model if there are any parents for this variable to fit
|
|
468
|
+
a_model = deepcopy(self.model)
|
|
469
|
+
if dim_z > 0:
|
|
470
|
+
a_model.fit(X=array[2:].T, y=array[1])
|
|
471
|
+
else:
|
|
472
|
+
# Just fit default (eg, mean)
|
|
473
|
+
class EmptyPredictorModel:
|
|
474
|
+
def fit(self, X, y):
|
|
475
|
+
self.result = empty_predictors_function(y)
|
|
476
|
+
def predict(self, X):
|
|
477
|
+
return self.result
|
|
478
|
+
a_model = EmptyPredictorModel()
|
|
479
|
+
# a_model = empty_predictors_model(array[1])
|
|
480
|
+
a_model.fit(X=array[2:].T, y=array[1])
|
|
481
|
+
|
|
482
|
+
fit_results[j]['model'] = a_model
|
|
483
|
+
|
|
484
|
+
# Cache and return the fit results
|
|
485
|
+
self.fit_results = fit_results
|
|
486
|
+
return fit_results
|
|
487
|
+
|
|
488
|
+
def get_coefs(self):
|
|
489
|
+
"""Returns dictionary of coefficients for linear models.
|
|
490
|
+
|
|
491
|
+
Only for models from sklearn.linear_model
|
|
492
|
+
|
|
493
|
+
Returns
|
|
494
|
+
-------
|
|
495
|
+
coeffs : dictionary
|
|
496
|
+
Dictionary of dictionaries for each variable with keys given by the
|
|
497
|
+
parents and the regression coefficients as values.
|
|
498
|
+
"""
|
|
499
|
+
coeffs = {}
|
|
500
|
+
for j in self.selected_variables:
|
|
501
|
+
coeffs[j] = {}
|
|
502
|
+
for ipar, par in enumerate(self.all_parents[j]):
|
|
503
|
+
coeffs[j][par] = self.fit_results[j]['model'].coef_[ipar]
|
|
504
|
+
return coeffs
|
|
505
|
+
|
|
506
|
+
def get_val_matrix(self):
|
|
507
|
+
"""Returns the coefficient array for different lags for linear model.
|
|
508
|
+
|
|
509
|
+
Requires fit_model() before. An entry val_matrix[i,j,tau] gives the
|
|
510
|
+
coefficient of the link from i to j at lag tau, including tau=0.
|
|
511
|
+
|
|
512
|
+
Returns
|
|
513
|
+
-------
|
|
514
|
+
val_matrix : array-like, shape (N, N, tau_max + 1)
|
|
515
|
+
Array of coefficients for each time lag, including lag-zero.
|
|
516
|
+
"""
|
|
517
|
+
|
|
518
|
+
coeffs = self.get_coefs()
|
|
519
|
+
val_matrix = np.zeros((self.N, self.N, self.tau_max + 1, ))
|
|
520
|
+
|
|
521
|
+
for j in list(coeffs):
|
|
522
|
+
for par in list(coeffs[j]):
|
|
523
|
+
i, tau = par
|
|
524
|
+
val_matrix[i,j,abs(tau)] = coeffs[j][par]
|
|
525
|
+
|
|
526
|
+
return val_matrix
|
|
527
|
+
|
|
528
|
+
def predict_full_model(self,
|
|
529
|
+
new_data=None,
|
|
530
|
+
pred_params=None,
|
|
531
|
+
cut_off='max_lag_or_tau_max'):
|
|
532
|
+
r"""Predict target variable with fitted model.
|
|
533
|
+
|
|
534
|
+
Uses the model.predict() function of the sklearn model.
|
|
535
|
+
|
|
536
|
+
A list of predicted time series for self.selected_variables is returned.
|
|
537
|
+
|
|
538
|
+
Parameters
|
|
539
|
+
----------
|
|
540
|
+
new_data : data object, optional
|
|
541
|
+
New Tigramite dataframe object with optional new mask. Note that
|
|
542
|
+
the data will be cut off according to cut_off, see parameter
|
|
543
|
+
`cut_off` below.
|
|
544
|
+
pred_params : dict, optional
|
|
545
|
+
Optional parameters passed on to sklearn prediction function.
|
|
546
|
+
cut_off : {'2xtau_max', 'max_lag', 'max_lag_or_tau_max'}
|
|
547
|
+
How many samples to cutoff at the beginning. The default is
|
|
548
|
+
'2xtau_max', which guarantees that MCI tests are all conducted on
|
|
549
|
+
the same samples. For modeling, 'max_lag_or_tau_max' can be used,
|
|
550
|
+
which uses the maximum of tau_max and the conditions, which is
|
|
551
|
+
useful to compare multiple models on the same sample. Last,
|
|
552
|
+
'max_lag' uses as much samples as possible.
|
|
553
|
+
|
|
554
|
+
Returns
|
|
555
|
+
-------
|
|
556
|
+
Results from prediction.
|
|
557
|
+
"""
|
|
558
|
+
|
|
559
|
+
if hasattr(self, 'selected_variables'):
|
|
560
|
+
target_list = self.selected_variables
|
|
561
|
+
else:
|
|
562
|
+
raise ValueError("Model not yet fitted.")
|
|
563
|
+
|
|
564
|
+
pred_list = []
|
|
565
|
+
self.stored_test_array = {}
|
|
566
|
+
for target in target_list:
|
|
567
|
+
# Default value for pred_params
|
|
568
|
+
if pred_params is None:
|
|
569
|
+
pred_params = {}
|
|
570
|
+
|
|
571
|
+
# Construct the array form of the data
|
|
572
|
+
Y = [(target, 0)] # dummy
|
|
573
|
+
X = [(target, 0)] # dummy
|
|
574
|
+
Z = self.all_parents[target]
|
|
575
|
+
|
|
576
|
+
# Check if we've passed a new dataframe object
|
|
577
|
+
if new_data is not None:
|
|
578
|
+
# if new_data.mask is None:
|
|
579
|
+
# # if no mask is supplied, use the same mask as for the fitted array
|
|
580
|
+
# new_data_mask = self.test_mask
|
|
581
|
+
# else:
|
|
582
|
+
new_data_mask = new_data.mask
|
|
583
|
+
test_array, _, _ = new_data.construct_array(X, Y, Z,
|
|
584
|
+
tau_max=self.tau_max,
|
|
585
|
+
mask=new_data_mask,
|
|
586
|
+
mask_type=self.mask_type,
|
|
587
|
+
cut_off=cut_off,
|
|
588
|
+
remove_overlaps=True,
|
|
589
|
+
verbosity=self.verbosity)
|
|
590
|
+
# Otherwise use the default values
|
|
591
|
+
else:
|
|
592
|
+
test_array, _, _ = \
|
|
593
|
+
self.dataframe.construct_array(X, Y, Z,
|
|
594
|
+
tau_max=self.tau_max,
|
|
595
|
+
mask_type=self.mask_type,
|
|
596
|
+
cut_off=cut_off,
|
|
597
|
+
remove_overlaps=True,
|
|
598
|
+
verbosity=self.verbosity)
|
|
599
|
+
# Transform the data if needed
|
|
600
|
+
a_transform = self.fit_results[target]['data_transform']
|
|
601
|
+
if a_transform is not None:
|
|
602
|
+
test_array = a_transform.transform(X=test_array.T).T
|
|
603
|
+
# Cache the test array
|
|
604
|
+
self.stored_test_array[target] = test_array
|
|
605
|
+
# Run the predictor
|
|
606
|
+
predicted = self.fit_results[target]['model'].predict(
|
|
607
|
+
X=test_array[2:].T, **pred_params)
|
|
608
|
+
|
|
609
|
+
if test_array[2:].size == 0:
|
|
610
|
+
# If there are no predictors, return the value of
|
|
611
|
+
# empty_predictors_function, which is np.mean
|
|
612
|
+
# and expand to the test array length
|
|
613
|
+
predicted = predicted * np.ones(test_array.shape[1])
|
|
614
|
+
|
|
615
|
+
pred_list.append(predicted)
|
|
616
|
+
|
|
617
|
+
return pred_list
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def get_residuals_cov_mean(self, new_data=None, pred_params=None):
|
|
621
|
+
r"""Returns covariance and means of residuals from fitted model.
|
|
622
|
+
|
|
623
|
+
Residuals are available as self.residuals.
|
|
624
|
+
|
|
625
|
+
Parameters
|
|
626
|
+
----------
|
|
627
|
+
new_data : data object, optional
|
|
628
|
+
New Tigramite dataframe object with optional new mask. Note that
|
|
629
|
+
the data will be cut off according to cut_off, see parameter
|
|
630
|
+
`cut_off` below.
|
|
631
|
+
pred_params : dict, optional
|
|
632
|
+
Optional parameters passed on to sklearn prediction function.
|
|
633
|
+
|
|
634
|
+
Returns
|
|
635
|
+
-------
|
|
636
|
+
Results from prediction.
|
|
637
|
+
"""
|
|
638
|
+
|
|
639
|
+
assert self.dataframe.analysis_mode == 'single'
|
|
640
|
+
|
|
641
|
+
N = self.dataframe.N
|
|
642
|
+
T = self.dataframe.T[0]
|
|
643
|
+
|
|
644
|
+
# Get overlapping samples
|
|
645
|
+
used_indices = {}
|
|
646
|
+
overlapping = set(list(range(0, T)))
|
|
647
|
+
for j in self.all_parents:
|
|
648
|
+
if self.fit_results[j] is not None:
|
|
649
|
+
if 'used_indices' not in self.fit_results[j]:
|
|
650
|
+
raise ValueError("Run ")
|
|
651
|
+
used_indices[j] = set(self.fit_results[j]['used_indices'][0])
|
|
652
|
+
overlapping = overlapping.intersection(used_indices[j])
|
|
653
|
+
|
|
654
|
+
overlapping = sorted(list(overlapping))
|
|
655
|
+
|
|
656
|
+
if len(overlapping) <= 10:
|
|
657
|
+
raise ValueError("Less than 10 overlapping samples due to masking and/or missing values,"
|
|
658
|
+
" cannot compute residual covariance!")
|
|
659
|
+
|
|
660
|
+
predicted = self.predict_full_model(new_data=new_data,
|
|
661
|
+
pred_params=pred_params,
|
|
662
|
+
cut_off='max_lag_or_tau_max')
|
|
663
|
+
|
|
664
|
+
# Residuals only exist after tau_max
|
|
665
|
+
residuals = self.dataframe.values[0].copy()
|
|
666
|
+
|
|
667
|
+
for index, j in enumerate([j for j in self.all_parents]): # if len(parents[j]) > 0]):
|
|
668
|
+
residuals[list(used_indices[j]), j] -= predicted[index]
|
|
669
|
+
|
|
670
|
+
overlapping_residuals = residuals[overlapping]
|
|
671
|
+
|
|
672
|
+
len_residuals = len(overlapping_residuals)
|
|
673
|
+
|
|
674
|
+
cov = np.cov(overlapping_residuals, rowvar=0)
|
|
675
|
+
mean = np.mean(overlapping_residuals, axis=0) # residuals should have zero mean due to prediction including constant
|
|
676
|
+
|
|
677
|
+
self.residuals = overlapping_residuals
|
|
678
|
+
|
|
679
|
+
return cov, mean
|
|
680
|
+
|
|
681
|
+
class LinearMediation(Models):
|
|
682
|
+
r"""Linear mediation analysis for time series models.
|
|
683
|
+
|
|
684
|
+
Fits linear model to parents and provides functions to return measures such
|
|
685
|
+
as causal effect, mediated causal effect, average causal effect, etc. as
|
|
686
|
+
described in [4]_. Also allows for contemporaneous links.
|
|
687
|
+
|
|
688
|
+
For general linear and nonlinear causal effect analysis including latent
|
|
689
|
+
variables and further functionality use the CausalEffects class.
|
|
690
|
+
|
|
691
|
+
Notes
|
|
692
|
+
-----
|
|
693
|
+
This class implements the following causal mediation measures introduced in
|
|
694
|
+
[4]_:
|
|
695
|
+
|
|
696
|
+
* causal effect (CE)
|
|
697
|
+
* mediated causal effect (MCE)
|
|
698
|
+
* average causal effect (ACE)
|
|
699
|
+
* average causal susceptibility (ACS)
|
|
700
|
+
* average mediated causal effect (AMCE)
|
|
701
|
+
|
|
702
|
+
Consider a simple model of a causal chain as given in the Example with
|
|
703
|
+
|
|
704
|
+
.. math:: X_t &= \eta^X_t \\
|
|
705
|
+
Y_t &= 0.5 X_{t-1} + \eta^Y_t \\
|
|
706
|
+
Z_t &= 0.5 Y_{t-1} + \eta^Z_t
|
|
707
|
+
|
|
708
|
+
Here the link coefficient of :math:`X_{t-2} \to Z_t` is zero while the
|
|
709
|
+
causal effect is 0.25. MCE through :math:`Y` is 0.25 implying that *all*
|
|
710
|
+
of the the CE is explained by :math:`Y`. ACE from :math:`X` is 0.37 since it
|
|
711
|
+
has CE 0.5 on :math:`Y` and 0.25 on :math:`Z`.
|
|
712
|
+
|
|
713
|
+
Examples
|
|
714
|
+
--------
|
|
715
|
+
>>> links_coeffs = {0: [], 1: [((0, -1), 0.5)], 2: [((1, -1), 0.5)]}
|
|
716
|
+
>>> data, true_parents = toys.var_process(links_coeffs, T=1000, seed=42)
|
|
717
|
+
>>> dataframe = pp.DataFrame(data)
|
|
718
|
+
>>> med = LinearMediation(dataframe=dataframe)
|
|
719
|
+
>>> med.fit_model(all_parents=true_parents, tau_max=3)
|
|
720
|
+
>>> print "Link coefficient (0, -2) --> 2: ", med.get_coeff(
|
|
721
|
+
i=0, tau=-2, j=2)
|
|
722
|
+
>>> print "Causal effect (0, -2) --> 2: ", med.get_ce(i=0, tau=-2, j=2)
|
|
723
|
+
>>> print "Mediated Causal effect (0, -2) --> 2 through 1: ", med.get_mce(
|
|
724
|
+
i=0, tau=-2, j=2, k=1)
|
|
725
|
+
>>> print "Average Causal Effect: ", med.get_all_ace()
|
|
726
|
+
>>> print "Average Causal Susceptibility: ", med.get_all_acs()
|
|
727
|
+
>>> print "Average Mediated Causal Effect: ", med.get_all_amce()
|
|
728
|
+
Link coefficient (0, -2) --> 2: 0.0
|
|
729
|
+
Causal effect (0, -2) --> 2: 0.250648072987
|
|
730
|
+
Mediated Causal effect (0, -2) --> 2 through 1: 0.250648072987
|
|
731
|
+
Average Causal Effect: [ 0.36897445 0.25718002 0. ]
|
|
732
|
+
Average Causal Susceptibility: [ 0. 0.24365041 0.38250406]
|
|
733
|
+
Average Mediated Causal Effect: [ 0. 0.12532404 0. ]
|
|
734
|
+
|
|
735
|
+
References
|
|
736
|
+
----------
|
|
737
|
+
.. [4] J. Runge et al. (2015): Identifying causal gateways and mediators in
|
|
738
|
+
complex spatio-temporal systems.
|
|
739
|
+
Nature Communications, 6, 8502. http://doi.org/10.1038/ncomms9502
|
|
740
|
+
|
|
741
|
+
Parameters
|
|
742
|
+
----------
|
|
743
|
+
dataframe : data object
|
|
744
|
+
Tigramite dataframe object. It must have the attributes dataframe.values
|
|
745
|
+
yielding a numpy array of shape (observations T, variables N) and
|
|
746
|
+
optionally a mask of the same shape and a missing values flag.
|
|
747
|
+
model_params : dictionary, optional (default: None)
|
|
748
|
+
Optional parameters passed on to sklearn model
|
|
749
|
+
data_transform : sklearn preprocessing object, optional (default: StandardScaler)
|
|
750
|
+
Used to transform data prior to fitting. For example,
|
|
751
|
+
sklearn.preprocessing.StandardScaler for simple standardization. The
|
|
752
|
+
fitted parameters are stored.
|
|
753
|
+
mask_type : {None, 'y','x','z','xy','xz','yz','xyz'}
|
|
754
|
+
Masking mode: Indicators for which variables in the dependence
|
|
755
|
+
measure I(X; Y | Z) the samples should be masked. If None, the mask
|
|
756
|
+
is not used. Explained in tutorial on masking and missing values.
|
|
757
|
+
verbosity : int, optional (default: 0)
|
|
758
|
+
Level of verbosity.
|
|
759
|
+
"""
|
|
760
|
+
|
|
761
|
+
def __init__(self,
|
|
762
|
+
dataframe,
|
|
763
|
+
model_params=None,
|
|
764
|
+
data_transform=sklearn.preprocessing.StandardScaler(),
|
|
765
|
+
mask_type=None,
|
|
766
|
+
verbosity=0):
|
|
767
|
+
# Initialize the member variables to None
|
|
768
|
+
self.phi = None
|
|
769
|
+
self.psi = None
|
|
770
|
+
self.all_psi_k = None
|
|
771
|
+
self.dataframe = dataframe
|
|
772
|
+
self.mask_type = mask_type
|
|
773
|
+
self.data_transform = data_transform
|
|
774
|
+
if model_params is None:
|
|
775
|
+
self.model_params = {}
|
|
776
|
+
else:
|
|
777
|
+
self.model_params = model_params
|
|
778
|
+
|
|
779
|
+
self.bootstrap_available = False
|
|
780
|
+
|
|
781
|
+
# Build the model using the parameters
|
|
782
|
+
if model_params is None:
|
|
783
|
+
model_params = {}
|
|
784
|
+
this_model = sklearn.linear_model.LinearRegression(**model_params)
|
|
785
|
+
Models.__init__(self,
|
|
786
|
+
dataframe=dataframe,
|
|
787
|
+
model=this_model,
|
|
788
|
+
data_transform=data_transform,
|
|
789
|
+
mask_type=mask_type,
|
|
790
|
+
verbosity=verbosity)
|
|
791
|
+
|
|
792
|
+
def fit_model(self, all_parents, tau_max=None, return_data=False):
|
|
793
|
+
r"""Fit linear time series model.
|
|
794
|
+
|
|
795
|
+
Fits a sklearn.linear_model.LinearRegression model to the parents of
|
|
796
|
+
each variable and computes the coefficient matrices :math:`\Phi` and
|
|
797
|
+
:math:`\Psi` as described in [4]_. Does accept contemporaneous links.
|
|
798
|
+
|
|
799
|
+
Parameters
|
|
800
|
+
----------
|
|
801
|
+
all_parents : dictionary
|
|
802
|
+
Dictionary of form {0:[(0, -1), (3, 0), ...], 1:[], ...} containing
|
|
803
|
+
the parents estimated with PCMCI.
|
|
804
|
+
tau_max : int, optional (default: None)
|
|
805
|
+
Maximum time lag. If None, the maximum lag in all_parents is used.
|
|
806
|
+
return_data : bool, optional (default: False)
|
|
807
|
+
Whether to save the data array. Needed to get residuals.
|
|
808
|
+
"""
|
|
809
|
+
|
|
810
|
+
# Fit the model using the base class
|
|
811
|
+
self.fit_results = self.fit_full_model(all_parents=all_parents,
|
|
812
|
+
selected_variables=None,
|
|
813
|
+
return_data=return_data,
|
|
814
|
+
tau_max=tau_max)
|
|
815
|
+
# Cache the results in the member variables
|
|
816
|
+
coeffs = self.get_coefs()
|
|
817
|
+
self.phi = self._get_phi(coeffs)
|
|
818
|
+
self.psi = self._get_psi(self.phi)
|
|
819
|
+
self.all_psi_k = self._get_all_psi_k(self.phi)
|
|
820
|
+
|
|
821
|
+
self.all_parents = all_parents
|
|
822
|
+
# self.tau_max = tau_max
|
|
823
|
+
|
|
824
|
+
def fit_model_bootstrap(self,
|
|
825
|
+
boot_blocklength=1,
|
|
826
|
+
seed=None,
|
|
827
|
+
boot_samples=100):
|
|
828
|
+
"""Fits boostrap-versions of Phi, Psi, etc.
|
|
829
|
+
|
|
830
|
+
Random draws are generated
|
|
831
|
+
|
|
832
|
+
Parameters
|
|
833
|
+
----------
|
|
834
|
+
boot_blocklength : int, or in {'cube_root', 'from_autocorrelation'}
|
|
835
|
+
Block length for block-bootstrap. If 'cube_root' it is the cube
|
|
836
|
+
root of the time series length.
|
|
837
|
+
seed : int, optional(default = None)
|
|
838
|
+
Seed for RandomState (default_rng)
|
|
839
|
+
boot_samples : int
|
|
840
|
+
Number of bootstrap samples.
|
|
841
|
+
"""
|
|
842
|
+
|
|
843
|
+
self.phi_boots = np.empty((boot_samples,) + self.phi.shape)
|
|
844
|
+
self.psi_boots = np.empty((boot_samples,) + self.psi.shape)
|
|
845
|
+
self.all_psi_k_boots = np.empty((boot_samples,) + self.all_psi_k.shape)
|
|
846
|
+
|
|
847
|
+
if self.verbosity > 0:
|
|
848
|
+
print("\n##\n## Generating bootstrap samples of Phi, Psi, etc " +
|
|
849
|
+
"\n##\n" +
|
|
850
|
+
"\nboot_samples = %s \n" % boot_samples +
|
|
851
|
+
"\nboot_blocklength = %s \n" % boot_blocklength
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
for b in range(boot_samples):
|
|
856
|
+
# # Replace dataframe in method args by bootstrapped dataframe
|
|
857
|
+
# method_args_bootstrap['dataframe'].bootstrap = boot_draw
|
|
858
|
+
if seed is None:
|
|
859
|
+
random_state = np.random.default_rng(None)
|
|
860
|
+
else:
|
|
861
|
+
random_state = np.random.default_rng(seed+b)
|
|
862
|
+
|
|
863
|
+
dataframe_here = deepcopy(self.dataframe)
|
|
864
|
+
|
|
865
|
+
dataframe_here.bootstrap = {'boot_blocklength':boot_blocklength,
|
|
866
|
+
'random_state':random_state}
|
|
867
|
+
model = Models(dataframe=dataframe_here,
|
|
868
|
+
model=sklearn.linear_model.LinearRegression(**self.model_params),
|
|
869
|
+
data_transform=self.data_transform,
|
|
870
|
+
mask_type=self.mask_type,
|
|
871
|
+
verbosity=0)
|
|
872
|
+
|
|
873
|
+
model.fit_full_model(all_parents=self.all_parents,
|
|
874
|
+
tau_max=self.tau_max)
|
|
875
|
+
# Cache the results in the member variables
|
|
876
|
+
coeffs = model.get_coefs()
|
|
877
|
+
phi = self._get_phi(coeffs)
|
|
878
|
+
self.phi_boots[b] = phi
|
|
879
|
+
self.psi_boots[b] = self._get_psi(phi)
|
|
880
|
+
self.all_psi_k_boots[b] = self._get_all_psi_k(phi)
|
|
881
|
+
|
|
882
|
+
self.bootstrap_available = True
|
|
883
|
+
|
|
884
|
+
return self
|
|
885
|
+
|
|
886
|
+
def get_bootstrap_of(self, function, function_args, conf_lev=0.9):
|
|
887
|
+
"""Applies bootstrap-versions of Phi, Psi, etc. to any function in
|
|
888
|
+
this class.
|
|
889
|
+
|
|
890
|
+
Parameters
|
|
891
|
+
----------
|
|
892
|
+
function : string
|
|
893
|
+
Valid function from LinearMediation class
|
|
894
|
+
function_args : dict
|
|
895
|
+
Optional function arguments.
|
|
896
|
+
conf_lev : float
|
|
897
|
+
Confidence interval.
|
|
898
|
+
|
|
899
|
+
Returns
|
|
900
|
+
-------
|
|
901
|
+
Upper/Lower confidence interval of function.
|
|
902
|
+
"""
|
|
903
|
+
|
|
904
|
+
valid_functions = [
|
|
905
|
+
'get_coeff',
|
|
906
|
+
'get_ce',
|
|
907
|
+
'get_ce_max',
|
|
908
|
+
'get_joint_ce',
|
|
909
|
+
'get_joint_ce_matrix',
|
|
910
|
+
'get_mce',
|
|
911
|
+
'get_conditional_mce',
|
|
912
|
+
'get_joint_mce',
|
|
913
|
+
'get_ace',
|
|
914
|
+
'get_all_ace',
|
|
915
|
+
'get_acs',
|
|
916
|
+
'get_all_acs',
|
|
917
|
+
'get_amce',
|
|
918
|
+
'get_all_amce',
|
|
919
|
+
'get_val_matrix',
|
|
920
|
+
]
|
|
921
|
+
|
|
922
|
+
if function not in valid_functions:
|
|
923
|
+
raise ValueError("function must be in %s" %valid_functions)
|
|
924
|
+
|
|
925
|
+
realizations = self.phi_boots.shape[0]
|
|
926
|
+
|
|
927
|
+
original_phi = deepcopy(self.phi)
|
|
928
|
+
original_psi = deepcopy(self.psi)
|
|
929
|
+
original_all_psi_k = deepcopy(self.all_psi_k)
|
|
930
|
+
|
|
931
|
+
for r in range(realizations):
|
|
932
|
+
self.phi = self.phi_boots[r]
|
|
933
|
+
self.psi = self.psi_boots[r]
|
|
934
|
+
self.all_psi_k = self.all_psi_k_boots[r]
|
|
935
|
+
|
|
936
|
+
boot_effect = getattr(self, function)(**function_args)
|
|
937
|
+
|
|
938
|
+
if r == 0:
|
|
939
|
+
bootstrap_result = np.empty((realizations,) + boot_effect.shape)
|
|
940
|
+
|
|
941
|
+
bootstrap_result[r] = boot_effect
|
|
942
|
+
|
|
943
|
+
# Confidence intervals for val_matrix; interval is two-sided
|
|
944
|
+
c_int = (1. - (1. - conf_lev)/2.)
|
|
945
|
+
confidence_interval = np.percentile(
|
|
946
|
+
bootstrap_result, axis=0,
|
|
947
|
+
q = [100*(1. - c_int), 100*c_int])
|
|
948
|
+
|
|
949
|
+
self.phi = original_phi
|
|
950
|
+
self.psi = original_psi
|
|
951
|
+
self.all_psi_k = original_all_psi_k
|
|
952
|
+
self.bootstrap_result = bootstrap_result
|
|
953
|
+
|
|
954
|
+
return confidence_interval
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
def _check_sanity(self, X, Y, k=None):
|
|
958
|
+
"""Checks validity of some parameters."""
|
|
959
|
+
|
|
960
|
+
if len(X) != 1 or len(Y) != 1:
|
|
961
|
+
raise ValueError("X must be of form [(i, -tau)] and Y = [(j, 0)], "
|
|
962
|
+
"but are X = %s, Y=%s" % (X, Y))
|
|
963
|
+
|
|
964
|
+
i, tau = X[0]
|
|
965
|
+
|
|
966
|
+
if abs(tau) > self.tau_max:
|
|
967
|
+
raise ValueError("X must be of form [(i, -tau)] with"
|
|
968
|
+
" tau <= tau_max")
|
|
969
|
+
|
|
970
|
+
if k is not None and (k < 0 or k >= self.N):
|
|
971
|
+
raise ValueError("k must be in [0, N)")
|
|
972
|
+
|
|
973
|
+
def _get_phi(self, coeffs):
|
|
974
|
+
"""Returns the linear coefficient matrices for different lags.
|
|
975
|
+
|
|
976
|
+
Parameters
|
|
977
|
+
----------
|
|
978
|
+
coeffs : dictionary
|
|
979
|
+
Dictionary of coefficients for each parent.
|
|
980
|
+
|
|
981
|
+
Returns
|
|
982
|
+
-------
|
|
983
|
+
phi : array-like, shape (tau_max + 1, N, N)
|
|
984
|
+
Matrices of coefficients for each time lag.
|
|
985
|
+
"""
|
|
986
|
+
|
|
987
|
+
phi = np.zeros((self.tau_max + 1, self.N, self.N))
|
|
988
|
+
# phi[0] = np.identity(self.N)
|
|
989
|
+
|
|
990
|
+
# Also includes contemporaneous lags
|
|
991
|
+
for j in list(coeffs):
|
|
992
|
+
for par in list(coeffs[j]):
|
|
993
|
+
i, tau = par
|
|
994
|
+
phi[abs(tau), j, i] = coeffs[j][par]
|
|
995
|
+
|
|
996
|
+
return phi
|
|
997
|
+
|
|
998
|
+
def _get_psi(self, phi):
|
|
999
|
+
"""Returns the linear causal effect matrices for different lags incl
|
|
1000
|
+
lag zero.
|
|
1001
|
+
|
|
1002
|
+
Parameters
|
|
1003
|
+
----------
|
|
1004
|
+
phi : array-like
|
|
1005
|
+
Coefficient matrices at different lags.
|
|
1006
|
+
|
|
1007
|
+
Returns
|
|
1008
|
+
-------
|
|
1009
|
+
psi : array-like, shape (tau_max + 1, N, N)
|
|
1010
|
+
Matrices of causal effects for each time lag incl contemporaneous links.
|
|
1011
|
+
"""
|
|
1012
|
+
|
|
1013
|
+
psi = np.zeros((self.tau_max + 1, self.N, self.N))
|
|
1014
|
+
|
|
1015
|
+
psi[0] = np.linalg.pinv(np.identity(self.N) - phi[0])
|
|
1016
|
+
for tau in range(1, self.tau_max + 1):
|
|
1017
|
+
for s in range(1, tau + 1):
|
|
1018
|
+
psi[tau] += np.matmul(psi[0], np.matmul(phi[s], psi[tau - s]) )
|
|
1019
|
+
|
|
1020
|
+
# Lagged-only effects:
|
|
1021
|
+
# psi = np.zeros((self.tau_max + 1, self.N, self.N))
|
|
1022
|
+
|
|
1023
|
+
# psi[0] = np.identity(self.N)
|
|
1024
|
+
# for n in range(1, self.tau_max + 1):
|
|
1025
|
+
# psi[n] = np.zeros((self.N, self.N))
|
|
1026
|
+
# for s in range(1, n + 1):
|
|
1027
|
+
# psi[n] += np.dot(phi[s], psi[n - s])
|
|
1028
|
+
|
|
1029
|
+
return psi
|
|
1030
|
+
|
|
1031
|
+
def _get_psi_k(self, phi, k):
|
|
1032
|
+
"""Returns the linear causal effect matrices excluding variable k.
|
|
1033
|
+
|
|
1034
|
+
Essentially, this blocks all path through parents of variable k
|
|
1035
|
+
at any lag.
|
|
1036
|
+
|
|
1037
|
+
Parameters
|
|
1038
|
+
----------
|
|
1039
|
+
phi : array-like
|
|
1040
|
+
Coefficient matrices at different lags.
|
|
1041
|
+
k : int or list of ints
|
|
1042
|
+
Variable indices to exclude causal effects through.
|
|
1043
|
+
|
|
1044
|
+
Returns
|
|
1045
|
+
-------
|
|
1046
|
+
psi_k : array-like, shape (tau_max + 1, N, N)
|
|
1047
|
+
Matrices of causal effects excluding k.
|
|
1048
|
+
"""
|
|
1049
|
+
|
|
1050
|
+
psi_k = np.zeros((self.tau_max + 1, self.N, self.N))
|
|
1051
|
+
|
|
1052
|
+
phi_k = np.copy(phi)
|
|
1053
|
+
if isinstance(k, int):
|
|
1054
|
+
phi_k[:, k, :] = 0.
|
|
1055
|
+
else:
|
|
1056
|
+
for k_here in k:
|
|
1057
|
+
phi_k[:, k_here, :] = 0.
|
|
1058
|
+
|
|
1059
|
+
|
|
1060
|
+
psi_k[0] = np.linalg.pinv(np.identity(self.N) - phi_k[0])
|
|
1061
|
+
for tau in range(1, self.tau_max + 1):
|
|
1062
|
+
# psi_k[tau] = np.matmul(psi_k[0], np.matmul(phi_k[tau], psi_k[0]))
|
|
1063
|
+
for s in range(1, tau + 1):
|
|
1064
|
+
psi_k[tau] += np.matmul(psi_k[0], np.matmul(phi_k[s], psi_k[tau - s]))
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
# psi_k[0] = np.identity(self.N)
|
|
1068
|
+
# phi_k = np.copy(phi)
|
|
1069
|
+
# phi_k[:, k, :] = 0.
|
|
1070
|
+
# for n in range(1, self.tau_max + 1):
|
|
1071
|
+
# psi_k[n] = np.zeros((self.N, self.N))
|
|
1072
|
+
# for s in range(1, n + 1):
|
|
1073
|
+
# psi_k[n] += np.dot(phi_k[s], psi_k[n - s])
|
|
1074
|
+
|
|
1075
|
+
return psi_k
|
|
1076
|
+
|
|
1077
|
+
def _get_all_psi_k(self, phi):
|
|
1078
|
+
"""Returns the linear causal effect matrices excluding variables.
|
|
1079
|
+
|
|
1080
|
+
Parameters
|
|
1081
|
+
----------
|
|
1082
|
+
phi : array-like
|
|
1083
|
+
Coefficient matrices at different lags.
|
|
1084
|
+
|
|
1085
|
+
Returns
|
|
1086
|
+
-------
|
|
1087
|
+
all_psi_k : array-like, shape (N, tau_max + 1, N, N)
|
|
1088
|
+
Matrices of causal effects where for each row another variable is
|
|
1089
|
+
excluded.
|
|
1090
|
+
"""
|
|
1091
|
+
|
|
1092
|
+
all_psi_k = np.zeros((self.N, self.tau_max + 1, self.N, self.N))
|
|
1093
|
+
|
|
1094
|
+
for k in range(self.N):
|
|
1095
|
+
all_psi_k[k] = self._get_psi_k(phi, k)
|
|
1096
|
+
|
|
1097
|
+
return all_psi_k
|
|
1098
|
+
|
|
1099
|
+
def get_coeff(self, i, tau, j):
|
|
1100
|
+
"""Returns link coefficient.
|
|
1101
|
+
|
|
1102
|
+
This is the direct causal effect for a particular link (i, -tau) --> j.
|
|
1103
|
+
|
|
1104
|
+
Parameters
|
|
1105
|
+
----------
|
|
1106
|
+
i : int
|
|
1107
|
+
Index of cause variable.
|
|
1108
|
+
tau : int
|
|
1109
|
+
Lag of cause variable (incl lag zero).
|
|
1110
|
+
j : int
|
|
1111
|
+
Index of effect variable.
|
|
1112
|
+
|
|
1113
|
+
Returns
|
|
1114
|
+
-------
|
|
1115
|
+
coeff : float
|
|
1116
|
+
"""
|
|
1117
|
+
return self.phi[abs(tau), j, i]
|
|
1118
|
+
|
|
1119
|
+
def get_ce(self, i, tau, j):
|
|
1120
|
+
"""Returns the causal effect.
|
|
1121
|
+
|
|
1122
|
+
This is the causal effect for (i, -tau) -- --> j.
|
|
1123
|
+
|
|
1124
|
+
Parameters
|
|
1125
|
+
----------
|
|
1126
|
+
i : int
|
|
1127
|
+
Index of cause variable.
|
|
1128
|
+
tau : int
|
|
1129
|
+
Lag of cause variable (incl lag zero).
|
|
1130
|
+
j : int
|
|
1131
|
+
Index of effect variable.
|
|
1132
|
+
|
|
1133
|
+
Returns
|
|
1134
|
+
-------
|
|
1135
|
+
ce : float
|
|
1136
|
+
"""
|
|
1137
|
+
return self.psi[abs(tau), j, i]
|
|
1138
|
+
|
|
1139
|
+
def get_ce_max(self, i, j):
|
|
1140
|
+
"""Returns the causal effect.
|
|
1141
|
+
|
|
1142
|
+
This is the maximum absolute causal effect for i --> j across all
|
|
1143
|
+
lags (incl lag zero).
|
|
1144
|
+
|
|
1145
|
+
Parameters
|
|
1146
|
+
----------
|
|
1147
|
+
i : int
|
|
1148
|
+
Index of cause variable.
|
|
1149
|
+
j : int
|
|
1150
|
+
Index of effect variable.
|
|
1151
|
+
|
|
1152
|
+
Returns
|
|
1153
|
+
-------
|
|
1154
|
+
ce : float
|
|
1155
|
+
"""
|
|
1156
|
+
argmax = np.abs(self.psi[:, j, i]).argmax()
|
|
1157
|
+
return self.psi[:, j, i][argmax]
|
|
1158
|
+
|
|
1159
|
+
def get_joint_ce(self, i, j):
|
|
1160
|
+
"""Returns the joint causal effect.
|
|
1161
|
+
|
|
1162
|
+
This is the causal effect from all lags [t, ..., t-tau_max]
|
|
1163
|
+
of i on j at time t. Note that the joint effect does not
|
|
1164
|
+
count links passing through parents of i itself.
|
|
1165
|
+
|
|
1166
|
+
Parameters
|
|
1167
|
+
----------
|
|
1168
|
+
i : int
|
|
1169
|
+
Index of cause variable.
|
|
1170
|
+
j : int
|
|
1171
|
+
Index of effect variable.
|
|
1172
|
+
|
|
1173
|
+
Returns
|
|
1174
|
+
-------
|
|
1175
|
+
joint_ce : array of shape (tau_max + 1)
|
|
1176
|
+
Causal effect from each lag [t, ..., t-tau_max] of i on j.
|
|
1177
|
+
"""
|
|
1178
|
+
joint_ce = self.all_psi_k[i, :, j, i]
|
|
1179
|
+
return joint_ce
|
|
1180
|
+
|
|
1181
|
+
def get_joint_ce_matrix(self, i, j):
|
|
1182
|
+
"""Returns the joint causal effect matrix of i on j.
|
|
1183
|
+
|
|
1184
|
+
This is the causal effect from all lags [t, ..., t-tau_max]
|
|
1185
|
+
of i on j at times [t, ..., t-tau_max]. Note that the joint effect does not
|
|
1186
|
+
count links passing through parents of i itself.
|
|
1187
|
+
|
|
1188
|
+
An entry (taui, tauj) stands for the effect of i at t-taui on j at t-tauj.
|
|
1189
|
+
|
|
1190
|
+
Parameters
|
|
1191
|
+
----------
|
|
1192
|
+
i : int
|
|
1193
|
+
Index of cause variable.
|
|
1194
|
+
j : int
|
|
1195
|
+
Index of effect variable.
|
|
1196
|
+
|
|
1197
|
+
Returns
|
|
1198
|
+
-------
|
|
1199
|
+
joint_ce_matrix : 2d array of shape (tau_max + 1, tau_max + 1)
|
|
1200
|
+
Causal effect matrix from each lag of i on each lag of j.
|
|
1201
|
+
"""
|
|
1202
|
+
joint_ce_matrix = np.zeros((self.tau_max + 1, self.tau_max + 1))
|
|
1203
|
+
for tauj in range(self.tau_max + 1):
|
|
1204
|
+
joint_ce_matrix[tauj:, tauj] = self.all_psi_k[i, tauj:, j, i][::-1]
|
|
1205
|
+
|
|
1206
|
+
return joint_ce_matrix
|
|
1207
|
+
|
|
1208
|
+
def get_mce(self, i, tau, j, k):
|
|
1209
|
+
"""Returns the mediated causal effect.
|
|
1210
|
+
|
|
1211
|
+
This is the causal effect for i --> j minus the causal effect not going
|
|
1212
|
+
through k.
|
|
1213
|
+
|
|
1214
|
+
Parameters
|
|
1215
|
+
----------
|
|
1216
|
+
i : int
|
|
1217
|
+
Index of cause variable.
|
|
1218
|
+
tau : int
|
|
1219
|
+
Lag of cause variable.
|
|
1220
|
+
j : int
|
|
1221
|
+
Index of effect variable.
|
|
1222
|
+
k : int or list of ints
|
|
1223
|
+
Indices of mediator variables.
|
|
1224
|
+
|
|
1225
|
+
Returns
|
|
1226
|
+
-------
|
|
1227
|
+
mce : float
|
|
1228
|
+
"""
|
|
1229
|
+
if isinstance(k, int):
|
|
1230
|
+
effect_without_k = self.all_psi_k[k, abs(tau), j, i]
|
|
1231
|
+
else:
|
|
1232
|
+
effect_without_k = self._get_psi_k(self.phi, k=k)[abs(tau), j, i]
|
|
1233
|
+
|
|
1234
|
+
mce = self.psi[abs(tau), j, i] - effect_without_k
|
|
1235
|
+
return mce
|
|
1236
|
+
|
|
1237
|
+
def get_conditional_mce(self, i, tau, j, k, notk):
|
|
1238
|
+
"""Returns the conditional mediated causal effect.
|
|
1239
|
+
|
|
1240
|
+
This is the causal effect for i --> j for all paths going through k, but not through notk.
|
|
1241
|
+
|
|
1242
|
+
Parameters
|
|
1243
|
+
----------
|
|
1244
|
+
i : int
|
|
1245
|
+
Index of cause variable.
|
|
1246
|
+
tau : int
|
|
1247
|
+
Lag of cause variable.
|
|
1248
|
+
j : int
|
|
1249
|
+
Index of effect variable.
|
|
1250
|
+
k : int or list of ints
|
|
1251
|
+
Indices of mediator variables.
|
|
1252
|
+
notk : int or list of ints
|
|
1253
|
+
Indices of mediator variables to exclude.
|
|
1254
|
+
|
|
1255
|
+
Returns
|
|
1256
|
+
-------
|
|
1257
|
+
mce : float
|
|
1258
|
+
"""
|
|
1259
|
+
if isinstance(k, int):
|
|
1260
|
+
k = set([k])
|
|
1261
|
+
else:
|
|
1262
|
+
k = set(k)
|
|
1263
|
+
if isinstance(notk, int):
|
|
1264
|
+
notk = set([notk])
|
|
1265
|
+
else:
|
|
1266
|
+
notk = set(notk)
|
|
1267
|
+
|
|
1268
|
+
bothk = list(k.union(notk))
|
|
1269
|
+
notk = list(notk)
|
|
1270
|
+
|
|
1271
|
+
effect_without_bothk = self._get_psi_k(self.phi, k=bothk)[abs(tau), j, i]
|
|
1272
|
+
effect_without_notk = self._get_psi_k(self.phi, k=notk)[abs(tau), j, i]
|
|
1273
|
+
|
|
1274
|
+
# mce = self.psi[abs(tau), j, i] - effect_without_k
|
|
1275
|
+
mce = effect_without_notk - effect_without_bothk
|
|
1276
|
+
|
|
1277
|
+
return mce
|
|
1278
|
+
|
|
1279
|
+
|
|
1280
|
+
def get_joint_mce(self, i, j, k):
|
|
1281
|
+
"""Returns the joint causal effect mediated through k.
|
|
1282
|
+
|
|
1283
|
+
This is the mediated causal effect from all lags [t, ..., t-tau_max]
|
|
1284
|
+
of i on j at time t for paths through k. Note that the joint effect
|
|
1285
|
+
does not count links passing through parents of i itself.
|
|
1286
|
+
|
|
1287
|
+
Parameters
|
|
1288
|
+
----------
|
|
1289
|
+
i : int
|
|
1290
|
+
Index of cause variable.
|
|
1291
|
+
j : int
|
|
1292
|
+
Index of effect variable.
|
|
1293
|
+
k : int or list of ints
|
|
1294
|
+
Indices of mediator variables.
|
|
1295
|
+
|
|
1296
|
+
Returns
|
|
1297
|
+
-------
|
|
1298
|
+
joint_mce : array of shape (tau_max + 1)
|
|
1299
|
+
Mediated causal effect from each lag [t, ..., t-tau_max] of i on j through k.
|
|
1300
|
+
"""
|
|
1301
|
+
if isinstance(k, int):
|
|
1302
|
+
k_here = [k]
|
|
1303
|
+
|
|
1304
|
+
effect_without_k = self._get_psi_k(self.phi, k=[i] + k_here)
|
|
1305
|
+
|
|
1306
|
+
joint_mce = self.all_psi_k[i, :, j, i] - effect_without_k[:, j, i]
|
|
1307
|
+
return joint_mce
|
|
1308
|
+
|
|
1309
|
+
def get_ace(self, i, lag_mode='absmax', exclude_i=True):
|
|
1310
|
+
"""Returns the average causal effect.
|
|
1311
|
+
|
|
1312
|
+
This is the average causal effect (ACE) emanating from variable i to any
|
|
1313
|
+
other variable. With lag_mode='absmax' this is based on the lag of
|
|
1314
|
+
maximum CE for each pair.
|
|
1315
|
+
|
|
1316
|
+
Parameters
|
|
1317
|
+
----------
|
|
1318
|
+
i : int
|
|
1319
|
+
Index of cause variable.
|
|
1320
|
+
lag_mode : {'absmax', 'all_lags'}
|
|
1321
|
+
Lag mode. Either average across all lags between each pair or only
|
|
1322
|
+
at the lag of maximum absolute causal effect.
|
|
1323
|
+
exclude_i : bool, optional (default: True)
|
|
1324
|
+
Whether to exclude causal effects on the variable itself at later
|
|
1325
|
+
lags.
|
|
1326
|
+
|
|
1327
|
+
Returns
|
|
1328
|
+
-------
|
|
1329
|
+
ace :float
|
|
1330
|
+
Average Causal Effect.
|
|
1331
|
+
"""
|
|
1332
|
+
|
|
1333
|
+
all_but_i = np.ones(self.N, dtype='bool')
|
|
1334
|
+
if exclude_i:
|
|
1335
|
+
all_but_i[i] = False
|
|
1336
|
+
|
|
1337
|
+
if lag_mode == 'absmax':
|
|
1338
|
+
return np.abs(self.psi[:, all_but_i, i]).max(axis=0).mean()
|
|
1339
|
+
elif lag_mode == 'all_lags':
|
|
1340
|
+
return np.abs(self.psi[:, all_but_i, i]).mean()
|
|
1341
|
+
else:
|
|
1342
|
+
raise ValueError("lag_mode = %s not implemented" % lag_mode)
|
|
1343
|
+
|
|
1344
|
+
def get_all_ace(self, lag_mode='absmax', exclude_i=True):
|
|
1345
|
+
"""Returns the average causal effect for all variables.
|
|
1346
|
+
|
|
1347
|
+
This is the average causal effect (ACE) emanating from variable i to any
|
|
1348
|
+
other variable. With lag_mode='absmax' this is based on the lag of
|
|
1349
|
+
maximum CE for each pair.
|
|
1350
|
+
|
|
1351
|
+
Parameters
|
|
1352
|
+
----------
|
|
1353
|
+
lag_mode : {'absmax', 'all_lags'}
|
|
1354
|
+
Lag mode. Either average across all lags between each pair or only
|
|
1355
|
+
at the lag of maximum absolute causal effect.
|
|
1356
|
+
exclude_i : bool, optional (default: True)
|
|
1357
|
+
Whether to exclude causal effects on the variable itself at later
|
|
1358
|
+
lags.
|
|
1359
|
+
|
|
1360
|
+
Returns
|
|
1361
|
+
-------
|
|
1362
|
+
ace : array of shape (N,)
|
|
1363
|
+
Average Causal Effect for each variable.
|
|
1364
|
+
"""
|
|
1365
|
+
|
|
1366
|
+
ace = np.zeros(self.N)
|
|
1367
|
+
for i in range(self.N):
|
|
1368
|
+
ace[i] = self.get_ace(i, lag_mode=lag_mode, exclude_i=exclude_i)
|
|
1369
|
+
|
|
1370
|
+
return ace
|
|
1371
|
+
|
|
1372
|
+
def get_acs(self, j, lag_mode='absmax', exclude_j=True):
|
|
1373
|
+
"""Returns the average causal susceptibility.
|
|
1374
|
+
|
|
1375
|
+
This is the Average Causal Susceptibility (ACS) affecting a variable j
|
|
1376
|
+
from any other variable. With lag_mode='absmax' this is based on the lag
|
|
1377
|
+
of maximum CE for each pair.
|
|
1378
|
+
|
|
1379
|
+
Parameters
|
|
1380
|
+
----------
|
|
1381
|
+
j : int
|
|
1382
|
+
Index of variable.
|
|
1383
|
+
lag_mode : {'absmax', 'all_lags'}
|
|
1384
|
+
Lag mode. Either average across all lags between each pair or only
|
|
1385
|
+
at the lag of maximum absolute causal effect.
|
|
1386
|
+
exclude_j : bool, optional (default: True)
|
|
1387
|
+
Whether to exclude causal effects on the variable itself at previous
|
|
1388
|
+
lags.
|
|
1389
|
+
|
|
1390
|
+
Returns
|
|
1391
|
+
-------
|
|
1392
|
+
acs : float
|
|
1393
|
+
Average Causal Susceptibility.
|
|
1394
|
+
"""
|
|
1395
|
+
|
|
1396
|
+
all_but_j = np.ones(self.N, dtype='bool')
|
|
1397
|
+
if exclude_j:
|
|
1398
|
+
all_but_j[j] = False
|
|
1399
|
+
|
|
1400
|
+
if lag_mode == 'absmax':
|
|
1401
|
+
return np.abs(self.psi[:, j, all_but_j]).max(axis=0).mean()
|
|
1402
|
+
elif lag_mode == 'all_lags':
|
|
1403
|
+
return np.abs(self.psi[:, j, all_but_j]).mean()
|
|
1404
|
+
else:
|
|
1405
|
+
raise ValueError("lag_mode = %s not implemented" % lag_mode)
|
|
1406
|
+
|
|
1407
|
+
def get_all_acs(self, lag_mode='absmax', exclude_j=True):
|
|
1408
|
+
"""Returns the average causal susceptibility.
|
|
1409
|
+
|
|
1410
|
+
This is the Average Causal Susceptibility (ACS) for each variable from
|
|
1411
|
+
any other variable. With lag_mode='absmax' this is based on the lag of
|
|
1412
|
+
maximum CE for each pair.
|
|
1413
|
+
|
|
1414
|
+
Parameters
|
|
1415
|
+
----------
|
|
1416
|
+
lag_mode : {'absmax', 'all_lags'}
|
|
1417
|
+
Lag mode. Either average across all lags between each pair or only
|
|
1418
|
+
at the lag of maximum absolute causal effect.
|
|
1419
|
+
exclude_j : bool, optional (default: True)
|
|
1420
|
+
Whether to exclude causal effects on the variable itself at previous
|
|
1421
|
+
lags.
|
|
1422
|
+
|
|
1423
|
+
Returns
|
|
1424
|
+
-------
|
|
1425
|
+
acs : array of shape (N,)
|
|
1426
|
+
Average Causal Susceptibility.
|
|
1427
|
+
"""
|
|
1428
|
+
|
|
1429
|
+
acs = np.zeros(self.N)
|
|
1430
|
+
for j in range(self.N):
|
|
1431
|
+
acs[j] = self.get_acs(j, lag_mode=lag_mode, exclude_j=exclude_j)
|
|
1432
|
+
|
|
1433
|
+
return acs
|
|
1434
|
+
|
|
1435
|
+
def get_amce(self, k, lag_mode='absmax',
|
|
1436
|
+
exclude_k=True, exclude_self_effects=True):
|
|
1437
|
+
"""Returns the average mediated causal effect.
|
|
1438
|
+
|
|
1439
|
+
This is the Average Mediated Causal Effect (AMCE) through a variable k
|
|
1440
|
+
With lag_mode='absmax' this is based on the lag of maximum CE for each
|
|
1441
|
+
pair.
|
|
1442
|
+
|
|
1443
|
+
Parameters
|
|
1444
|
+
----------
|
|
1445
|
+
k : int
|
|
1446
|
+
Index of variable.
|
|
1447
|
+
lag_mode : {'absmax', 'all_lags'}
|
|
1448
|
+
Lag mode. Either average across all lags between each pair or only
|
|
1449
|
+
at the lag of maximum absolute causal effect.
|
|
1450
|
+
exclude_k : bool, optional (default: True)
|
|
1451
|
+
Whether to exclude causal effects through the variable itself at
|
|
1452
|
+
previous lags.
|
|
1453
|
+
exclude_self_effects : bool, optional (default: True)
|
|
1454
|
+
Whether to exclude causal self effects of variables on themselves.
|
|
1455
|
+
|
|
1456
|
+
Returns
|
|
1457
|
+
-------
|
|
1458
|
+
amce : float
|
|
1459
|
+
Average Mediated Causal Effect.
|
|
1460
|
+
"""
|
|
1461
|
+
|
|
1462
|
+
all_but_k = np.ones(self.N, dtype='bool')
|
|
1463
|
+
if exclude_k:
|
|
1464
|
+
all_but_k[k] = False
|
|
1465
|
+
N_new = self.N - 1
|
|
1466
|
+
else:
|
|
1467
|
+
N_new = self.N
|
|
1468
|
+
|
|
1469
|
+
if exclude_self_effects:
|
|
1470
|
+
weights = np.identity(N_new) == False
|
|
1471
|
+
else:
|
|
1472
|
+
weights = np.ones((N_new, N_new), dtype='bool')
|
|
1473
|
+
|
|
1474
|
+
# if self.tau_max < 2:
|
|
1475
|
+
# raise ValueError("Mediation only nonzero for tau_max >= 2")
|
|
1476
|
+
|
|
1477
|
+
all_mce = self.psi[:, :, :] - self.all_psi_k[k, :, :, :]
|
|
1478
|
+
# all_mce[:, range(self.N), range(self.N)] = 0.
|
|
1479
|
+
|
|
1480
|
+
if lag_mode == 'absmax':
|
|
1481
|
+
return np.average(np.abs(all_mce[:, all_but_k, :]
|
|
1482
|
+
[:, :, all_but_k]
|
|
1483
|
+
).max(axis=0), weights=weights)
|
|
1484
|
+
elif lag_mode == 'all_lags':
|
|
1485
|
+
return np.abs(all_mce[:, all_but_k, :][:, :, all_but_k]).mean()
|
|
1486
|
+
else:
|
|
1487
|
+
raise ValueError("lag_mode = %s not implemented" % lag_mode)
|
|
1488
|
+
|
|
1489
|
+
def get_all_amce(self, lag_mode='absmax',
|
|
1490
|
+
exclude_k=True, exclude_self_effects=True):
|
|
1491
|
+
"""Returns the average mediated causal effect.
|
|
1492
|
+
|
|
1493
|
+
This is the Average Mediated Causal Effect (AMCE) through all variables
|
|
1494
|
+
With lag_mode='absmax' this is based on the lag of maximum CE for each
|
|
1495
|
+
pair.
|
|
1496
|
+
|
|
1497
|
+
Parameters
|
|
1498
|
+
----------
|
|
1499
|
+
lag_mode : {'absmax', 'all_lags'}
|
|
1500
|
+
Lag mode. Either average across all lags between each pair or only
|
|
1501
|
+
at the lag of maximum absolute causal effect.
|
|
1502
|
+
exclude_k : bool, optional (default: True)
|
|
1503
|
+
Whether to exclude causal effects through the variable itself at
|
|
1504
|
+
previous lags.
|
|
1505
|
+
exclude_self_effects : bool, optional (default: True)
|
|
1506
|
+
Whether to exclude causal self effects of variables on themselves.
|
|
1507
|
+
|
|
1508
|
+
Returns
|
|
1509
|
+
-------
|
|
1510
|
+
amce : array of shape (N,)
|
|
1511
|
+
Average Mediated Causal Effect.
|
|
1512
|
+
"""
|
|
1513
|
+
amce = np.zeros(self.N)
|
|
1514
|
+
for k in range(self.N):
|
|
1515
|
+
amce[k] = self.get_amce(k,
|
|
1516
|
+
lag_mode=lag_mode,
|
|
1517
|
+
exclude_k=exclude_k,
|
|
1518
|
+
exclude_self_effects=exclude_self_effects)
|
|
1519
|
+
|
|
1520
|
+
return amce
|
|
1521
|
+
|
|
1522
|
+
|
|
1523
|
+
def get_val_matrix(self, symmetrize=False):
|
|
1524
|
+
"""Returns the matrix of linear coefficients.
|
|
1525
|
+
|
|
1526
|
+
Requires fit_model() before. An entry val_matrix[i,j,tau] gives the
|
|
1527
|
+
coefficient of the link from i to j at lag tau. Lag=0 is always set
|
|
1528
|
+
to zero for LinearMediation, use Models class for contemporaneous
|
|
1529
|
+
models.
|
|
1530
|
+
|
|
1531
|
+
Parameters
|
|
1532
|
+
----------
|
|
1533
|
+
symmetrize : bool
|
|
1534
|
+
If True, the lag-zero entries will be symmetrized such that
|
|
1535
|
+
no zeros appear. Useful since other parts of tigramite
|
|
1536
|
+
through an error for non-symmetric val_matrix, eg plotting.
|
|
1537
|
+
|
|
1538
|
+
Returns
|
|
1539
|
+
-------
|
|
1540
|
+
val_matrix : array
|
|
1541
|
+
Matrix of linear coefficients, shape (N, N, tau_max + 1).
|
|
1542
|
+
"""
|
|
1543
|
+
val_matrix = np.copy(self.phi.transpose())
|
|
1544
|
+
N = val_matrix.shape[0]
|
|
1545
|
+
|
|
1546
|
+
if symmetrize:
|
|
1547
|
+
# Symmetrize since otherwise other parts of tigramite through an error
|
|
1548
|
+
for i in range(N):
|
|
1549
|
+
for j in range(N):
|
|
1550
|
+
if val_matrix[i,j, 0] == 0.:
|
|
1551
|
+
val_matrix[i,j, 0] = val_matrix[j,i, 0]
|
|
1552
|
+
|
|
1553
|
+
return val_matrix
|
|
1554
|
+
|
|
1555
|
+
def net_to_tsg(self, row, lag, max_lag):
|
|
1556
|
+
"""Helper function to translate from network to time series graph."""
|
|
1557
|
+
return row * max_lag + lag
|
|
1558
|
+
|
|
1559
|
+
def tsg_to_net(self, node, max_lag):
|
|
1560
|
+
"""Helper function to translate from time series graph to network."""
|
|
1561
|
+
row = node // max_lag
|
|
1562
|
+
lag = node % max_lag
|
|
1563
|
+
return (row, -lag)
|
|
1564
|
+
|
|
1565
|
+
def get_tsg(self, link_matrix, val_matrix=None, include_neighbors=False):
|
|
1566
|
+
"""Returns time series graph matrix.
|
|
1567
|
+
|
|
1568
|
+
Constructs a matrix of shape (N*tau_max, N*tau_max) from link_matrix.
|
|
1569
|
+
This matrix can be used for plotting the time series graph and analyzing
|
|
1570
|
+
causal pathways.
|
|
1571
|
+
|
|
1572
|
+
Parameters
|
|
1573
|
+
----------
|
|
1574
|
+
link_matrix : bool array-like, optional (default: None)
|
|
1575
|
+
Matrix of significant links. Must be of same shape as val_matrix.
|
|
1576
|
+
Either sig_thres or link_matrix has to be provided.
|
|
1577
|
+
val_matrix : array_like
|
|
1578
|
+
Matrix of shape (N, N, tau_max+1) containing test statistic values.
|
|
1579
|
+
include_neighbors : bool, optional (default: False)
|
|
1580
|
+
Whether to include causal paths emanating from neighbors of i
|
|
1581
|
+
|
|
1582
|
+
Returns
|
|
1583
|
+
-------
|
|
1584
|
+
tsg : array of shape (N*tau_max, N*tau_max)
|
|
1585
|
+
Time series graph matrix.
|
|
1586
|
+
"""
|
|
1587
|
+
|
|
1588
|
+
N = len(link_matrix)
|
|
1589
|
+
max_lag = link_matrix.shape[2] + 1
|
|
1590
|
+
|
|
1591
|
+
# Create TSG
|
|
1592
|
+
tsg = np.zeros((N * max_lag, N * max_lag))
|
|
1593
|
+
for i, j, tau in np.column_stack(np.where(link_matrix)):
|
|
1594
|
+
# if tau > 0 or include_neighbors:
|
|
1595
|
+
for t in range(max_lag):
|
|
1596
|
+
link_start = self.net_to_tsg(i, t - tau, max_lag)
|
|
1597
|
+
link_end = self.net_to_tsg(j, t, max_lag)
|
|
1598
|
+
if (0 <= link_start and
|
|
1599
|
+
(link_start % max_lag) <= (link_end % max_lag)):
|
|
1600
|
+
if val_matrix is not None:
|
|
1601
|
+
tsg[link_start, link_end] = val_matrix[i, j, tau]
|
|
1602
|
+
else:
|
|
1603
|
+
tsg[link_start, link_end] = 1
|
|
1604
|
+
return tsg
|
|
1605
|
+
|
|
1606
|
+
def get_mediation_graph_data(self, i, tau, j, include_neighbors=False):
|
|
1607
|
+
r"""Returns link and node weights for mediation analysis.
|
|
1608
|
+
|
|
1609
|
+
Returns array with non-zero entries for links that are on causal
|
|
1610
|
+
paths between :math:`i` and :math:`j` at lag :math:`\tau`.
|
|
1611
|
+
``path_val_matrix`` contains the corresponding path coefficients and
|
|
1612
|
+
``path_node_array`` the MCE values. ``tsg_path_val_matrix`` contains the
|
|
1613
|
+
corresponding values in the time series graph format.
|
|
1614
|
+
|
|
1615
|
+
Parameters
|
|
1616
|
+
----------
|
|
1617
|
+
i : int
|
|
1618
|
+
Index of cause variable.
|
|
1619
|
+
tau : int
|
|
1620
|
+
Lag of cause variable.
|
|
1621
|
+
j : int
|
|
1622
|
+
Index of effect variable.
|
|
1623
|
+
include_neighbors : bool, optional (default: False)
|
|
1624
|
+
Whether to include causal paths emanating from neighbors of i
|
|
1625
|
+
|
|
1626
|
+
Returns
|
|
1627
|
+
-------
|
|
1628
|
+
graph_data : dictionary
|
|
1629
|
+
Dictionary of matrices for coloring mediation graph plots.
|
|
1630
|
+
"""
|
|
1631
|
+
|
|
1632
|
+
path_link_matrix = np.zeros((self.N, self.N, self.tau_max + 1))
|
|
1633
|
+
path_val_matrix = np.zeros((self.N, self.N, self.tau_max + 1))
|
|
1634
|
+
|
|
1635
|
+
# Get mediation of path variables
|
|
1636
|
+
path_node_array = (self.psi.reshape(1, self.tau_max + 1, self.N, self.N)
|
|
1637
|
+
- self.all_psi_k)[:, abs(tau), j, i]
|
|
1638
|
+
|
|
1639
|
+
# Get involved links
|
|
1640
|
+
val_matrix = self.phi.transpose()
|
|
1641
|
+
link_matrix = val_matrix != 0.
|
|
1642
|
+
|
|
1643
|
+
max_lag = link_matrix.shape[2] + 1
|
|
1644
|
+
|
|
1645
|
+
# include_neighbors = False because True would allow
|
|
1646
|
+
# --> o -- motifs in networkx.all_simple_paths as paths, but
|
|
1647
|
+
# these are blocked...
|
|
1648
|
+
tsg = self.get_tsg(link_matrix, val_matrix=val_matrix,
|
|
1649
|
+
include_neighbors=False)
|
|
1650
|
+
|
|
1651
|
+
if include_neighbors:
|
|
1652
|
+
# Add contemporaneous links only at source node
|
|
1653
|
+
for m, n in zip(*np.where(link_matrix[:, :, 0])):
|
|
1654
|
+
# print m,n
|
|
1655
|
+
if m != n:
|
|
1656
|
+
tsg[self.net_to_tsg(m, max_lag - tau - 1, max_lag),
|
|
1657
|
+
self.net_to_tsg(n, max_lag - tau - 1, max_lag)
|
|
1658
|
+
] = val_matrix[m, n, 0]
|
|
1659
|
+
|
|
1660
|
+
tsg_path_val_matrix = np.zeros(tsg.shape)
|
|
1661
|
+
|
|
1662
|
+
graph = networkx.DiGraph(tsg)
|
|
1663
|
+
pathways = []
|
|
1664
|
+
|
|
1665
|
+
for path in networkx.all_simple_paths(graph,
|
|
1666
|
+
source=self.net_to_tsg(i,
|
|
1667
|
+
max_lag - tau - 1,
|
|
1668
|
+
max_lag),
|
|
1669
|
+
target=self.net_to_tsg(j,
|
|
1670
|
+
max_lag - 0 - 1,
|
|
1671
|
+
max_lag)):
|
|
1672
|
+
pathways.append([self.tsg_to_net(p, max_lag) for p in path])
|
|
1673
|
+
for ip, p in enumerate(path[1:]):
|
|
1674
|
+
tsg_path_val_matrix[path[ip], p] = tsg[path[ip], p]
|
|
1675
|
+
|
|
1676
|
+
k, tau_k = self.tsg_to_net(p, max_lag)
|
|
1677
|
+
link_start = self.tsg_to_net(path[ip], max_lag)
|
|
1678
|
+
link_end = self.tsg_to_net(p, max_lag)
|
|
1679
|
+
delta_tau = abs(link_end[1] - link_start[1])
|
|
1680
|
+
path_val_matrix[link_start[0],
|
|
1681
|
+
link_end[0],
|
|
1682
|
+
delta_tau] = val_matrix[link_start[0],
|
|
1683
|
+
link_end[0],
|
|
1684
|
+
delta_tau]
|
|
1685
|
+
|
|
1686
|
+
graph_data = {'path_node_array': path_node_array,
|
|
1687
|
+
'path_val_matrix': path_val_matrix,
|
|
1688
|
+
'tsg_path_val_matrix': tsg_path_val_matrix}
|
|
1689
|
+
|
|
1690
|
+
return graph_data
|
|
1691
|
+
|
|
1692
|
+
|
|
1693
|
+
class Prediction(Models, PCMCI):
|
|
1694
|
+
r"""Prediction class for time series models.
|
|
1695
|
+
|
|
1696
|
+
Allows to fit and predict from any sklearn model. The optimal predictors can
|
|
1697
|
+
be estimated using PCMCI. Also takes care of missing values, masking and
|
|
1698
|
+
preprocessing.
|
|
1699
|
+
|
|
1700
|
+
Parameters
|
|
1701
|
+
----------
|
|
1702
|
+
dataframe : data object
|
|
1703
|
+
Tigramite dataframe object. It must have the attributes dataframe.values
|
|
1704
|
+
yielding a numpy array of shape (observations T, variables N) and
|
|
1705
|
+
optionally a mask of the same shape and a missing values flag.
|
|
1706
|
+
train_indices : array-like
|
|
1707
|
+
Either boolean array or time indices marking the training data.
|
|
1708
|
+
test_indices : array-like
|
|
1709
|
+
Either boolean array or time indices marking the test data.
|
|
1710
|
+
prediction_model : sklearn model object
|
|
1711
|
+
For example, sklearn.linear_model.LinearRegression() for a linear
|
|
1712
|
+
regression model.
|
|
1713
|
+
cond_ind_test : Conditional independence test object, optional
|
|
1714
|
+
Only needed if predictors are estimated with causal algorithm.
|
|
1715
|
+
The class will be initialized with masking set to the training data.
|
|
1716
|
+
data_transform : sklearn preprocessing object, optional (default: None)
|
|
1717
|
+
Used to transform data prior to fitting. For example,
|
|
1718
|
+
sklearn.preprocessing.StandardScaler for simple standardization. The
|
|
1719
|
+
fitted parameters are stored.
|
|
1720
|
+
verbosity : int, optional (default: 0)
|
|
1721
|
+
Level of verbosity.
|
|
1722
|
+
"""
|
|
1723
|
+
|
|
1724
|
+
def __init__(self,
|
|
1725
|
+
dataframe,
|
|
1726
|
+
train_indices,
|
|
1727
|
+
test_indices,
|
|
1728
|
+
prediction_model,
|
|
1729
|
+
cond_ind_test=None,
|
|
1730
|
+
data_transform=None,
|
|
1731
|
+
verbosity=0):
|
|
1732
|
+
|
|
1733
|
+
if dataframe.analysis_mode != 'single':
|
|
1734
|
+
raise ValueError("Prediction class currently only supports single "
|
|
1735
|
+
"datasets.")
|
|
1736
|
+
|
|
1737
|
+
# dataframe.values = {0: dataframe.values[0]}
|
|
1738
|
+
|
|
1739
|
+
# Default value for the mask
|
|
1740
|
+
if dataframe.mask is not None:
|
|
1741
|
+
mask = {0: dataframe.mask[0]}
|
|
1742
|
+
else:
|
|
1743
|
+
mask = {0: np.zeros(dataframe.values[0].shape, dtype='bool')}
|
|
1744
|
+
# Get the dataframe shape
|
|
1745
|
+
T = dataframe.T[0]
|
|
1746
|
+
|
|
1747
|
+
# Have the default dataframe be the training data frame
|
|
1748
|
+
train_mask = deepcopy(mask)
|
|
1749
|
+
train_mask[0][[t for t in range(T) if t not in train_indices]] = True
|
|
1750
|
+
self.dataframe = deepcopy(dataframe)
|
|
1751
|
+
self.dataframe.mask = train_mask
|
|
1752
|
+
self.dataframe._initialized_from = 'dict'
|
|
1753
|
+
# = DataFrame(dataframe.values[0],
|
|
1754
|
+
# mask=train_mask,
|
|
1755
|
+
# missing_flag=dataframe.missing_flag)
|
|
1756
|
+
# Initialize the models baseclass with the training dataframe
|
|
1757
|
+
Models.__init__(self,
|
|
1758
|
+
dataframe=self.dataframe,
|
|
1759
|
+
model=prediction_model,
|
|
1760
|
+
data_transform=data_transform,
|
|
1761
|
+
mask_type='y',
|
|
1762
|
+
verbosity=verbosity)
|
|
1763
|
+
|
|
1764
|
+
# Build the testing dataframe as well
|
|
1765
|
+
self.test_mask = deepcopy(mask)
|
|
1766
|
+
self.test_mask[0][[t for t in range(T) if t not in test_indices]] = True
|
|
1767
|
+
|
|
1768
|
+
self.train_indices = train_indices
|
|
1769
|
+
self.test_indices = test_indices
|
|
1770
|
+
|
|
1771
|
+
# Setup the PCMCI instance
|
|
1772
|
+
if cond_ind_test is not None:
|
|
1773
|
+
# Force the masking
|
|
1774
|
+
cond_ind_test.set_mask_type('y')
|
|
1775
|
+
cond_ind_test.verbosity = verbosity
|
|
1776
|
+
# PCMCI.__init__(self,
|
|
1777
|
+
# dataframe=self.dataframe,
|
|
1778
|
+
# cond_ind_test=cond_ind_test,
|
|
1779
|
+
# verbosity=verbosity)
|
|
1780
|
+
self.pcmci = PCMCI(dataframe=self.dataframe,
|
|
1781
|
+
cond_ind_test=cond_ind_test,
|
|
1782
|
+
verbosity=verbosity)
|
|
1783
|
+
|
|
1784
|
+
# Set the member variables
|
|
1785
|
+
self.cond_ind_test = cond_ind_test
|
|
1786
|
+
# Initialize member varialbes that are set outside
|
|
1787
|
+
self.target_predictors = None
|
|
1788
|
+
self.selected_targets = None
|
|
1789
|
+
self.fitted_model = None
|
|
1790
|
+
self.test_array = None
|
|
1791
|
+
|
|
1792
|
+
def get_predictors(self,
|
|
1793
|
+
selected_targets=None,
|
|
1794
|
+
selected_links=None,
|
|
1795
|
+
steps_ahead=1,
|
|
1796
|
+
tau_max=1,
|
|
1797
|
+
pc_alpha=0.2,
|
|
1798
|
+
max_conds_dim=None,
|
|
1799
|
+
max_combinations=1):
|
|
1800
|
+
"""Estimate predictors using PC1 algorithm.
|
|
1801
|
+
|
|
1802
|
+
Wrapper around PCMCI.run_pc_stable that estimates causal predictors.
|
|
1803
|
+
The lead time can be specified by ``steps_ahead``.
|
|
1804
|
+
|
|
1805
|
+
Parameters
|
|
1806
|
+
----------
|
|
1807
|
+
selected_targets : list of ints, optional (default: None)
|
|
1808
|
+
List of variables to estimate predictors of. If None, predictors of
|
|
1809
|
+
all variables are estimated.
|
|
1810
|
+
selected_links : dict or None
|
|
1811
|
+
Dictionary of form {0:[(0, -1), (3, -2), ...], 1:[], ...}
|
|
1812
|
+
specifying whether only selected links should be tested. If None is
|
|
1813
|
+
passed, all links are tested
|
|
1814
|
+
steps_ahead : int, default: 1
|
|
1815
|
+
Minimum time lag to test. Useful for multi-step ahead predictions.
|
|
1816
|
+
tau_max : int, default: 1
|
|
1817
|
+
Maximum time lag. Must be larger or equal to tau_min.
|
|
1818
|
+
pc_alpha : float or list of floats, default: 0.2
|
|
1819
|
+
Significance level in algorithm. If a list or None is passed, the
|
|
1820
|
+
pc_alpha level is optimized for every variable across the given
|
|
1821
|
+
pc_alpha values using the score computed in
|
|
1822
|
+
cond_ind_test.get_model_selection_criterion()
|
|
1823
|
+
max_conds_dim : int or None
|
|
1824
|
+
Maximum number of conditions to test. If None is passed, this number
|
|
1825
|
+
is unrestricted.
|
|
1826
|
+
max_combinations : int, default: 1
|
|
1827
|
+
Maximum number of combinations of conditions of current cardinality
|
|
1828
|
+
to test. Defaults to 1 for PC_1 algorithm. For original PC algorithm
|
|
1829
|
+
a larger number, such as 10, can be used.
|
|
1830
|
+
|
|
1831
|
+
Returns
|
|
1832
|
+
-------
|
|
1833
|
+
predictors : dict
|
|
1834
|
+
Dictionary of form {0:[(0, -1), (3, -2), ...], 1:[], ...}
|
|
1835
|
+
containing estimated predictors.
|
|
1836
|
+
"""
|
|
1837
|
+
|
|
1838
|
+
if selected_links is not None:
|
|
1839
|
+
link_assumptions = {}
|
|
1840
|
+
for j in selected_links.keys():
|
|
1841
|
+
link_assumptions[j] = {(i, -tau):"-?>" for i in range(self.N) for tau in range(1, tau_max+1)}
|
|
1842
|
+
else:
|
|
1843
|
+
link_assumptions = None
|
|
1844
|
+
|
|
1845
|
+
# Ensure an independence model is given
|
|
1846
|
+
if self.cond_ind_test is None:
|
|
1847
|
+
raise ValueError("No cond_ind_test given!")
|
|
1848
|
+
# Set the selected variables
|
|
1849
|
+
self.selected_variables = range(self.N)
|
|
1850
|
+
if selected_targets is not None:
|
|
1851
|
+
self.selected_variables = selected_targets
|
|
1852
|
+
|
|
1853
|
+
predictors = self.pcmci.run_pc_stable(link_assumptions=link_assumptions,
|
|
1854
|
+
tau_min=steps_ahead,
|
|
1855
|
+
tau_max=tau_max,
|
|
1856
|
+
save_iterations=False,
|
|
1857
|
+
pc_alpha=pc_alpha,
|
|
1858
|
+
max_conds_dim=max_conds_dim,
|
|
1859
|
+
max_combinations=max_combinations)
|
|
1860
|
+
return predictors
|
|
1861
|
+
|
|
1862
|
+
def fit(self, target_predictors,
|
|
1863
|
+
selected_targets=None, tau_max=None, return_data=False):
|
|
1864
|
+
r"""Fit time series model.
|
|
1865
|
+
|
|
1866
|
+
Wrapper around ``Models.fit_full_model()``. To each variable in
|
|
1867
|
+
``selected_targets``, the sklearn model is fitted with :math:`y` given
|
|
1868
|
+
by the target variable, and :math:`X` given by its predictors. The
|
|
1869
|
+
fitted model class is returned for later use.
|
|
1870
|
+
|
|
1871
|
+
Parameters
|
|
1872
|
+
----------
|
|
1873
|
+
target_predictors : dictionary
|
|
1874
|
+
Dictionary of form {0:[(0, -1), (3, -2), ...], 1:[], ...} containing
|
|
1875
|
+
the predictors estimated with PCMCI.
|
|
1876
|
+
selected_targets : list of integers, optional (default: range(N))
|
|
1877
|
+
Specify to fit model only for selected targets. If None is
|
|
1878
|
+
passed, models are estimated for all variables.
|
|
1879
|
+
tau_max : int, optional (default: None)
|
|
1880
|
+
Maximum time lag. If None, the maximum lag in target_predictors is
|
|
1881
|
+
used.
|
|
1882
|
+
return_data : bool, optional (default: False)
|
|
1883
|
+
Whether to save the data array.
|
|
1884
|
+
|
|
1885
|
+
Returns
|
|
1886
|
+
-------
|
|
1887
|
+
self : instance of self
|
|
1888
|
+
"""
|
|
1889
|
+
|
|
1890
|
+
if selected_targets is None:
|
|
1891
|
+
self.selected_targets = range(self.N)
|
|
1892
|
+
else:
|
|
1893
|
+
self.selected_targets = selected_targets
|
|
1894
|
+
|
|
1895
|
+
if tau_max is None:
|
|
1896
|
+
# Find the maximal parents lag
|
|
1897
|
+
max_parents_lag = 0
|
|
1898
|
+
for j in self.selected_targets:
|
|
1899
|
+
if target_predictors[j]:
|
|
1900
|
+
this_parent_lag = np.abs(np.array(target_predictors[j])[:, 1]).max()
|
|
1901
|
+
max_parents_lag = max(max_parents_lag, this_parent_lag)
|
|
1902
|
+
else:
|
|
1903
|
+
max_parents_lag = tau_max
|
|
1904
|
+
|
|
1905
|
+
if len(set(np.array(self.test_indices) - max_parents_lag)
|
|
1906
|
+
.intersection(self.train_indices)) > 0:
|
|
1907
|
+
if self.verbosity > 0:
|
|
1908
|
+
warnings.warn("test_indices - maxlag(predictors) [or tau_max] "
|
|
1909
|
+
"overlaps with train_indices: Choose test_indices "
|
|
1910
|
+
"such that there is a gap of max_lag to train_indices!")
|
|
1911
|
+
|
|
1912
|
+
self.target_predictors = target_predictors
|
|
1913
|
+
|
|
1914
|
+
for target in self.selected_targets:
|
|
1915
|
+
if target not in list(self.target_predictors):
|
|
1916
|
+
raise ValueError("No predictors given for target %s" % target)
|
|
1917
|
+
|
|
1918
|
+
self.fitted_model = \
|
|
1919
|
+
self.fit_full_model(all_parents=self.target_predictors,
|
|
1920
|
+
selected_variables=self.selected_targets,
|
|
1921
|
+
tau_max=tau_max,
|
|
1922
|
+
return_data=return_data)
|
|
1923
|
+
return self
|
|
1924
|
+
|
|
1925
|
+
def predict(self, target,
|
|
1926
|
+
new_data=None,
|
|
1927
|
+
pred_params=None,
|
|
1928
|
+
cut_off='max_lag_or_tau_max'):
|
|
1929
|
+
r"""Predict target variable with fitted model.
|
|
1930
|
+
|
|
1931
|
+
Uses the model.predict() function of the sklearn model.
|
|
1932
|
+
|
|
1933
|
+
If target is an int, the predicted time series is returned. If target
|
|
1934
|
+
is a list of integers, then a list of predicted time series is returned.
|
|
1935
|
+
If the list of integers equals range(N), then an array of shape (T, N)
|
|
1936
|
+
of the predicted series is returned.
|
|
1937
|
+
|
|
1938
|
+
Parameters
|
|
1939
|
+
----------
|
|
1940
|
+
target : int or list of integers
|
|
1941
|
+
Index or indices of target variable(s).
|
|
1942
|
+
new_data : data object, optional
|
|
1943
|
+
New Tigramite dataframe object with optional new mask. Note that
|
|
1944
|
+
the data will be cut off according to cut_off, see parameter
|
|
1945
|
+
`cut_off` below.
|
|
1946
|
+
pred_params : dict, optional
|
|
1947
|
+
Optional parameters passed on to sklearn prediction function.
|
|
1948
|
+
cut_off : {'2xtau_max', 'max_lag', 'max_lag_or_tau_max'}
|
|
1949
|
+
How many samples to cutoff at the beginning. The default is
|
|
1950
|
+
'2xtau_max', which guarantees that MCI tests are all conducted on
|
|
1951
|
+
the same samples. For modeling, 'max_lag_or_tau_max' can be used,
|
|
1952
|
+
which uses the maximum of tau_max and the conditions, which is
|
|
1953
|
+
useful to compare multiple models on the same sample. Last,
|
|
1954
|
+
'max_lag' uses as much samples as possible.
|
|
1955
|
+
|
|
1956
|
+
Returns
|
|
1957
|
+
-------
|
|
1958
|
+
Results from prediction.
|
|
1959
|
+
"""
|
|
1960
|
+
|
|
1961
|
+
if isinstance(target, int):
|
|
1962
|
+
target_list = [target]
|
|
1963
|
+
elif isinstance(target, list):
|
|
1964
|
+
target_list = target
|
|
1965
|
+
else:
|
|
1966
|
+
raise ValueError("target must be either int or list of integers "
|
|
1967
|
+
"indicating the index of the variables to "
|
|
1968
|
+
"predict.")
|
|
1969
|
+
|
|
1970
|
+
if target_list == list(range(self.N)):
|
|
1971
|
+
return_type = 'array'
|
|
1972
|
+
elif len(target_list) == 1:
|
|
1973
|
+
return_type = 'series'
|
|
1974
|
+
else:
|
|
1975
|
+
return_type = 'list'
|
|
1976
|
+
|
|
1977
|
+
pred_list = []
|
|
1978
|
+
self.stored_test_array = {}
|
|
1979
|
+
for target in target_list:
|
|
1980
|
+
# Print message
|
|
1981
|
+
if self.verbosity > 0:
|
|
1982
|
+
print("\n##\n## Predicting target %s\n##" % target)
|
|
1983
|
+
if pred_params is not None:
|
|
1984
|
+
for key in list(pred_params):
|
|
1985
|
+
print("%s = %s" % (key, pred_params[key]))
|
|
1986
|
+
# Default value for pred_params
|
|
1987
|
+
if pred_params is None:
|
|
1988
|
+
pred_params = {}
|
|
1989
|
+
# Check this is a valid target
|
|
1990
|
+
if target not in self.selected_targets:
|
|
1991
|
+
raise ValueError("Target %s not yet fitted" % target)
|
|
1992
|
+
# Construct the array form of the data
|
|
1993
|
+
Y = [(target, 0)] # dummy
|
|
1994
|
+
X = [(target, 0)] # dummy
|
|
1995
|
+
Z = self.target_predictors[target]
|
|
1996
|
+
|
|
1997
|
+
# Check if we've passed a new dataframe object
|
|
1998
|
+
if new_data is not None:
|
|
1999
|
+
# if new_data.mask is None:
|
|
2000
|
+
# # if no mask is supplied, use the same mask as for the fitted array
|
|
2001
|
+
# new_data_mask = self.test_mask
|
|
2002
|
+
# else:
|
|
2003
|
+
new_data_mask = new_data.mask
|
|
2004
|
+
test_array, _, _ = new_data.construct_array(X, Y, Z,
|
|
2005
|
+
tau_max=self.tau_max,
|
|
2006
|
+
mask=new_data_mask,
|
|
2007
|
+
mask_type=self.mask_type,
|
|
2008
|
+
cut_off=cut_off,
|
|
2009
|
+
remove_overlaps=True,
|
|
2010
|
+
verbosity=self.verbosity)
|
|
2011
|
+
# Otherwise use the default values
|
|
2012
|
+
else:
|
|
2013
|
+
test_array, _, _ = \
|
|
2014
|
+
self.dataframe.construct_array(X, Y, Z,
|
|
2015
|
+
tau_max=self.tau_max,
|
|
2016
|
+
mask=self.test_mask,
|
|
2017
|
+
mask_type=self.mask_type,
|
|
2018
|
+
cut_off=cut_off,
|
|
2019
|
+
remove_overlaps=True,
|
|
2020
|
+
verbosity=self.verbosity)
|
|
2021
|
+
# Transform the data if needed
|
|
2022
|
+
a_transform = self.fitted_model[target]['data_transform']
|
|
2023
|
+
if a_transform is not None:
|
|
2024
|
+
test_array = a_transform.transform(X=test_array.T).T
|
|
2025
|
+
# Cache the test array
|
|
2026
|
+
self.stored_test_array[target] = test_array
|
|
2027
|
+
# Run the predictor
|
|
2028
|
+
predicted = self.fitted_model[target]['model'].predict(
|
|
2029
|
+
X=test_array[2:].T, **pred_params)
|
|
2030
|
+
|
|
2031
|
+
if test_array[2:].size == 0:
|
|
2032
|
+
# If there are no predictors, return the value of
|
|
2033
|
+
# empty_predictors_function, which is np.mean
|
|
2034
|
+
# and expand to the test array length
|
|
2035
|
+
predicted = predicted * np.ones(test_array.shape[1])
|
|
2036
|
+
|
|
2037
|
+
pred_list.append(predicted)
|
|
2038
|
+
|
|
2039
|
+
if return_type == 'series':
|
|
2040
|
+
return pred_list[0]
|
|
2041
|
+
elif return_type == 'list':
|
|
2042
|
+
return pred_list
|
|
2043
|
+
elif return_type == 'array':
|
|
2044
|
+
return np.array(pred_list).transpose()
|
|
2045
|
+
|
|
2046
|
+
def get_train_array(self, j):
|
|
2047
|
+
"""Returns training array for variable j."""
|
|
2048
|
+
return self.fitted_model[j]['data']
|
|
2049
|
+
|
|
2050
|
+
def get_test_array(self, j):
|
|
2051
|
+
"""Returns test array for variable j."""
|
|
2052
|
+
return self.stored_test_array[j]
|
|
2053
|
+
|
|
2054
|
+
if __name__ == '__main__':
|
|
2055
|
+
|
|
2056
|
+
import tigramite
|
|
2057
|
+
import tigramite.data_processing as pp
|
|
2058
|
+
from tigramite.toymodels import structural_causal_processes as toys
|
|
2059
|
+
from tigramite.independence_tests.parcorr import ParCorr
|
|
2060
|
+
import tigramite.plotting as tp
|
|
2061
|
+
|
|
2062
|
+
from sklearn.linear_model import LinearRegression, LogisticRegression
|
|
2063
|
+
from sklearn.multioutput import MultiOutputRegressor
|
|
2064
|
+
|
|
2065
|
+
def lin_f(x): return x
|
|
2066
|
+
|
|
2067
|
+
|
|
2068
|
+
T = 1000
|
|
2069
|
+
def lin_f(x): return x
|
|
2070
|
+
auto_coeff = 0.
|
|
2071
|
+
coeff = 2.
|
|
2072
|
+
links = {
|
|
2073
|
+
0: [((0, -1), auto_coeff, lin_f)],
|
|
2074
|
+
1: [((1, -1), auto_coeff, lin_f), ((0, 0), coeff, lin_f)],
|
|
2075
|
+
}
|
|
2076
|
+
data, nonstat = toys.structural_causal_process(links, T=T,
|
|
2077
|
+
noises=None, seed=7)
|
|
2078
|
+
|
|
2079
|
+
# data[:,1] = data[:,1] > 0.
|
|
2080
|
+
|
|
2081
|
+
# # Create some missing values
|
|
2082
|
+
# data[-10:,:] = 999.
|
|
2083
|
+
# var_names = range(2)
|
|
2084
|
+
|
|
2085
|
+
# graph = np.array([['', '-->'],
|
|
2086
|
+
# ['<--', '']],
|
|
2087
|
+
# dtype='<U3')
|
|
2088
|
+
print(data, data.mean(axis=0))
|
|
2089
|
+
dataframe = pp.DataFrame(data,
|
|
2090
|
+
# vector_vars={0:[(0,0), (1,0)], 1:[(2,0), (3,0)]}
|
|
2091
|
+
)
|
|
2092
|
+
graph = toys.links_to_graph(links, tau_max=4)
|
|
2093
|
+
|
|
2094
|
+
# # We are interested in lagged total effect of X on Y
|
|
2095
|
+
X = [(0, 0), (0, -1)]
|
|
2096
|
+
Y = [(1, 0), (1, -1)]
|
|
2097
|
+
|
|
2098
|
+
model = Models(dataframe=dataframe,
|
|
2099
|
+
model = LinearRegression(),
|
|
2100
|
+
# model = LogisticRegression(),
|
|
2101
|
+
# model = MultiOutputRegressor(LogisticRegression()),
|
|
2102
|
+
|
|
2103
|
+
)
|
|
2104
|
+
|
|
2105
|
+
model.get_general_fitted_model(
|
|
2106
|
+
Y=Y, X=X, Z=[(0, -2)],
|
|
2107
|
+
conditions=[(0, -3)],
|
|
2108
|
+
tau_max=7,
|
|
2109
|
+
cut_off='tau_max',
|
|
2110
|
+
empty_predictors_function=np.mean,
|
|
2111
|
+
return_data=False)
|
|
2112
|
+
|
|
2113
|
+
# print(model.fit_results[(1, 0)]['model'].coef_)
|
|
2114
|
+
|
|
2115
|
+
dox_vals = np.array([0.]) #np.linspace(-1., 1., 1)
|
|
2116
|
+
intervention_data = np.tile(dox_vals.reshape(len(dox_vals), 1), len(X))
|
|
2117
|
+
|
|
2118
|
+
conditions_data = np.tile(1. + dox_vals.reshape(len(dox_vals), 1), 1)
|
|
2119
|
+
|
|
2120
|
+
def aggregation_func(x, axis=0, bins=2):
|
|
2121
|
+
x = x.astype('int64')
|
|
2122
|
+
return np.apply_along_axis(np.bincount, axis=axis, arr=x, minlength=bins).T
|
|
2123
|
+
aggregation_func = np.mean
|
|
2124
|
+
|
|
2125
|
+
pred = model.get_general_prediction(
|
|
2126
|
+
intervention_data=intervention_data,
|
|
2127
|
+
conditions_data=conditions_data,
|
|
2128
|
+
pred_params=None,
|
|
2129
|
+
transform_interventions_and_prediction=False,
|
|
2130
|
+
return_further_pred_results=False,
|
|
2131
|
+
aggregation_func=aggregation_func,
|
|
2132
|
+
)
|
|
2133
|
+
|
|
2134
|
+
print("\n", pred)
|
|
2135
|
+
|
|
2136
|
+
# T = 1000
|
|
2137
|
+
|
|
2138
|
+
# links = {0: [((0, -1), 0.9, lin_f)],
|
|
2139
|
+
# 1: [((1, -1), 0.9, lin_f), ((0, 0), -0.8, lin_f)],
|
|
2140
|
+
# 2: [((2, -1), 0.9, lin_f), ((0, 0), 0.9, lin_f), ((1, 0), 0.8, lin_f)],
|
|
2141
|
+
# # 3: [((3, -1), 0.9, lin_f), ((1, 0), 0.8, lin_f), ((2, 0), -0.9, lin_f)]
|
|
2142
|
+
# }
|
|
2143
|
+
# # noises = [np.random.randn for j in links.keys()]
|
|
2144
|
+
# data, nonstat = toys.structural_causal_process(links, T=T, noises=None, seed=7)
|
|
2145
|
+
|
|
2146
|
+
# missing_flag = 999
|
|
2147
|
+
# for i in range(0, 20):
|
|
2148
|
+
# data[i::100] = missing_flag
|
|
2149
|
+
|
|
2150
|
+
# # mask = data>0
|
|
2151
|
+
|
|
2152
|
+
# parents = toys._get_true_parent_neighbor_dict(links)
|
|
2153
|
+
# dataframe = pp.DataFrame(data, missing_flag = missing_flag)
|
|
2154
|
+
|
|
2155
|
+
|
|
2156
|
+
|
|
2157
|
+
# model = LinearRegression()
|
|
2158
|
+
# model.fit(X=np.random.randn(10,2), y=np.random.randn(10))
|
|
2159
|
+
# model.predict(X=np.random.randn(10,2)[:,2:])
|
|
2160
|
+
# sys.exit(0)
|
|
2161
|
+
|
|
2162
|
+
# med = LinearMediation(dataframe=dataframe, #mask_type='y',
|
|
2163
|
+
# data_transform=None)
|
|
2164
|
+
# med.fit_model(all_parents=parents, tau_max=None, return_data=True)
|
|
2165
|
+
|
|
2166
|
+
# print(med.get_residuals_cov_mean())
|
|
2167
|
+
|
|
2168
|
+
# med.fit_model_bootstrap(
|
|
2169
|
+
# boot_blocklength='cube_root',
|
|
2170
|
+
# seed = 42,
|
|
2171
|
+
# )
|
|
2172
|
+
|
|
2173
|
+
# # print(med.get_val_matrix())
|
|
2174
|
+
|
|
2175
|
+
# print (med.get_ce(i=0, tau=0, j=3))
|
|
2176
|
+
# print(med.get_bootstrap_of(function='get_ce',
|
|
2177
|
+
# function_args={'i':0, 'tau':0, 'j':3}, conf_lev=0.9))
|
|
2178
|
+
|
|
2179
|
+
# print (med.get_coeff(i=0, tau=-2, j=1))
|
|
2180
|
+
|
|
2181
|
+
# print (med.get_ce_max(i=0, j=2))
|
|
2182
|
+
# print (med.get_ce(i=0, tau=0, j=3))
|
|
2183
|
+
# print (med.get_mce(i=0, tau=0, k=[2], j=3))
|
|
2184
|
+
# print (med.get_mce(i=0, tau=0, k=[1,2], j=3) - med.get_mce(i=0, tau=0, k=[1], j=3))
|
|
2185
|
+
# print (med.get_conditional_mce(i=0, tau=0, k=[2], notk=[1], j=3))
|
|
2186
|
+
# print (med.get_bootstrap_of('get_conditional_mce', {'i':0, 'tau':0, 'k':[2], 'notk':[1], 'j':3}))
|
|
2187
|
+
|
|
2188
|
+
# print(med.get_joint_ce(i=0, j=2))
|
|
2189
|
+
# print(med.get_joint_mce(i=0, j=2, k=1))
|
|
2190
|
+
|
|
2191
|
+
# print(med.get_joint_ce_matrix(i=0, j=2))
|
|
2192
|
+
|
|
2193
|
+
# i=0; tau=4; j=2
|
|
2194
|
+
# graph_data = med.get_mediation_graph_data(i=i, tau=tau, j=j)
|
|
2195
|
+
# tp.plot_mediation_time_series_graph(
|
|
2196
|
+
# # var_names=var_names,
|
|
2197
|
+
# path_node_array=graph_data['path_node_array'],
|
|
2198
|
+
# tsg_path_val_matrix=graph_data['tsg_path_val_matrix']
|
|
2199
|
+
# )
|
|
2200
|
+
# tp.plot_mediation_graph(
|
|
2201
|
+
# # var_names=var_names,
|
|
2202
|
+
# path_val_matrix=graph_data['path_val_matrix'],
|
|
2203
|
+
# path_node_array=graph_data['path_node_array'],
|
|
2204
|
+
# );
|
|
2205
|
+
# plt.show()
|
|
2206
|
+
|
|
2207
|
+
# print ("Average Causal Effect X=%.2f, Y=%.2f, Z=%.2f " % tuple(med.get_all_ace()))
|
|
2208
|
+
# print ("Average Causal Susceptibility X=%.2f, Y=%.2f, Z=%.2f " % tuple(med.get_all_acs()))
|
|
2209
|
+
# print ("Average Mediated Causal Effect X=%.2f, Y=%.2f, Z=%.2f " % tuple(med.get_all_amce()))
|
|
2210
|
+
# med = Models(dataframe=dataframe, model=sklearn.linear_model.LinearRegression(), data_transform=None)
|
|
2211
|
+
# # Fit the model
|
|
2212
|
+
# med.get_fit(all_parents=true_parents, tau_max=3)
|
|
2213
|
+
|
|
2214
|
+
# print(med.get_val_matrix())
|
|
2215
|
+
|
|
2216
|
+
# for j, i, tau, coeff in toys._iter_coeffs(links):
|
|
2217
|
+
# print(i, j, tau, coeff, med.get_coeff(i=i, tau=tau, j=j))
|
|
2218
|
+
|
|
2219
|
+
# for causal_coeff in [med.get_ce(i=0, tau=-2, j=2),
|
|
2220
|
+
# med.get_mce(i=0, tau=-2, j=2, k=1)]:
|
|
2221
|
+
# print(causal_coeff)
|
|
2222
|
+
|
|
2223
|
+
|
|
2224
|
+
# pred = Prediction(dataframe=dataframe,
|
|
2225
|
+
# cond_ind_test=ParCorr(), #CMIknn ParCorr
|
|
2226
|
+
# prediction_model = sklearn.linear_model.LinearRegression(),
|
|
2227
|
+
# # prediction_model = sklearn.gaussian_process.GaussianProcessRegressor(),
|
|
2228
|
+
# # prediction_model = sklearn.neighbors.KNeighborsRegressor(),
|
|
2229
|
+
# data_transform=sklearn.preprocessing.StandardScaler(),
|
|
2230
|
+
# train_indices= list(range(int(0.8*T))),
|
|
2231
|
+
# test_indices= list(range(int(0.8*T), T)),
|
|
2232
|
+
# verbosity=0
|
|
2233
|
+
# )
|
|
2234
|
+
|
|
2235
|
+
# # predictors = pred.get_predictors(
|
|
2236
|
+
# # selected_targets=[2],
|
|
2237
|
+
# # selected_links=None,
|
|
2238
|
+
# # steps_ahead=1,
|
|
2239
|
+
# # tau_max=1,
|
|
2240
|
+
# # pc_alpha=0.2,
|
|
2241
|
+
# # max_conds_dim=None,
|
|
2242
|
+
# # max_combinations=1)
|
|
2243
|
+
# predictors = {0: [], # [(0, -1)],
|
|
2244
|
+
# 1: [(1, -1), (0, -1)],
|
|
2245
|
+
# 2: [(2, -1), (1, 0)]}
|
|
2246
|
+
# pred.fit(target_predictors=predictors,
|
|
2247
|
+
# selected_targets=None, tau_max=None, return_data=False)
|
|
2248
|
+
|
|
2249
|
+
# res = pred.predict(target=0,
|
|
2250
|
+
# new_data=None,
|
|
2251
|
+
# pred_params=None,
|
|
2252
|
+
# cut_off='max_lag_or_tau_max')
|
|
2253
|
+
|
|
2254
|
+
# print(data[:,2])
|
|
2255
|
+
# print(res)
|
|
2256
|
+
|
|
2257
|
+
|