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
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from tigramite.toymodels import structural_causal_processes as toys
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _nb_latent_before(node, observed_context_indices, node_classification):
|
|
7
|
+
return len(
|
|
8
|
+
[el for el in range(node) if not (el in observed_context_indices or node_classification[el] == "system")])
|
|
9
|
+
|
|
10
|
+
def _do_dummy_projection(links, node_classification, observed_context_indices, time_dummy_index, space_dummy_index):
|
|
11
|
+
"""
|
|
12
|
+
Helper function to augment the true_parents by remove context-context links and adding dummy
|
|
13
|
+
(i.e. perform dummy projection)
|
|
14
|
+
|
|
15
|
+
links : dictionary
|
|
16
|
+
Ground truth links
|
|
17
|
+
node_classification : dictionary
|
|
18
|
+
Corresponds to ground truth links
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# remove latent links, shift remaining, add dummy
|
|
22
|
+
augmented_links = {}
|
|
23
|
+
for node in node_classification.keys():
|
|
24
|
+
if node_classification[node] == "system":
|
|
25
|
+
keep_parents = []
|
|
26
|
+
for parent in links[node]:
|
|
27
|
+
if node_classification[parent[0][0]] == "system":
|
|
28
|
+
keep_parents.append(parent)
|
|
29
|
+
elif node_classification[parent[0][0]] == "time_context":
|
|
30
|
+
if parent[0][0] in observed_context_indices:
|
|
31
|
+
keep_parents.append(((parent[0][0] - _nb_latent_before(parent[0][0], observed_context_indices,
|
|
32
|
+
node_classification),
|
|
33
|
+
parent[0][1]), parent[1], parent[2]))
|
|
34
|
+
else:
|
|
35
|
+
keep_parents.append(((time_dummy_index, 0), 1., "dummy"))
|
|
36
|
+
elif node_classification[parent[0][0]] == "space_context":
|
|
37
|
+
if parent[0][0] in observed_context_indices:
|
|
38
|
+
keep_parents.append(((parent[0][0] - _nb_latent_before(parent[0][0], observed_context_indices,
|
|
39
|
+
node_classification), parent[0][1]),
|
|
40
|
+
parent[1], parent[2]))
|
|
41
|
+
else:
|
|
42
|
+
keep_parents.append(((space_dummy_index, 0), 1., "dummy"))
|
|
43
|
+
augmented_links[node] = list(dict.fromkeys(keep_parents))
|
|
44
|
+
|
|
45
|
+
# remove all parents of context nodes
|
|
46
|
+
elif node_classification[node] == "time_context":
|
|
47
|
+
if node in observed_context_indices:
|
|
48
|
+
augmented_links[node - _nb_latent_before(node, observed_context_indices, node_classification)] = []
|
|
49
|
+
elif node_classification[node] == "space_context":
|
|
50
|
+
if node in observed_context_indices:
|
|
51
|
+
augmented_links[node - _nb_latent_before(node, observed_context_indices, node_classification)] = []
|
|
52
|
+
|
|
53
|
+
augmented_links[time_dummy_index] = []
|
|
54
|
+
augmented_links[space_dummy_index] = []
|
|
55
|
+
return augmented_links
|
|
56
|
+
|
|
57
|
+
def _shift_link_entries(links, const):
|
|
58
|
+
"""
|
|
59
|
+
Helper Function to shift keys and values of a link dictionary by an integer constant.
|
|
60
|
+
"""
|
|
61
|
+
shifted_links = {}
|
|
62
|
+
for key in links.keys():
|
|
63
|
+
shifted_key = key + const
|
|
64
|
+
values = links[key]
|
|
65
|
+
shifted_values = [((item + const, lag), c, f) for ((item, lag), c, f) in values]
|
|
66
|
+
shifted_links[shifted_key] = shifted_values
|
|
67
|
+
return shifted_links
|
|
68
|
+
|
|
69
|
+
def _group_links(links, node_types, node_type):
|
|
70
|
+
return {i: links[i] for i in links.keys() if node_types[i] == node_type}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ContextModel:
|
|
74
|
+
"""Allows to sample from a joint structural causal model over different spatial
|
|
75
|
+
and temporal contexts. Restricts temporal and spatial context nodes to be constant over datasets or
|
|
76
|
+
time, respectively.
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
links : dict
|
|
81
|
+
Dictionary of format: {0:[((i, -tau), coeff, func),...], 1:[...],
|
|
82
|
+
...} for all variables where i must be in [0..N-1] and tau >= 0 with
|
|
83
|
+
number of variables N. coeff must be a float and func a python
|
|
84
|
+
callable of one argument.
|
|
85
|
+
node_classification : dictionary
|
|
86
|
+
Classification of nodes into system, or context nodes.
|
|
87
|
+
Keys of the dictionary are from {0, ..., N-1} where N is the number of nodes.
|
|
88
|
+
Options for the values are "system", "time_context", "space_context". The temporal context variables are
|
|
89
|
+
assumed exogenous to the system variables. They cannot interact with the spatial context variables due
|
|
90
|
+
to the assumption that they are constant across datasets.
|
|
91
|
+
transient_fraction : float
|
|
92
|
+
Added percentage of T used as a transient. In total a realization of length
|
|
93
|
+
(transient_fraction + 1)*T will be generated, but then transient_fraction*T will be
|
|
94
|
+
cut off.
|
|
95
|
+
noises : list of callables or list of arrays, optional (default: None)
|
|
96
|
+
Random distribution function that is called with noises[j](T) for system and time-context nodes, or
|
|
97
|
+
noises[j](M) for space-context nodes where M is the number of datasets. If list of arrays,
|
|
98
|
+
for noises corresponding to time-context and system variables the array needs to be of
|
|
99
|
+
shape ((transient_fraction + 1)*T, ), for space-context variables it needs to be of shape (M, )
|
|
100
|
+
seed : int, optional (default: None)
|
|
101
|
+
Random seed.
|
|
102
|
+
|
|
103
|
+
Attributes
|
|
104
|
+
-------
|
|
105
|
+
links_tc : dict
|
|
106
|
+
Dictionary of format: {0:[((i, -tau), coeff, func),...], 1:[...],
|
|
107
|
+
...} for all temporal context variables where i must be in [0..N-1] and tau >= 0 with
|
|
108
|
+
number of variables N. coeff must be a float and func a python
|
|
109
|
+
callable of one argument. The temporal context variables are assumed exogenous
|
|
110
|
+
to the system variables. They cannot interact with the spatial context variables due to the assumption
|
|
111
|
+
that they are constant across datasets.
|
|
112
|
+
links_sc : dict
|
|
113
|
+
Dictionary of format: {0:[((i, -tau), coeff, func),...], 1:[...],
|
|
114
|
+
...} for all spatial context variables where i must be in [0..N-1] and tau >= 0 with
|
|
115
|
+
number of variables N. coeff must be a float and func a python
|
|
116
|
+
callable of one argument. The spatial context variables are assumed exogenous
|
|
117
|
+
to the system variables. They cannot interact with the temporal context variables due to the assumption
|
|
118
|
+
that they are time-independent, i.e. constant across time.
|
|
119
|
+
links_sys : dict
|
|
120
|
+
Dictionary of format: {0:[((i, -tau), coeff, func),...], 1:[...],
|
|
121
|
+
...} for all system variables where i must be in [0..N-1] and tau >= 0 with
|
|
122
|
+
number of variables N. coeff must be a float and func a python
|
|
123
|
+
callable of one argument.
|
|
124
|
+
transient_fraction : float
|
|
125
|
+
Added percentage of T used as a transient. In total a realization of length
|
|
126
|
+
(transient_fraction + 1)*T will be generated, but then transient_fraction*T will be
|
|
127
|
+
cut off.
|
|
128
|
+
noises : list of callables or list of arrays, optional (default: None)
|
|
129
|
+
Random distribution function that is called with noises[j](T) for system and time-context nodes, or
|
|
130
|
+
noises[j](M) for space-context nodes where M is the number of datasets. If list of arrays,
|
|
131
|
+
for noises corresponding to time-context and system variables the array needs to be of
|
|
132
|
+
shape ((transient_fraction + 1)*T, ), for space-context variables it needs to be of shape (M, )
|
|
133
|
+
seed : int, optional (default: None)
|
|
134
|
+
Random seed.
|
|
135
|
+
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
def __init__(self, links={}, node_classification={}, transient_fraction=0.2, noises=None, seed=None):
|
|
139
|
+
self.links_tc = _group_links(links, node_classification, "time_context")
|
|
140
|
+
self.links_sc = _group_links(links, node_classification, "space_context")
|
|
141
|
+
self.links_sys = _group_links(links, node_classification, "system")
|
|
142
|
+
|
|
143
|
+
self.N = len(self.links_sys.keys())
|
|
144
|
+
self.noises = noises
|
|
145
|
+
self.seed = seed
|
|
146
|
+
self.transient_fraction = transient_fraction
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _constant_over_space(self, data_tc, M):
|
|
151
|
+
data_tc_list = [data_tc for _ in range(M)]
|
|
152
|
+
return data_tc_list
|
|
153
|
+
|
|
154
|
+
def _constant_over_time(self, data_sc, T, M, shift):
|
|
155
|
+
data_sc_list = [{i: np.repeat(data_sc[m, i-shift], T) for i in self.links_sc.keys()} for m in
|
|
156
|
+
range(M)]
|
|
157
|
+
return data_sc_list
|
|
158
|
+
|
|
159
|
+
def _generate_temporal_context_data(self, links_tc, T, M, seed):
|
|
160
|
+
"""
|
|
161
|
+
Helper Function to generate data for the temporal context nodes. It essentially is a
|
|
162
|
+
wrapper around toys.structural_causal_process to generate data that is random across time
|
|
163
|
+
but constant across datasets.
|
|
164
|
+
"""
|
|
165
|
+
if self.noises is not None:
|
|
166
|
+
noises_tc = [self.noises[key] for key in links_tc.keys()]
|
|
167
|
+
if np.all([isinstance(el, np.ndarray) for el in noises_tc]):
|
|
168
|
+
noises_tc = np.stack(noises_tc).transpose()
|
|
169
|
+
else:
|
|
170
|
+
noises_tc = None
|
|
171
|
+
shifted_links_tc = _shift_link_entries(links_tc, -self.N)
|
|
172
|
+
data_tc, nonstat_tc = toys.structural_causal_process(shifted_links_tc, T=T, noises=noises_tc,
|
|
173
|
+
transient_fraction=self.transient_fraction,
|
|
174
|
+
seed=seed)
|
|
175
|
+
data_tc = {i: data_tc[:, i - self.N] for i in links_tc.keys()}
|
|
176
|
+
|
|
177
|
+
data_tc_list = self._constant_over_space(data_tc, M)
|
|
178
|
+
return data_tc_list, np.any(nonstat_tc)
|
|
179
|
+
|
|
180
|
+
def _generate_spatial_context_data(self, links_sc, T, M, shift, seed):
|
|
181
|
+
"""
|
|
182
|
+
Helper Function to generate data for the spatial context nodes. It essentially is a
|
|
183
|
+
wrapper around toys.structural_causal_process to generate data that is random across datasets
|
|
184
|
+
but constant across time.
|
|
185
|
+
"""
|
|
186
|
+
shifted_links_sc = _shift_link_entries(links_sc, -shift)
|
|
187
|
+
if self.noises is not None:
|
|
188
|
+
noises_sc = [self.noises[key] for key in links_sc.keys()]
|
|
189
|
+
if np.all([isinstance(el, np.ndarray) for el in noises_sc]):
|
|
190
|
+
noises_sc = np.stack(noises_sc).transpose()
|
|
191
|
+
else:
|
|
192
|
+
noises_sc = None
|
|
193
|
+
|
|
194
|
+
data_sc, nonstat_sc = toys.structural_causal_process(shifted_links_sc, T=M, noises=noises_sc,
|
|
195
|
+
transient_fraction=0.,
|
|
196
|
+
seed=seed)
|
|
197
|
+
|
|
198
|
+
data_sc_list = self._constant_over_time(data_sc, T, M, shift)
|
|
199
|
+
return data_sc_list, np.any(nonstat_sc)
|
|
200
|
+
|
|
201
|
+
def generate_data(self, M, T):
|
|
202
|
+
"""
|
|
203
|
+
Generates M datasets of time series generated from a joint structural causal model over different spatial
|
|
204
|
+
and temporal contexts.
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
M : int
|
|
209
|
+
Number of datasets.
|
|
210
|
+
T : int
|
|
211
|
+
Sample size.
|
|
212
|
+
|
|
213
|
+
Returns
|
|
214
|
+
----------
|
|
215
|
+
data : dictionary with array-like values
|
|
216
|
+
Datasets generated from this process, each dataset has the shape (T, N).
|
|
217
|
+
nonvalid : bool
|
|
218
|
+
Indicates whether data has NaNs or infinities.
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
links = {**self.links_tc, **self.links_sc, **self.links_sys}
|
|
222
|
+
|
|
223
|
+
K_time = len(self.links_tc.keys())
|
|
224
|
+
|
|
225
|
+
data = {}
|
|
226
|
+
nonstationary = []
|
|
227
|
+
|
|
228
|
+
time_seed = [1, self.seed]
|
|
229
|
+
space_seed = [2, self.seed]
|
|
230
|
+
system_seed = [3, self.seed]
|
|
231
|
+
|
|
232
|
+
# first generate data for temporal context nodes
|
|
233
|
+
data_tc_list, nonstat_tc = self._generate_temporal_context_data(self.links_tc, T, M, time_seed)
|
|
234
|
+
|
|
235
|
+
# generate spatial context data (constant in time)
|
|
236
|
+
data_sc_list, nonstat_sc = self._generate_spatial_context_data(self.links_sc,
|
|
237
|
+
T, M,
|
|
238
|
+
K_time + self.N,
|
|
239
|
+
space_seed)
|
|
240
|
+
for m in range(M):
|
|
241
|
+
data_context = dict(data_tc_list[m])
|
|
242
|
+
data_context.update(data_sc_list[m])
|
|
243
|
+
|
|
244
|
+
if self.noises is not None:
|
|
245
|
+
noises_filled = self.noises
|
|
246
|
+
if np.all([isinstance(el, np.ndarray) for el in self.noises]):
|
|
247
|
+
noises_filled = np.copy(self.noises)
|
|
248
|
+
for key in self.links_sc.keys():
|
|
249
|
+
# fill up any space-context noise to have T entries, then convert to numpy array
|
|
250
|
+
noises_filled[key] = np.random.standard_normal(len(self.noises[list(self.links_sys.keys())[0]]))
|
|
251
|
+
noises_filled = np.stack(noises_filled).transpose()
|
|
252
|
+
else:
|
|
253
|
+
noises_filled = None
|
|
254
|
+
|
|
255
|
+
# generate system data that varies over space and time
|
|
256
|
+
data_m, nonstat = toys.structural_causal_process(links, T=T, intervention=data_context,
|
|
257
|
+
transient_fraction=self.transient_fraction,
|
|
258
|
+
seed=system_seed, noises=noises_filled)
|
|
259
|
+
data[m] = data_m
|
|
260
|
+
nonstationary.append(nonstat or nonstat_tc or nonstat_sc)
|
|
261
|
+
return data, np.any(nonstationary)
|