From 376f2bda99ab16ad6204e3f72bf899b55f2d9e52 Mon Sep 17 00:00:00 2001 From: jakobrunge Date: Sun, 27 Feb 2022 12:30:24 +0100 Subject: [PATCH 1/6] add graph construction function in causal_effects.py --- tigramite/causal_effects.py | 85 ++++++++++++++++++- .../toymodels/structural_causal_processes.py | 69 +++++++++++++-- ...orial_general_causal_effect_analysis.ipynb | 50 ++++++++++- 3 files changed, 193 insertions(+), 11 deletions(-) diff --git a/tigramite/causal_effects.py b/tigramite/causal_effects.py index 2063e547..8bec3813 100644 --- a/tigramite/causal_effects.py +++ b/tigramite/causal_effects.py @@ -2129,4 +2129,87 @@ def predict_wright_effect(self, X=predictor_array, **pred_params) predicted_array[index, iy] = predicted_vals.mean() - return predicted_array \ No newline at end of file + return predicted_array + + @staticmethod + def get_graph_from_dict(links, tau_max=None): + """Helper function to convert dictionary of links to graph array format. + + Parameters + --------- + links : dict + Dictionary of form {0:[((0, -1), coeff, func), ...], 1:[...], ...}. + Also format {0:[(0, -1), ...], 1:[...], ...} is allowed. + tau_max : int or None + Maximum lag. If None, the maximum lag in links is used. + + Returns + ------- + graph : array of shape (N, N, tau_max+1) + Matrix format of graph with 1 for true links and 0 else. + """ + + def _get_minmax_lag(links): + """Helper function to retrieve tau_min and tau_max from links. + """ + + N = len(links) + + # Get maximum time lag + min_lag = np.inf + max_lag = 0 + for j in range(N): + for link_props in links[j]: + if len(link_props) > 2: + var, lag = link_props[0] + coeff = link_props[1] + # func = link_props[2] + if coeff != 0.: + min_lag = min(min_lag, abs(lag)) + max_lag = max(max_lag, abs(lag)) + else: + var, lag = link_props + min_lag = min(min_lag, abs(lag)) + max_lag = max(max_lag, abs(lag)) + + return min_lag, max_lag + + N = len(links) + + # Get maximum time lag + min_lag, max_lag = _get_minmax_lag(links) + + # Set maximum lag + if tau_max is None: + tau_max = max_lag + else: + if max_lag > tau_max: + raise ValueError("tau_max is smaller than maximum lag = %d " + "found in links, use tau_max=None or larger " + "value" % max_lag) + + graph = np.zeros((N, N, tau_max + 1), dtype=' 2: + var, lag = link_props[0] + coeff = link_props[1] + if coeff != 0.: + graph[var, j, abs(lag)] = "-->" + if lag == 0: + graph[j, var, 0] = "<--" + else: + var, lag = link_props + graph[var, j, abs(lag)] = "-->" + if lag == 0: + graph[j, var, 0] = "<--" + + return graph + +if __name__ == '__main__': + + links = {0: [((0, 0), 0.9, None)], + 1: [((1, 0), 0.8, None), ((0, 0), 0.3, None)], + 2: [((2, 0), 0.7, None), ((1, 0), -0.2, None)], + } + print(CausalEffects.get_graph_from_dict(links, tau_max=None)) \ No newline at end of file diff --git a/tigramite/toymodels/structural_causal_processes.py b/tigramite/toymodels/structural_causal_processes.py index 1eb6dd77..66bafd4e 100644 --- a/tigramite/toymodels/structural_causal_processes.py +++ b/tigramite/toymodels/structural_causal_processes.py @@ -714,7 +714,7 @@ def structural_causal_process(links, T, noises=None, return data, nonstationary def _get_minmax_lag(links): - """Helper function to retrieve tau_min and tau_max from links + """Helper function to retrieve tau_min and tau_max from links. """ N = len(links) @@ -724,12 +724,18 @@ def _get_minmax_lag(links): max_lag = 0 for j in range(N): for link_props in links[j]: - var, lag = link_props[0] - coeff = link_props[1] - # func = link_props[2] - if coeff != 0.: + if len(link_props) > 2: + var, lag = link_props[0] + coeff = link_props[1] + # func = link_props[2] + if coeff != 0.: + min_lag = min(min_lag, abs(lag)) + max_lag = max(max_lag, abs(lag)) + else: + var, lag = link_props min_lag = min(min_lag, abs(lag)) - max_lag = max(max_lag, abs(lag)) + max_lag = max(max_lag, abs(lag)) + return min_lag, max_lag def _get_parents(links, exclude_contemp=False): @@ -767,6 +773,54 @@ def _get_children(parents): return children +def links_to_graph(links, tau_max=None): + """Helper function to convert dictionary of links to graph array format. + + Parameters + --------- + links : dict + Dictionary of form {0:[((0, -1), coeff, func), ...], 1:[...], ...}. + Also format {0:[(0, -1), ...], 1:[...], ...} is allowed. + tau_max : int or None + Maximum lag. If None, the maximum lag in links is used. + + Returns + ------- + graph : array of shape (N, N, tau_max+1) + Matrix format of graph with 1 for true links and 0 else. + """ + N = len(links) + + # Get maximum time lag + min_lag, max_lag = _get_minmax_lag(links) + + # Set maximum lag + if tau_max is None: + tau_max = max_lag + else: + if max_lag > tau_max: + raise ValueError("tau_max is smaller than maximum lag = %d " + "found in links, use tau_max=None or larger " + "value" % max_lag) + + graph = np.zeros((N, N, tau_max + 1), dtype=' 2: + var, lag = link_props[0] + coeff = link_props[1] + if coeff != 0.: + graph[var, j, abs(lag)] = "-->" + if lag == 0: + graph[j, var, 0] = "<--" + else: + var, lag = link_props + graph[var, j, abs(lag)] = "-->" + if lag == 0: + graph[j, var, 0] = "<--" + + return graph + class _Logger(object): """Class to append print output to a string which can be saved""" def __init__(self): @@ -792,3 +846,6 @@ def nonlin_f(x): return (x + 5. * x**2 * np.exp(-x**2 / 20.)) data, nonstat = structural_causal_process(links, T=100, noises=noises) print(data.shape) + + # Construct graph + print(links_to_graph(links)) diff --git a/tutorials/tigramite_tutorial_general_causal_effect_analysis.ipynb b/tutorials/tigramite_tutorial_general_causal_effect_analysis.ipynb index 3934e624..704c064d 100644 --- a/tutorials/tigramite_tutorial_general_causal_effect_analysis.ipynb +++ b/tutorials/tigramite_tutorial_general_causal_effect_analysis.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "scrolled": true }, @@ -389,7 +389,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -438,7 +438,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -471,7 +471,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -537,6 +537,48 @@ "The graph shown is the DAG extended to $\\tau_{\\max}=maxlag(X,Y,S)+graph.shape[2] - 1$ (here $\\tau_{\\max}=4$). The reasoning behind this choice is that the $\\mathbf{O}$-set consisting of the parents of $Y$ and $M$ can then be fully reconstructed and, hence, is a valid adjustment set. For the case with hidden variables, where the $\\mathbf{O}$-set also contains collider path nodes, there can be cases where the $\\mathbf{O}$-set is infinite and a theory to truncate the $\\mathbf{O}$-set is currently in development." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You may also use the static method ``CausalEffects.get_graph_from_dict(links, tau_max=None)`` to generate a graph from a link dictionary of the form ``{0:[(0, -1), ...], 1:[...], ...}``. Also the format ``{0:[((0, -1), coeff, func), ...], 1:[...], ...}`` is allowed which is used to generate toy model data in tigramite. The ``coeff`` and ``func`` parameters are ignored for the graph construction. The shape of ``graph`` will be ``(N, N, tau_max+1)`` where ``tau_max`` is either automatically inferred from the dictionary or can be given as an argument. If you want greate a non-timeseries DAG of shape ``(N, N)``, just provide links with zero-lag and then squeeze the graph: ``dag_graph=graph.squeeze()``.\n", + "\n", + "Since it's a static method, you do *not* need to intitialize ``CausalEffects`` beforehand!" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[['' '-->' '' '']\n", + " ['' '' '' '-->']\n", + " ['' '' '' '']]\n", + "\n", + " [['' '' '' '']\n", + " ['' '-->' '' '']\n", + " ['-->' '' '' '']]\n", + "\n", + " [['' '' '' '']\n", + " ['<--' '' '' '']\n", + " ['' '-->' '' '']]]\n" + ] + } + ], + "source": [ + "def lin_f(x): return x\n", + "links = {0: [(0, -1)],\n", + " 1: [(1, -1), (0, -3)],\n", + " 2: [(2, -1), (1, 0)],\n", + " }\n", + "graph = CausalEffects.get_graph_from_dict(links, tau_max=None)\n", + "print(graph)" + ] + }, { "cell_type": "markdown", "metadata": {}, From ff44e4eb1904dea7cb334924166eeb4b8bcb49c8 Mon Sep 17 00:00:00 2001 From: jakobrunge Date: Sun, 27 Feb 2022 12:49:35 +0100 Subject: [PATCH 2/6] add graph construction function in causal_effects.py --- tigramite/causal_effects.py | 57 +++++++++++++++++-- .../toymodels/structural_causal_processes.py | 4 ++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/tigramite/causal_effects.py b/tigramite/causal_effects.py index 8bec3813..069b0911 100644 --- a/tigramite/causal_effects.py +++ b/tigramite/causal_effects.py @@ -2208,8 +2208,57 @@ def _get_minmax_lag(links): if __name__ == '__main__': - links = {0: [((0, 0), 0.9, None)], - 1: [((1, 0), 0.8, None), ((0, 0), 0.3, None)], - 2: [((2, 0), 0.7, None), ((1, 0), -0.2, None)], + # Consider some toy data + import tigramite + import tigramite.toymodels.structural_causal_processes as toys + import tigramite.data_processing as pp + + import sklearn + from sklearn.linear_model import LinearRegression + + T = 1000 + def lin_f(x): return x + auto_coeff = 0.3 + coeff = 2. + links = { + 0: [((0, -1), auto_coeff, lin_f)], + 1: [((1, -1), auto_coeff, lin_f), ((0, -1), coeff, lin_f)], + 2: [((2, -1), auto_coeff, lin_f), ((1, 0), coeff, lin_f)], + } + data, nonstat = toys.structural_causal_process(links, T=T, + noises=None, seed=None) + dataframe = pp.DataFrame(data) + + # Construct expert knowledge graph from links here + links = {0: [(0, -1)], + 1: [(1, -1), (0, -1)], + 2: [(2, -1), (1, 0),], } - print(CausalEffects.get_graph_from_dict(links, tau_max=None)) \ No newline at end of file + # Use staticmethod to get graph + graph = CausalEffects.get_graph_from_dict(links, tau_max=None) + + # We are interested in lagged total effect of X on Y + X = [(0, -1)] + Y = [(2, 0)] + + # Initialize class as `stationary_dag` + causal_effects = CausalEffects(graph, graph_type='stationary_dag', + X=X, Y=Y, S=None, + hidden_variables=None, + verbosity=0) + + # Optimal adjustment set (is used by default) + print(causal_effects.get_optimal_set()) + + # Fit causal effect model from observational data + causal_effects.fit_total_effect( + dataframe=dataframe, + estimator=LinearRegression(), + ) + + # Predict effect of interventions do(X=0.), ..., do(X=1.) in one go + dox_vals = np.linspace(0., 1., 5) + intervention_data = dox_vals.reshape(len(dox_vals), len(X)) + pred_Y = causal_effects.predict_total_effect( + intervention_data=intervention_data) + print(pred_Y) \ No newline at end of file diff --git a/tigramite/toymodels/structural_causal_processes.py b/tigramite/toymodels/structural_causal_processes.py index 66bafd4e..f73d87c5 100644 --- a/tigramite/toymodels/structural_causal_processes.py +++ b/tigramite/toymodels/structural_causal_processes.py @@ -848,4 +848,8 @@ def nonlin_f(x): return (x + 5. * x**2 * np.exp(-x**2 / 20.)) print(data.shape) # Construct graph + # links = {0: [(0, -1)], + # 1: [(1, -1), (0, -1)], + # 2: [(2, -1), (1, 0),], + # } print(links_to_graph(links)) From 0c3e13381791458281df36f9a287296cbf833033 Mon Sep 17 00:00:00 2001 From: jakobrunge Date: Mon, 28 Feb 2022 13:18:18 +0100 Subject: [PATCH 3/6] add graph construction function in causal_effects.py --- tigramite/causal_effects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tigramite/causal_effects.py b/tigramite/causal_effects.py index 069b0911..1054d2e2 100644 --- a/tigramite/causal_effects.py +++ b/tigramite/causal_effects.py @@ -2099,7 +2099,7 @@ def predict_wright_effect(self, predicted_array = np.zeros((intervention_T, lenY)) pred_dict = {} - for iy, y in enumerate(self.Y): + for iy, y in enumerate(self.listY): # Print message if self.verbosity > 1: print("\n## Predicting target %s" % str(y)) From d5a91aa632f2d4c290a997e888864dd22bd32d95 Mon Sep 17 00:00:00 2001 From: jakobrunge Date: Mon, 28 Feb 2022 13:21:31 +0100 Subject: [PATCH 4/6] add graph construction function in causal_effects.py --- tigramite/causal_effects.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tigramite/causal_effects.py b/tigramite/causal_effects.py index 1054d2e2..a2a3d01f 100644 --- a/tigramite/causal_effects.py +++ b/tigramite/causal_effects.py @@ -1870,11 +1870,11 @@ def predict_total_effect(self, Results from prediction: an array of shape (time, len(Y)). """ - if intervention_data.shape[1] != len(self.X): + if intervention_data.shape[1] != len(self.listX): raise ValueError("intervention_data.shape[1] must be len(X).") if conditions_data is not None: - if conditions_data.shape[1] != len(self.S): + if conditions_data.shape[1] != len(self.listS): raise ValueError("conditions_data.shape[1] must be len(S).") if conditions_data.shape[0] != intervention_data.shape[0]: raise ValueError("conditions_data.shape[0] must match intervention_data.shape[0].") @@ -1882,7 +1882,7 @@ def predict_total_effect(self, if self.no_causal_path: if self.verbosity > 0: print("No causal path from X to Y exists.") - return np.zeros((len(intervention_data), len(self.Y))) + return np.zeros((len(intervention_data), len(self.listY))) effect = self.model.get_general_prediction( intervention_data=intervention_data, @@ -2095,7 +2095,7 @@ def predict_wright_effect(self, intervention_T, lenX = intervention_data.shape - lenY = len(self.Y) + lenY = len(self.listY) predicted_array = np.zeros((intervention_T, lenY)) pred_dict = {} From 753c958364776206e45ab54fb66039084c82924e Mon Sep 17 00:00:00 2001 From: jakobrunge Date: Mon, 28 Feb 2022 20:51:36 +0100 Subject: [PATCH 5/6] add graph construction function in causal_effects.py --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 00320ca1..840677d1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Tigramite – Causal discovery for time series datasets +# Tigramite – Causal inference and causal discovery for time series datasets Version 5.0 (Python Package) @@ -32,7 +32,7 @@ Further, Tigramite provides several causal discovery methods that can be used un ## General Notes -Tigramite is a causal time series analysis python package. It allows to efficiently estimate causal graphs from high-dimensional time series datasets (causal discovery) and to use these graphs for robust forecasting and the estimation and prediction of direct, total, and mediated effects. Causal discovery is based on linear as well as non-parametric conditional independence tests applicable to discrete or continuously-valued time series. Also includes functions for high-quality plots of the results. You can find a simple GUI version on https://github.com/stbachinger/TigramiteGui. Please cite the following papers depending on which method you use: +Tigramite is a causal time series analysis python package. It allows to efficiently estimate causal graphs from high-dimensional time series datasets (causal discovery) and to use graphs for robust forecasting and the estimation and prediction of direct, total, and mediated effects. Causal discovery is based on linear as well as non-parametric conditional independence tests applicable to discrete or continuously-valued time series. Also includes functions for high-quality plots of the results. You can find a simple GUI version on https://github.com/stbachinger/TigramiteGui. Please cite the following papers depending on which method you use: - PCMCI: J. Runge, P. Nowack, M. Kretschmer, S. Flaxman, D. Sejdinovic, Detecting and quantifying causal associations in large nonlinear time series datasets. Sci. Adv. 5, eaau4996 (2019). https://advances.sciencemag.org/content/5/11/eaau4996 - PCMCI+: J. Runge (2020): Discovering contemporaneous and lagged causal relations in autocorrelated nonlinear time series datasets. Proceedings of the 36th Conference on Uncertainty in Artificial Intelligence, UAI 2020,Toronto, Canada, 2019, AUAI Press, 2020. http://auai.org/uai2020/proceedings/579_main_paper.pdf From 16050c3add99b1f6022e7302d5538f590c1762b9 Mon Sep 17 00:00:00 2001 From: jakobrunge Date: Wed, 9 Mar 2022 19:09:08 +0100 Subject: [PATCH 6/6] fix bug regarding causal_effects.py and masking --- tigramite/causal_effects.py | 7 +++-- tigramite/data_processing.py | 30 ++++++++++++------- tigramite/models.py | 10 ++++--- ...orial_general_causal_effect_analysis.ipynb | 3 +- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/tigramite/causal_effects.py b/tigramite/causal_effects.py index a2a3d01f..11631ae2 100644 --- a/tigramite/causal_effects.py +++ b/tigramite/causal_effects.py @@ -2216,7 +2216,7 @@ def _get_minmax_lag(links): import sklearn from sklearn.linear_model import LinearRegression - T = 1000 + T = 10000 def lin_f(x): return x auto_coeff = 0.3 coeff = 2. @@ -2226,7 +2226,7 @@ def lin_f(x): return x 2: [((2, -1), auto_coeff, lin_f), ((1, 0), coeff, lin_f)], } data, nonstat = toys.structural_causal_process(links, T=T, - noises=None, seed=None) + noises=None, seed=7) dataframe = pp.DataFrame(data) # Construct expert knowledge graph from links here @@ -2245,7 +2245,7 @@ def lin_f(x): return x causal_effects = CausalEffects(graph, graph_type='stationary_dag', X=X, Y=Y, S=None, hidden_variables=None, - verbosity=0) + verbosity=1) # Optimal adjustment set (is used by default) print(causal_effects.get_optimal_set()) @@ -2253,6 +2253,7 @@ def lin_f(x): return x # Fit causal effect model from observational data causal_effects.fit_total_effect( dataframe=dataframe, + # mask_type='y', estimator=LinearRegression(), ) diff --git a/tigramite/data_processing.py b/tigramite/data_processing.py index 21d30cfb..f795a1e9 100644 --- a/tigramite/data_processing.py +++ b/tigramite/data_processing.py @@ -114,6 +114,7 @@ def _check_mask(self, mask=None, require_mask=False): % str(_use_mask.shape)) def construct_array(self, X, Y, Z, tau_max, + extraZ=None, mask=None, mask_type=None, return_cleaned_xyz=False, @@ -127,10 +128,10 @@ def construct_array(self, X, Y, Z, tau_max, Parameters ---------- - X, Y, Z : list of tuples + X, Y, Z, extraZ : list of tuples For a dependence measure I(X;Y|Z), X, Y, Z can be multivariate of the form [(var1, -lag), (var2, -lag), ...]. At least one varlag in Y - has to be at lag zero. + has to be at lag zero. extraZ is only used in CausalEffects class. tau_max : int Maximum time lag. This may be used to make sure that estimates for different lags in X and Z all have the same sample size. @@ -171,16 +172,20 @@ def construct_array(self, X, Y, Z, tau_max, # Get the length in time and the number of nodes T, N = self.values.shape - # Remove duplicates in X, Y, Z + if extraZ is None: + extraZ = [] + # Remove duplicates in X, Y, Z, extraZ X = list(OrderedDict.fromkeys(X)) Y = list(OrderedDict.fromkeys(Y)) Z = list(OrderedDict.fromkeys(Z)) + extraZ = list(OrderedDict.fromkeys(extraZ)) # If a node in Z occurs already in X or Y, remove it from Z Z = [node for node in Z if (node not in X) and (node not in Y)] + extraZ = [node for node in extraZ if (node not in X) and (node not in Y) and (node not in Z)] # Check that all lags are non-positive and indices are in [0,N-1] - XYZ = X + Y + Z + XYZ = X + Y + Z + extraZ dim = len(XYZ) # Ensure that XYZ makes sense @@ -200,9 +205,10 @@ def construct_array(self, X, Y, Z, tau_max, # Setup XYZ identifier index_code = {'x' : 0, 'y' : 1, - 'z' : 2} + 'z' : 2, + 'e' : 3} xyz = np.array([index_code[name] - for var, name in zip([X, Y, Z], ['x', 'y', 'z']) + for var, name in zip([X, Y, Z, extraZ], ['x', 'y', 'z', 'e']) for _ in var]) # Setup and fill array with lagged time series @@ -250,7 +256,7 @@ def construct_array(self, X, Y, Z, tau_max, array_mask[i, :] = (_use_mask[self.bootstrap + lag, var] == False) # Iterate over defined mapping from letter index to number index, - # i.e. 'x' -> 0, 'y' -> 1, 'z'-> 2 + # i.e. 'x' -> 0, 'y' -> 1, 'z'-> 2, 'e'-> 3 for idx, cde in index_code.items(): # Check if the letter index is in the mask type if (mask_type is not None) and (idx in mask_type): @@ -268,7 +274,7 @@ def construct_array(self, X, Y, Z, tau_max, # Print information about the constructed array if verbosity > 2: - self.print_array_info(array, X, Y, Z, self.missing_flag, mask_type) + self.print_array_info(array, X, Y, Z, self.missing_flag, mask_type, extraZ) # Return the array and xyz and optionally (X, Y, Z) if return_cleaned_xyz: @@ -310,7 +316,7 @@ def _check_nodes(self, Y, XYZ, N, dim): # raise ValueError("Y-nodes are %s, " % str(Y) + # "but one of the Y-nodes must have zero lag") - def print_array_info(self, array, X, Y, Z, missing_flag, mask_type): + def print_array_info(self, array, X, Y, Z, missing_flag, mask_type, extraZ=None): """ Print info about the constructed array @@ -318,7 +324,7 @@ def print_array_info(self, array, X, Y, Z, missing_flag, mask_type): ---------- array : Data array of shape (dim, T) Data array. - X, Y, Z : list of tuples + X, Y, Z, extraZ : list of tuples For a dependence measure I(X;Y|Z), Y is of the form [(varY, 0)], where var specifies the variable index. X typically is of the form [(varX, -tau)] with tau denoting the time lag and Z can be @@ -333,11 +339,15 @@ def print_array_info(self, array, X, Y, Z, missing_flag, mask_type): measure I(X; Y | Z) the samples should be masked. If None, the mask is not used. Explained in tutorial on masking and missing values. """ + if extraZ is None: + extraZ = [] indt = " " * 12 print(indt + "Constructed array of shape %s from"%str(array.shape) + "\n" + indt + "X = %s" % str(X) + "\n" + indt + "Y = %s" % str(Y) + "\n" + indt + "Z = %s" % str(Z)) + if extraZ is not None: + print(indt + "extraZ = %s" % str(extraZ)) if self.mask is not None and mask_type is not None: print(indt+"with masked samples in %s removed" % mask_type) if self.missing_flag is not None: diff --git a/tigramite/models.py b/tigramite/models.py index 96fdd6e3..02fd225f 100644 --- a/tigramite/models.py +++ b/tigramite/models.py @@ -154,7 +154,9 @@ def get_general_fitted_model(self, # Construct array of shape (var, time) with first entry being # a dummy, second is y followed by joint X and Z (ignore the notation in construct_array) array, xyz = \ - self.dataframe.construct_array(X=self.X, Y=[y] + self.Z, Z=self.conditions, + self.dataframe.construct_array(X=self.X, Y=[y], # + self.Z, + Z=self.conditions, + extraZ=self.Z, tau_max=self.tau_max, mask_type=self.mask_type, cut_off=self.cut_off, @@ -170,8 +172,8 @@ def get_general_fitted_model(self, a_model = deepcopy(self.model) predictor_indices = list(np.where(xyz==0)[0]) \ - + list(np.where(xyz==1)[0][1:]) \ - + list(np.where(xyz==2)[0]) + + list(np.where(xyz==2)[0]) \ + + list(np.where(xyz==3)[0]) predictor_array = array[predictor_indices, :].T # Target is only first entry of Y, ie [y] target_array = array[np.where(xyz==1)[0][0], :] @@ -255,7 +257,7 @@ def get_general_prediction(self, conditions_data = a_transform.transform(X=conditions_data) # Extract observational Z from stored array - z_indices = list(np.where(self.fit_results[y]['xyz']==1)[0][1:]) + z_indices = list(np.where(self.fit_results[y]['xyz']==3)[0]) z_array = self.fit_results[y]['observation_array'][z_indices, :].T Tobs = len(z_array) diff --git a/tutorials/tigramite_tutorial_general_causal_effect_analysis.ipynb b/tutorials/tigramite_tutorial_general_causal_effect_analysis.ipynb index 704c064d..bfab242b 100644 --- a/tutorials/tigramite_tutorial_general_causal_effect_analysis.ipynb +++ b/tutorials/tigramite_tutorial_general_causal_effect_analysis.ipynb @@ -262,7 +262,8 @@ "MCE = \\sum_{\\text{causal paths through at least one $M\\in M^*$}} \\prod_{\\text{link $i\\to j$ in path}} \\beta_{i\\to j}\n", "$$\n", "\n", - "This is also implemented in the class through the parameter ``mediation``. For ``mediation='direct'`` only the __direct effect__ in the coefficient $\\beta_{X\\to Y}$, if non-zero, is returned. \n", + "This is also implemented in the class through the parameter ``mediation``. For ``mediation='direct'`` only the __direct effect__ in the coefficient $\\beta_{X\\to Y}$, if non-zero, is returned.\n", + "\n", "\n", "\n", "## Types of graphs describing qualitative causal knowledge\n",