Skip to content

Commit

Permalink
Merge pull request #127 from haraoka-screen/interpretability
Browse files Browse the repository at this point in the history
Add functions for the interpretability of graphs
  • Loading branch information
ikeuchi-screen authored Feb 15, 2024
2 parents f7d0201 + 0d36b44 commit eb27323
Show file tree
Hide file tree
Showing 34 changed files with 5,085 additions and 3,964 deletions.
Binary file modified docs/image/bootstrap_hist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/image/bottom_up_parce_hist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/image/longitudinal_hist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/image/longitudinal_scatter1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/image/longitudinal_scatter2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/image/multiple_dataset_hist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/image/rcd_hist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/image/var_hist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/image/varma_hist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
248 changes: 131 additions & 117 deletions docs/tutorial/bootstrap.rst

Large diffs are not rendered by default.

169 changes: 94 additions & 75 deletions docs/tutorial/bottom_up_parce.rst

Large diffs are not rendered by default.

519 changes: 293 additions & 226 deletions docs/tutorial/longitudinal.rst

Large diffs are not rendered by default.

220 changes: 95 additions & 125 deletions docs/tutorial/multiple_dataset.rst

Large diffs are not rendered by default.

279 changes: 121 additions & 158 deletions docs/tutorial/rcd.rst

Large diffs are not rendered by default.

449 changes: 253 additions & 196 deletions docs/tutorial/var.rst

Large diffs are not rendered by default.

574 changes: 345 additions & 229 deletions docs/tutorial/varma.rst

Large diffs are not rendered by default.

551 changes: 316 additions & 235 deletions examples/Bootstrap.ipynb

Large diffs are not rendered by default.

633 changes: 341 additions & 292 deletions examples/BottomUpParceLiNGAM.ipynb

Large diffs are not rendered by default.

535 changes: 264 additions & 271 deletions examples/LongitudinalLiNGAM.ipynb

Large diffs are not rendered by default.

1,267 changes: 657 additions & 610 deletions examples/MultiGroupDirectLiNGAM.ipynb

Large diffs are not rendered by default.

738 changes: 363 additions & 375 deletions examples/RCD.ipynb

Large diffs are not rendered by default.

384 changes: 205 additions & 179 deletions examples/TotalEffect.ipynb

Large diffs are not rendered by default.

955 changes: 518 additions & 437 deletions examples/VARLiNGAM.ipynb

Large diffs are not rendered by default.

972 changes: 560 additions & 412 deletions examples/VARMALiNGAM.ipynb

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions lingam/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import numpy as np
from sklearn.utils import check_array, resample

from .utils import find_all_paths
from .utils import find_all_paths, calculate_total_effect


class BootstrapMixin:
Expand Down Expand Up @@ -52,8 +52,8 @@ def bootstrap(self, X, n_sampling):
# Calculate total effects
for c, from_ in enumerate(self._causal_order):
for to in self._causal_order[c + 1 :]:
total_effects[i, to, from_] = self.estimate_total_effect(
resampled_X, from_, to
total_effects[i, to, from_] = calculate_total_effect(
self._adjacency_matrix, from_, to
)

resampled_indices.append(resampled_index)
Expand Down
58 changes: 54 additions & 4 deletions lingam/bottom_up_parce_lingam.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from .bootstrap import BootstrapResult
from .hsic import hsic_test_gamma
from .utils import predict_adaptive_lasso, f_correlation
from .utils import predict_adaptive_lasso, f_correlation, calculate_total_effect


class BottomUpParceLiNGAM:
Expand Down Expand Up @@ -453,6 +453,56 @@ def estimate_total_effect(self, X, from_index, to_index):

return coefs[0]

def estimate_total_effect2(self, from_index, to_index):
"""Estimate total effect using causal model.
Parameters
----------
from_index :
Index of source variable to estimate total effect.
to_index :
Index of destination variable to estimate total effect.
Returns
-------
total_effect : float
Estimated total effect.
"""
# Check from/to causal order
for i, order in enumerate(self._causal_order):
if hasattr(order, "__iter__") and from_index in order:
from_order = i
break
elif not hasattr(order, "__iter__") and int(from_index) == int(order):
from_order = i
break

for i, order in enumerate(self._causal_order):
if hasattr(order, "__iter__") and to_index in order:
to_order = i
break
elif not hasattr(order, "__iter__") and int(to_index) == int(order):
to_order = i
break

if from_order > to_order:
warnings.warn(
f"The estimated causal effect may be incorrect because "
f"the causal order of the destination variable (to_index={to_index}) "
f"is earlier than the source variable (from_index={from_index})."
)

# Check confounders
if True in np.isnan(self._adjacency_matrix[from_index]):
warnings.warn(
f"The estimated causal effect may be incorrect because "
f"the source variable (from_index={from_index}) is influenced by confounders."
)
return np.nan

effect = calculate_total_effect(self._adjacency_matrix, from_index, to_index)
return effect

def get_error_independence_p_values(self, X):
"""Calculate the p-value matrix of independence between error variables.
Expand Down Expand Up @@ -555,10 +605,10 @@ def bootstrap(self, X, n_sampling):
for from_item in from_:
total_effects[
i, to, from_item
] = self.estimate_total_effect(resampled_X, from_item, to)
] = self.estimate_total_effect2(from_item, to)
else:
total_effects[i, to, from_] = self.estimate_total_effect(
resampled_X, from_, to
total_effects[i, to, from_] = self.estimate_total_effect2(
from_, to
)

return BootstrapResult(adjacency_matrices, total_effects)
53 changes: 49 additions & 4 deletions lingam/longitudinal_lingam.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from .direct_lingam import DirectLiNGAM
from .hsic import hsic_test_gamma
from .utils import predict_adaptive_lasso, find_all_paths
from .utils import predict_adaptive_lasso, find_all_paths, calculate_total_effect


class LongitudinalLiNGAM:
Expand Down Expand Up @@ -151,13 +151,13 @@ def bootstrap(self, X_list, n_sampling, start_from_t=1):
for to in self._causal_orders[from_t][c + 1 :]:
total_effects[
i, to_t * self._p + to, from_t * self._p + from_
] = self.estimate_total_effect(resampled_X_t, from_t, from_, to_t, to)
] = self.estimate_total_effect2(from_t, from_, to_t, to)

for to_t in range(from_t + 1, self._T):
for to in self._causal_orders[to_t]:
total_effects[
i, to_t * self._p + to, from_t * self._p + from_
] = self.estimate_total_effect(resampled_X_t, from_t, from_, to_t, to)
] = self.estimate_total_effect2(from_t, from_, to_t, to)

return LongitudinalBootstrapResult(self._T, adjacency_matrices, total_effects)

Expand All @@ -166,7 +166,7 @@ def estimate_total_effect(self, X_t, from_t, from_index, to_t, to_index):
Parameters
----------
X_t : array-like, shape (n_samples, n_features)
X_t : array-like, shape (timepoint, n_samples, n_features)
Original data, where n_samples is the number of samples
and n_features is the number of features.
from _t :
Expand Down Expand Up @@ -220,6 +220,51 @@ def estimate_total_effect(self, X_t, from_t, from_index, to_t, to_index):

return coefs[0]

def estimate_total_effect2(self, from_t, from_index, to_t, to_index):
"""Estimate total effect using causal model.
Parameters
----------
from _t :
The timepoint of source variable.
from_index :
Index of source variable to estimate total effect.
to_t :
The timepoint of destination variable.
to_index :
Index of destination variable to estimate total effect.
Returns
-------
total_effect : float
Estimated total effect.
"""
# Check from/to causal order
if to_t == from_t:
from_order = self._causal_orders[to_t].index(from_index)
to_order = self._causal_orders[from_t].index(to_index)
if from_order > to_order:
warnings.warn(
f"The estimated causal effect may be incorrect because "
f"the causal order of the destination variable (to_t={to_t}, to_index={to_index}) "
f"is earlier than the source variable (from_t={from_t}, from_index={from_index})."
)
elif to_t < from_t:
warnings.warn(
f"The estimated causal effect may be incorrect because "
f"the causal order of the destination variable (to_t={to_t}) "
f"is earlier than the source variable (from_t={from_t})."
)

am = np.concatenate([*self._adjacency_matrices[to_t]], axis=1)
am = np.pad(am, [(0, am.shape[1] - am.shape[0]), (0, 0)])

from_index = from_index + self._p * (to_t - from_t)

effect = calculate_total_effect(am, from_index, to_index)

return effect

def get_error_independence_p_values(self):
"""Calculate the p-value matrix of independence between error variables.
Expand Down
36 changes: 34 additions & 2 deletions lingam/multi_group_direct_lingam.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .bootstrap import BootstrapResult
from .direct_lingam import DirectLiNGAM
from .hsic import hsic_test_gamma
from .utils import predict_adaptive_lasso
from .utils import predict_adaptive_lasso, calculate_total_effect


class MultiGroupDirectLiNGAM(DirectLiNGAM):
Expand Down Expand Up @@ -146,7 +146,7 @@ def bootstrap(self, X_list, n_sampling):
# Calculate total effects
for c, from_ in enumerate(self._causal_order):
for to in self._causal_order[c + 1 :]:
effects = self.estimate_total_effect(resampled_X_list, from_, to)
effects = self.estimate_total_effect2(from_, to)
for i, effect in enumerate(effects):
total_effects_list[i, n, to, from_] = effect

Expand Down Expand Up @@ -203,6 +203,38 @@ def estimate_total_effect(self, X_list, from_index, to_index):

return effects

def estimate_total_effect2(self, from_index, to_index):
"""Estimate total effect using causal model.
Parameters
----------
from_index :
Index of source variable to estimate total effect.
to_index :
Index of destination variable to estimate total effect.
Returns
-------
total_effect : float
Estimated total effect.
"""
# Check from/to causal order
from_order = self._causal_order.index(from_index)
to_order = self._causal_order.index(to_index)
if from_order > to_order:
warnings.warn(
f"The estimated causal effect may be incorrect because "
f"the causal order of the destination variable (to_index={to_index}) "
f"is earlier than the source variable (from_index={from_index})."
)

effects = []
for am in self._adjacency_matrices:
effect = calculate_total_effect(am, from_index, to_index)
effects.append(effect)

return effects

def get_error_independence_p_values(self, X_list):
"""Calculate the p-value matrix of independence between error variables.
Expand Down
43 changes: 41 additions & 2 deletions lingam/multi_group_rcd.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from .bootstrap import BootstrapResult
from .hsic import get_gram_matrix, get_kernel_width, hsic_test_gamma, hsic_teststat
from .utils import predict_adaptive_lasso, f_correlation
from .utils import predict_adaptive_lasso, f_correlation, calculate_total_effect


class MultiGroupRCD:
Expand Down Expand Up @@ -178,6 +178,45 @@ def estimate_total_effect(self, X_list, from_index, to_index):

return effects

def estimate_total_effect2(self, from_index, to_index):
"""Estimate total effect using causal model.
Parameters
----------
from_index :
Index of source variable to estimate total effect.
to_index :
Index of destination variable to estimate total effect.
Returns
-------
total_effect : float
Estimated total effect.
"""
# Check from/to ancestors
if to_index in self._ancestors_list[from_index]:
warnings.warn(
f"The estimated causal effect may be incorrect because "
f"the causal order of the destination variable (to_index={to_index}) "
f"is earlier than the source variable (from_index={from_index})."
)

effects = []
for am in self._adjacency_matrices:
# Check confounders
if True in np.isnan(am[from_index]):
warnings.warn(
f"The estimated causal effect may be incorrect because "
f"the source variable (from_index={from_index}) is influenced by confounders."
)
return effects

effect = calculate_total_effect(am, from_index, to_index)
effects.append(effect)

return effects


def get_error_independence_p_values(self, X_list):
"""Calculate the p-value matrix of independence between error variables.
Expand Down Expand Up @@ -258,7 +297,7 @@ def bootstrap(self, X_list, n_sampling):
# Calculate total effects
for to, ancestors in enumerate(self._ancestors_list):
for from_ in ancestors:
effects = self.estimate_total_effect(resampled_X_list, from_, to)
effects = self.estimate_total_effect2(from_, to)
for i, effect in enumerate(effects):
total_effects_list[i, n, to, from_] = effect

Expand Down
28 changes: 25 additions & 3 deletions lingam/rcd.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from .bootstrap import BootstrapResult
from .hsic import get_gram_matrix, get_kernel_width, hsic_test_gamma, hsic_teststat
from .utils import predict_adaptive_lasso, f_correlation
from .utils import predict_adaptive_lasso, f_correlation, calculate_total_effect


class RCD:
Expand Down Expand Up @@ -435,6 +435,28 @@ def estimate_total_effect(self, X, from_index, to_index):

return coefs[0]

def estimate_total_effect2(self, from_index, to_index):
# Check from/to ancestors
if to_index in self._ancestors_list[from_index]:
warnings.warn(
f"The estimated causal effect may be incorrect because "
f"the causal order of the destination variable (to_index={to_index}) "
f"is earlier than the source variable (from_index={from_index})."
)

# Check confounders
if True in np.isnan(self._adjacency_matrix[from_index]):
warnings.warn(
f"The estimated causal effect may be incorrect because "
f"the source variable (from_index={from_index}) is influenced by confounders."
)
return np.nan

effect = calculate_total_effect(self._adjacency_matrix, from_index, to_index)

return effect


def get_error_independence_p_values(self, X):
"""Calculate the p-value matrix of independence between error variables.
Expand Down Expand Up @@ -532,8 +554,8 @@ def bootstrap(self, X, n_sampling):
# Calculate total effects
for to, ancestors in enumerate(self._ancestors_list):
for from_ in ancestors:
total_effects[i, to, from_] = self.estimate_total_effect(
resampled_X, from_, to
total_effects[i, to, from_] = self.estimate_total_effect2(
from_, to
)

return BootstrapResult(adjacency_matrices, total_effects)
Loading

0 comments on commit eb27323

Please sign in to comment.