GLDF 0.9.0__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.
- GLDF/__init__.py +2 -0
- GLDF/bridges/__init__.py +0 -0
- GLDF/bridges/causal_learn.py +185 -0
- GLDF/bridges/tigramite.py +143 -0
- GLDF/bridges/tigramite_plotting_modified.py +4764 -0
- GLDF/cit.py +274 -0
- GLDF/data_management.py +588 -0
- GLDF/data_processing.py +754 -0
- GLDF/frontend.py +537 -0
- GLDF/hccd.py +403 -0
- GLDF/hyperparams.py +205 -0
- GLDF/independence_atoms.py +78 -0
- GLDF/state_space_construction.py +288 -0
- GLDF/tutorials/01_preconfigured_quickstart.ipynb +302 -0
- GLDF/tutorials/02_detailed_configuration.ipynb +394 -0
- GLDF/tutorials/03_custom_patterns.ipynb +447 -0
- gldf-0.9.0.dist-info/METADATA +101 -0
- gldf-0.9.0.dist-info/RECORD +20 -0
- gldf-0.9.0.dist-info/WHEEL +4 -0
- gldf-0.9.0.dist-info/licenses/LICENSE +621 -0
GLDF/__init__.py
ADDED
GLDF/bridges/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
try:
|
|
2
|
+
import causallearn.search
|
|
3
|
+
except ModuleNotFoundError:
|
|
4
|
+
raise RuntimeError("Causal-learn bridge could not be loaded. Is causal-learn installed?")
|
|
5
|
+
|
|
6
|
+
from causallearn.utils import cit
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
from functools import partial
|
|
10
|
+
from typing import Callable
|
|
11
|
+
|
|
12
|
+
from ..hccd import CI_Identifier, abstract_cit_t, abstract_cd_t
|
|
13
|
+
from ..data_management import IManageData
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# in theory pyhton can copy functions, in practise it cannot (as of v3.13), so we copy-paste the code fromcausallearn.utils instead
|
|
17
|
+
def named_CIT(data, method='fisherz', **kwargs):
|
|
18
|
+
if method == cit.fisherz:
|
|
19
|
+
return cit.FisherZ(data, **kwargs)
|
|
20
|
+
elif method == cit.kci:
|
|
21
|
+
return cit.KCI(data, **kwargs)
|
|
22
|
+
elif method in [cit.chisq, cit.gsq]:
|
|
23
|
+
return cit.Chisq_or_Gsq(data, method_name=method, **kwargs)
|
|
24
|
+
elif method == cit.mv_fisherz:
|
|
25
|
+
return cit.MV_FisherZ(data, **kwargs)
|
|
26
|
+
elif method == cit.mc_fisherz:
|
|
27
|
+
return cit.MC_FisherZ(data, **kwargs)
|
|
28
|
+
elif method == cit.d_separation:
|
|
29
|
+
return cit.D_Separation(data, **kwargs)
|
|
30
|
+
else:
|
|
31
|
+
raise ValueError("Unknown method: {}".format(method))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def pick_CIT(data, method, **kwargs):
|
|
36
|
+
if isinstance(method, str):
|
|
37
|
+
return named_CIT(data, method, **kwargs)
|
|
38
|
+
else:
|
|
39
|
+
return method.provide_with_cl_args(**kwargs)
|
|
40
|
+
|
|
41
|
+
cit.CIT = pick_CIT
|
|
42
|
+
|
|
43
|
+
cit.CIT_Base = object # fix "validation" in fci
|
|
44
|
+
|
|
45
|
+
from causallearn.search.ConstraintBased.PC import pc as _cl_impl_pc
|
|
46
|
+
from causallearn.search.ConstraintBased.FCI import fci as _cl_impl_fci
|
|
47
|
+
import causallearn.search.ConstraintBased.FCI as _FCI_module
|
|
48
|
+
|
|
49
|
+
# in 'get_color_edges', l. 627 of causallearn.search.ConstraintBased.FCI there is a print command
|
|
50
|
+
# apparently left over from debugging, which will spam links,
|
|
51
|
+
# suppress all prints from this modul by brute-force:
|
|
52
|
+
_FCI_module.print = lambda *args : None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def causallearn_graph_to_tigramite_graph_pc(G):
|
|
56
|
+
N, validate = G.shape
|
|
57
|
+
assert validate == N
|
|
58
|
+
result = np.full_like(G, '', dtype='<U3')
|
|
59
|
+
for i in range(N):
|
|
60
|
+
for j in range(N):
|
|
61
|
+
if G[i,j] == +1:
|
|
62
|
+
if G[j,i] == +1:
|
|
63
|
+
result[i,j] = "<->"
|
|
64
|
+
else:
|
|
65
|
+
assert G[j,i] == -1
|
|
66
|
+
result[i,j] = "<--"
|
|
67
|
+
elif G[i,j] == -1:
|
|
68
|
+
if G[j,i] == +1:
|
|
69
|
+
result[i,j] = "-->"
|
|
70
|
+
else:
|
|
71
|
+
assert G[j,i] == -1
|
|
72
|
+
result[i,j] = "o-o"
|
|
73
|
+
return result
|
|
74
|
+
|
|
75
|
+
def _np_replace_char(data, i, j, idx, value):
|
|
76
|
+
current = list(data[i,j])
|
|
77
|
+
current[idx] = value
|
|
78
|
+
data[i,j] = "".join(current)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def causallearn_graph_to_tigramite_graph_fci(G):
|
|
82
|
+
N, validate = G.shape
|
|
83
|
+
assert validate == N
|
|
84
|
+
result = np.full_like(G, '', dtype='<U3')
|
|
85
|
+
for i in range(N):
|
|
86
|
+
for j in range(N):
|
|
87
|
+
if G[i,j] == 0:
|
|
88
|
+
assert G[j,i] == 0
|
|
89
|
+
continue
|
|
90
|
+
result[i,j] = "?-?"
|
|
91
|
+
if G[i,j] == +1:
|
|
92
|
+
_np_replace_char( result, i, j, 0, '<' )
|
|
93
|
+
elif G[i,j] == -1:
|
|
94
|
+
_np_replace_char( result, i, j, 0, '-' )
|
|
95
|
+
elif G[i,j] == 2:
|
|
96
|
+
_np_replace_char( result, i, j, 0, 'o' )
|
|
97
|
+
else:
|
|
98
|
+
assert False
|
|
99
|
+
if G[j,i] == +1:
|
|
100
|
+
_np_replace_char( result, i, j, 2, '>' )
|
|
101
|
+
elif G[j,i] == -1:
|
|
102
|
+
_np_replace_char( result, i, j, 2, '-' )
|
|
103
|
+
elif G[j,i] == 2:
|
|
104
|
+
_np_replace_char( result, i, j, 2, 'o' )
|
|
105
|
+
else:
|
|
106
|
+
assert False
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def causallearn_graph_to_tigramite_graph(graph):
|
|
111
|
+
G = None
|
|
112
|
+
if isinstance(graph, tuple):
|
|
113
|
+
general_graph, _ = graph
|
|
114
|
+
G = general_graph.graph # fci
|
|
115
|
+
return causallearn_graph_to_tigramite_graph_fci(G)
|
|
116
|
+
else:
|
|
117
|
+
G = graph.G.graph # pc
|
|
118
|
+
return causallearn_graph_to_tigramite_graph_pc(G)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class WrapCIT_CausalLearnIndexing:
|
|
122
|
+
def __init__(self, generalized_cit: abstract_cit_t):
|
|
123
|
+
self.generalized_cit = generalized_cit
|
|
124
|
+
self.method = "custom"
|
|
125
|
+
|
|
126
|
+
def __call__(self, x_idx: int, y_idx: int, Z: list[int]) -> float:
|
|
127
|
+
ci = CI_Identifier(idx_x=x_idx, idx_y=y_idx, idx_list_z=Z)
|
|
128
|
+
result = self.generalized_cit(ci)
|
|
129
|
+
# cl wants a pvalue under independence (signigicance is decided by test, cf run_pc)
|
|
130
|
+
return 0.0 if result else 1.0
|
|
131
|
+
|
|
132
|
+
def provide_with_cl_args(self, **args) -> Callable[[int, int, list[int]], float]:
|
|
133
|
+
return self
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def run_pc(data_format: IManageData, generalized_cit: abstract_cit_t, **args):
|
|
137
|
+
# Note: alpha param of cl is unused (signigicance is decided by test, cf cit-wrapper)
|
|
138
|
+
data_ignored_but_need_correct_shape_and_type = np.empty(shape=(data_format.total_sample_size(), data_format.number_of_variables()), dtype=np.void)
|
|
139
|
+
result = _cl_impl_pc(data=data_ignored_but_need_correct_shape_and_type, indep_test=WrapCIT_CausalLearnIndexing(generalized_cit), show_progress=False, **args)
|
|
140
|
+
return causallearn_graph_to_tigramite_graph(result)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def run_fci(data_format: IManageData, generalized_cit: abstract_cit_t, **args):
|
|
145
|
+
# Note: alpha param of cl is unused (signigicance is decided by test, cf cit-wrapper)
|
|
146
|
+
data_ignored_but_need_correct_shape_and_type = np.empty(shape=(data_format.total_sample_size(), data_format.number_of_variables()), dtype=np.void)
|
|
147
|
+
result = _cl_impl_fci(dataset=data_ignored_but_need_correct_shape_and_type, independence_test_method=WrapCIT_CausalLearnIndexing(generalized_cit), show_progress=False, **args)
|
|
148
|
+
return causallearn_graph_to_tigramite_graph(result)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def alg_pc(data_format: IManageData, **runtime_args) -> abstract_cd_t:
|
|
152
|
+
"""
|
|
153
|
+
Get PC [SG91]_ implementation from causal learn.
|
|
154
|
+
|
|
155
|
+
:param data_format: data manager
|
|
156
|
+
:type data_format: IManageData
|
|
157
|
+
:param runtime_args: forwarded to causal-learns run_pc (together with "stable=False")
|
|
158
|
+
:return: PC as abstract CD-algorithm.
|
|
159
|
+
:rtype: abstract_cd_t
|
|
160
|
+
"""
|
|
161
|
+
return partial(run_pc, data_format=data_format, stable=False, **runtime_args)
|
|
162
|
+
|
|
163
|
+
def alg_pc_stable(data_format: IManageData, **runtime_args) -> abstract_cd_t:
|
|
164
|
+
"""
|
|
165
|
+
Get PC-stable [CM+14]_ implementation from causal learn.
|
|
166
|
+
|
|
167
|
+
:param data_format: data manager
|
|
168
|
+
:type data_format: IManageData
|
|
169
|
+
:param runtime_args: forwarded to causal-learns run_pc (together with "stable=True")
|
|
170
|
+
:return: PC-stable as abstract CD-algorithm.
|
|
171
|
+
:rtype: abstract_cd_t
|
|
172
|
+
"""
|
|
173
|
+
return partial(run_pc, data_format=data_format, stable=True, **runtime_args)
|
|
174
|
+
|
|
175
|
+
def alg_fci(data_format: IManageData, **runtime_args) -> abstract_cd_t:
|
|
176
|
+
"""
|
|
177
|
+
Get FCI [SGS01]_ implementation from causal learn.
|
|
178
|
+
|
|
179
|
+
:param data_format: data manager
|
|
180
|
+
:type data_format: IManageData
|
|
181
|
+
:param runtime_args: forwarded to causal-learns run_fci
|
|
182
|
+
:return: FCI as abstract CD-algorithm.
|
|
183
|
+
:rtype: abstract_cd_t
|
|
184
|
+
"""
|
|
185
|
+
return partial(run_fci, data_format=data_format, **runtime_args)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
try:
|
|
2
|
+
from tigramite.pcmci import PCMCI
|
|
3
|
+
from tigramite.lpcmci import LPCMCI
|
|
4
|
+
except ModuleNotFoundError:
|
|
5
|
+
raise RuntimeError("Tigramite bridge could not be loaded. Is tigramite installed?")
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from typing import Literal
|
|
9
|
+
np.fastCopyAndTranspose = lambda a: a.T.copy() # this was deprecated since 1.24.0, removed in 2.0.0, but is used in some places in tigramite
|
|
10
|
+
|
|
11
|
+
from ..hccd import IHandleExplicitTransitionToMCI, CI_Identifier_TimeSeries, abstract_cit_t, abstract_cd_t
|
|
12
|
+
from ..data_management import IManageData
|
|
13
|
+
|
|
14
|
+
class WrapCIT_TigramiteIndexing:
|
|
15
|
+
def __init__(self, generalized_cit: abstract_cit_t):
|
|
16
|
+
self.generalized_cit = generalized_cit
|
|
17
|
+
self.method = "custom"
|
|
18
|
+
self.measure = "custom" # used only on higher verbosity of PCMCI(?)
|
|
19
|
+
self.confidence = None
|
|
20
|
+
self.significance = "custom"
|
|
21
|
+
def run_test(self, X: list[tuple[int,int]], Y: list[tuple[int,int]], Z: list[tuple[int,int]], tau_max: int, alpha_or_thres: float) -> tuple[float, float, bool]:
|
|
22
|
+
if len(X) != 1 or len(Y) != 1:
|
|
23
|
+
raise NotImplementedError("Currently this implementation supports only univariate X, Y.")
|
|
24
|
+
ci = CI_Identifier_TimeSeries(idx_x=X[0], idx_y=Y[0], idx_list_z=Z)
|
|
25
|
+
result = self.generalized_cit(ci)
|
|
26
|
+
# return dependency-score, pvalue, dependent (yes/no)
|
|
27
|
+
return 1.0 if result else 0.0, 0.0 if result else 1.0, result
|
|
28
|
+
def get_confidence(*args, **args_dict):
|
|
29
|
+
return None # run_mci with val_only=True does not seem to work
|
|
30
|
+
|
|
31
|
+
class WrapPCMCI(PCMCI):
|
|
32
|
+
def __init__(self, data_format: IManageData, mode: Literal["PCMCI", "PCMCI+"]="PCMCI", mci_transition_callback: IHandleExplicitTransitionToMCI=None, pcmci_obj_init_args: dict={}, pcmci_obj_run_args: dict={}):
|
|
33
|
+
class PlaceholderTest:
|
|
34
|
+
def set_dataframe(self, not_a_dataframe):
|
|
35
|
+
pass
|
|
36
|
+
class PlaceholderDataframe:
|
|
37
|
+
def __init__(self):
|
|
38
|
+
self.var_names = None
|
|
39
|
+
self.T = None
|
|
40
|
+
self.N = None
|
|
41
|
+
|
|
42
|
+
super().__init__(dataframe=PlaceholderDataframe(), cond_ind_test=PlaceholderTest(), **pcmci_obj_init_args)
|
|
43
|
+
self.data_format = data_format
|
|
44
|
+
self.mci_transition_callback = mci_transition_callback
|
|
45
|
+
self.runtime_args = pcmci_obj_run_args
|
|
46
|
+
if mode == "PCMCI":
|
|
47
|
+
self.run = self.run_pcmci
|
|
48
|
+
elif mode == "PCMCIplus" or mode == "PCMCI+":
|
|
49
|
+
self.run = self.run_pcmciplus
|
|
50
|
+
else:
|
|
51
|
+
raise ValueError("Unknown mode for PCMCI, supported values are 'PCMCI' or 'PCMCI+'. Did you want to use WrapLPCMCI instead?")
|
|
52
|
+
|
|
53
|
+
def run_pc_stable(self, *args_tuple, **args_dict):
|
|
54
|
+
self.mci_transition_callback.enter_pc1()
|
|
55
|
+
return super().run_pc_stable(*args_tuple, **args_dict)
|
|
56
|
+
def run_mci(self, *args_tuple, **args_dict):
|
|
57
|
+
self.mci_transition_callback.enter_mci()
|
|
58
|
+
return super().run_mci(*args_tuple, **args_dict)
|
|
59
|
+
def _pcmciplus_mci_skeleton_phase(self, *args_tuple, **args_dict):
|
|
60
|
+
self.mci_transition_callback.enter_mci()
|
|
61
|
+
return super()._pcmciplus_mci_skeleton_phase(*args_tuple, **args_dict)
|
|
62
|
+
|
|
63
|
+
def __call__(self, generalized_cit: abstract_cit_t):
|
|
64
|
+
self.T, self.N = {0: self.data_format.total_sample_size()}, self.data_format.number_of_variables()
|
|
65
|
+
self.cond_ind_test = WrapCIT_TigramiteIndexing(generalized_cit)
|
|
66
|
+
result = self.run(**self.runtime_args)
|
|
67
|
+
return result['graph']
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class WrapLPCMCI(LPCMCI):
|
|
71
|
+
def __init__(self, data_format: IManageData, lpcmci_obj_init_args: dict={}, lpcmci_obj_run_args: dict={}):
|
|
72
|
+
class PlaceholderTest:
|
|
73
|
+
def set_dataframe(self, not_a_dataframe):
|
|
74
|
+
pass
|
|
75
|
+
class PlaceholderDataframe:
|
|
76
|
+
def __init__(self):
|
|
77
|
+
self.var_names = None
|
|
78
|
+
self.T = None
|
|
79
|
+
self.N = None
|
|
80
|
+
super().__init__(dataframe=PlaceholderDataframe(), cond_ind_test=PlaceholderTest(), **lpcmci_obj_init_args)
|
|
81
|
+
self.data_format = data_format
|
|
82
|
+
self.runtime_args = lpcmci_obj_run_args
|
|
83
|
+
|
|
84
|
+
def __call__(self, generalized_cit: abstract_cit_t):
|
|
85
|
+
self.T, self.N = {0: self.data_format.total_sample_size()}, self.data_format.number_of_variables()
|
|
86
|
+
self.cond_ind_test = WrapCIT_TigramiteIndexing(generalized_cit)
|
|
87
|
+
result = self.run_lpcmci(**self.runtime_args)
|
|
88
|
+
return result['graph']
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def alg_pcmci(data_format: IManageData, mci_transition_callback: IHandleExplicitTransitionToMCI=None, pcmci_obj_init_args: dict={}, pcmci_obj_run_args: dict={}) -> abstract_cd_t:
|
|
92
|
+
"""
|
|
93
|
+
Get PCMCI [RNK+19]_ implementation from tigramite. Use with :py:class:`ControllerTimeseries<GLDF.hccd.ControllerTimeseries>`.
|
|
94
|
+
|
|
95
|
+
:param data_format: data manager
|
|
96
|
+
:type data_format: IManageData
|
|
97
|
+
:param mci_transition_callback: transition callback (typically :py:class:`IndependenceAtoms_TimeSeries<GLDF.independence_atoms.IndependenceAtoms_TimeSeries>`)
|
|
98
|
+
:type mci_transition_callback: IHandleExplicitTransitionToMCI
|
|
99
|
+
:param pcmci_obj_init_args: forwarded to tigramites PCMCI constructor
|
|
100
|
+
:type pcmci_obj_init_args: dict
|
|
101
|
+
:param pcmci_obj_run_args: forwarded to tigramites PCMCI.run_pcmci
|
|
102
|
+
:type pcmci_obj_run_args: dict
|
|
103
|
+
:return: PCMCI as abstract CD-algorithm.
|
|
104
|
+
:rtype: abstract_cd_t
|
|
105
|
+
"""
|
|
106
|
+
return WrapPCMCI(data_format=data_format, mode="PCMCI", mci_transition_callback=mci_transition_callback,
|
|
107
|
+
pcmci_obj_init_args=pcmci_obj_init_args, pcmci_obj_run_args=pcmci_obj_run_args)
|
|
108
|
+
|
|
109
|
+
def alg_pcmciplus(data_format: IManageData, mci_transition_callback: IHandleExplicitTransitionToMCI=None, pcmci_obj_init_args: dict={}, pcmci_obj_run_args: dict={}) -> abstract_cd_t:
|
|
110
|
+
"""
|
|
111
|
+
Get PCMCI+ [R20]_ implementation from tigramite. Use with :py:class:`ControllerTimeseries<GLDF.hccd.ControllerTimeseries>`.
|
|
112
|
+
|
|
113
|
+
:param data_format: data manager
|
|
114
|
+
:type data_format: IManageData
|
|
115
|
+
:param mci_transition_callback: transition callback (typically :py:class:`IndependenceAtoms_TimeSeries<GLDF.independence_atoms.IndependenceAtoms_TimeSeries>`)
|
|
116
|
+
:type mci_transition_callback: IHandleExplicitTransitionToMCI
|
|
117
|
+
:param pcmci_obj_init_args: forwarded to tigramites PCMCI constructor
|
|
118
|
+
:type pcmci_obj_init_args: dict
|
|
119
|
+
:param pcmci_obj_run_args: forwarded to tigramites PCMCI.run_pcmciplus
|
|
120
|
+
:type pcmci_obj_run_args: dict
|
|
121
|
+
:return: PCMCI+ as abstract CD-algorithm.
|
|
122
|
+
:rtype: abstract_cd_t
|
|
123
|
+
"""
|
|
124
|
+
return WrapPCMCI(data_format=data_format, mode="PCMCI+", mci_transition_callback=mci_transition_callback,
|
|
125
|
+
pcmci_obj_init_args=pcmci_obj_init_args, pcmci_obj_run_args=pcmci_obj_run_args)
|
|
126
|
+
|
|
127
|
+
def alg_lpcmci(data_format: IManageData, lpcmci_obj_init_args: dict={}, lpcmci_obj_run_args: dict={}) -> abstract_cd_t:
|
|
128
|
+
"""
|
|
129
|
+
Get LPCMCI [GR20]_ implementation from tigramite. Use with :py:class:`ControllerTimeseriesLPCMCI<GLDF.hccd.ControllerTimeseriesLPCMCI>`.
|
|
130
|
+
|
|
131
|
+
*Note: In this case a transition callback* :py:class:`IHandleExplicitTransitionToMCI<GLDF.hccd.IHandleExplicitTransitionToMCI>` *is*
|
|
132
|
+
*notified by the controller* :py:class:`ControllerTimeseriesLPCMCI<GLDF.hccd.ControllerTimeseriesLPCMCI>`\\ *.*
|
|
133
|
+
|
|
134
|
+
:param data_format: data manager
|
|
135
|
+
:type data_format: IManageData
|
|
136
|
+
:param lpcmci_obj_init_args: forwarded to tigramites LPCMCI constructor
|
|
137
|
+
:type lpcmci_obj_init_args: dict
|
|
138
|
+
:param lpcmci_obj_run_args: forwarded to tigramites LPCMCI.run_lpcmci
|
|
139
|
+
:type lpcmci_obj_run_args: dict
|
|
140
|
+
:return: LPCMCI as abstract CD-algorithm.
|
|
141
|
+
:rtype: abstract_cd_t
|
|
142
|
+
"""
|
|
143
|
+
return WrapLPCMCI(data_format=data_format, lpcmci_obj_init_args=lpcmci_obj_init_args, lpcmci_obj_run_args=lpcmci_obj_run_args)
|