fast-causal-shap 0.1.0__tar.gz → 0.1.2__tar.gz
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.
Potentially problematic release.
This version of fast-causal-shap might be problematic. Click here for more details.
- fast_causal_shap-0.1.2/PKG-INFO +96 -0
- fast_causal_shap-0.1.2/README.md +79 -0
- fast_causal_shap-0.1.2/fast_causal_shap.egg-info/PKG-INFO +96 -0
- {fast_causal_shap-0.1.0 → fast_causal_shap-0.1.2}/fast_causal_shap.egg-info/SOURCES.txt +2 -1
- {fast_causal_shap-0.1.0 → fast_causal_shap-0.1.2}/pyproject.toml +2 -2
- fast_causal_shap-0.1.2/tests/test_core.py +356 -0
- fast_causal_shap-0.1.0/PKG-INFO +0 -50
- fast_causal_shap-0.1.0/README.md +0 -33
- fast_causal_shap-0.1.0/fast_causal_shap.egg-info/PKG-INFO +0 -50
- {fast_causal_shap-0.1.0 → fast_causal_shap-0.1.2}/LICENSE +0 -0
- {fast_causal_shap-0.1.0 → fast_causal_shap-0.1.2}/fast_causal_shap/__init__.py +0 -0
- {fast_causal_shap-0.1.0 → fast_causal_shap-0.1.2}/fast_causal_shap/core.py +0 -0
- {fast_causal_shap-0.1.0 → fast_causal_shap-0.1.2}/fast_causal_shap.egg-info/dependency_links.txt +0 -0
- {fast_causal_shap-0.1.0 → fast_causal_shap-0.1.2}/fast_causal_shap.egg-info/requires.txt +0 -0
- {fast_causal_shap-0.1.0 → fast_causal_shap-0.1.2}/fast_causal_shap.egg-info/top_level.txt +0 -0
- {fast_causal_shap-0.1.0 → fast_causal_shap-0.1.2}/setup.cfg +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fast-causal-shap
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A Python package for efficient causal SHAP computations
|
|
5
|
+
Author-email: woonyee28 <ngnwy289@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/woonyee28/CausalSHAP
|
|
8
|
+
Project-URL: Issues, https://github.com/woonyee28/CausalSHAP/issues
|
|
9
|
+
Requires-Python: >=3.7
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: pandas>=1.0.0
|
|
13
|
+
Requires-Dist: networkx>=2.0
|
|
14
|
+
Requires-Dist: numpy>=1.18.0
|
|
15
|
+
Requires-Dist: scikit-learn>=0.24.0
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# Fast Causal SHAP
|
|
19
|
+
|
|
20
|
+
Fast Causal SHAP is a Python package designed for efficient and interpretable SHAP value computation in causal inference tasks. It integrates seamlessly with various causal inference frameworks and enables feature attribution with awareness of causal dependencies.
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- Fast computation of SHAP values for causal models
|
|
25
|
+
- Support for multiple causal inference frameworks
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
Install the stable version via PyPI:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install fast-causal-shap
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or, for the latest development version:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install git+https://github.com/woonyee28/CausalSHAP.git
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
```
|
|
43
|
+
from fast_causal_shap.core import FastCausalSHAP
|
|
44
|
+
|
|
45
|
+
# Predict probabilities and assign to training data
|
|
46
|
+
predicted_probabilities = model.predict_proba(X_train)[:,1]
|
|
47
|
+
X_train['target'] = predicted_probabilities
|
|
48
|
+
|
|
49
|
+
# Initialize FastCausalInference
|
|
50
|
+
ci = FastCausalInference(data=X_train, model=model, target_variable='target')
|
|
51
|
+
|
|
52
|
+
# Load causal strengths (precomputed using R packages)
|
|
53
|
+
ci.load_causal_strengths(result_dir + 'Causal_Effect.json')
|
|
54
|
+
|
|
55
|
+
# Compute modified SHAP values for a single instance
|
|
56
|
+
x_instance = X_train.iloc[33]
|
|
57
|
+
|
|
58
|
+
print(ci.compute_modified_shap_proba(x_instance, is_classifier=True))
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Format of the Causal_Effect.json:
|
|
62
|
+
```
|
|
63
|
+
[
|
|
64
|
+
{
|
|
65
|
+
"Pair": "Bacteroidia->Clostridia",
|
|
66
|
+
"Mean_Causal_Effect": 0.71292
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"Pair": "Clostridia->Alphaproteobacteria",
|
|
70
|
+
"Mean_Causal_Effect": 0.37652
|
|
71
|
+
}, ......
|
|
72
|
+
]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Fast Causal SHAP supports integration with structural algorithms such as:
|
|
76
|
+
1. Peter-Clarke (PC) Algorithm
|
|
77
|
+
2. IDA Algorithm
|
|
78
|
+
3. Fast Causal Inference (FCI) Algorithm
|
|
79
|
+
You can find example R code for these integrations here: [FastCausalSHAP R code examples](https://github.com/woonyee28/CausalSHAP/tree/main/code/r)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## Citation
|
|
83
|
+
If you use Fast Causal SHAP in your research, please cite:
|
|
84
|
+
```
|
|
85
|
+
@inproceedings{ng2025causal,
|
|
86
|
+
title={Causal SHAP: Feature Attribution with Dependency Awareness through Causal Discovery},
|
|
87
|
+
author={Ng, Woon Yee and Wang, Li Rong and Liu, Siyuan and Fan, Xiuyi},
|
|
88
|
+
booktitle={Proceedings of the International Joint Conference on Neural Networks (IJCNN)},
|
|
89
|
+
year={2025},
|
|
90
|
+
organization={IEEE}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
This project is licensed under the MIT License.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Fast Causal SHAP
|
|
2
|
+
|
|
3
|
+
Fast Causal SHAP is a Python package designed for efficient and interpretable SHAP value computation in causal inference tasks. It integrates seamlessly with various causal inference frameworks and enables feature attribution with awareness of causal dependencies.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Fast computation of SHAP values for causal models
|
|
8
|
+
- Support for multiple causal inference frameworks
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Install the stable version via PyPI:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install fast-causal-shap
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or, for the latest development version:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install git+https://github.com/woonyee28/CausalSHAP.git
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
```
|
|
26
|
+
from fast_causal_shap.core import FastCausalSHAP
|
|
27
|
+
|
|
28
|
+
# Predict probabilities and assign to training data
|
|
29
|
+
predicted_probabilities = model.predict_proba(X_train)[:,1]
|
|
30
|
+
X_train['target'] = predicted_probabilities
|
|
31
|
+
|
|
32
|
+
# Initialize FastCausalInference
|
|
33
|
+
ci = FastCausalInference(data=X_train, model=model, target_variable='target')
|
|
34
|
+
|
|
35
|
+
# Load causal strengths (precomputed using R packages)
|
|
36
|
+
ci.load_causal_strengths(result_dir + 'Causal_Effect.json')
|
|
37
|
+
|
|
38
|
+
# Compute modified SHAP values for a single instance
|
|
39
|
+
x_instance = X_train.iloc[33]
|
|
40
|
+
|
|
41
|
+
print(ci.compute_modified_shap_proba(x_instance, is_classifier=True))
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Format of the Causal_Effect.json:
|
|
45
|
+
```
|
|
46
|
+
[
|
|
47
|
+
{
|
|
48
|
+
"Pair": "Bacteroidia->Clostridia",
|
|
49
|
+
"Mean_Causal_Effect": 0.71292
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"Pair": "Clostridia->Alphaproteobacteria",
|
|
53
|
+
"Mean_Causal_Effect": 0.37652
|
|
54
|
+
}, ......
|
|
55
|
+
]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Fast Causal SHAP supports integration with structural algorithms such as:
|
|
59
|
+
1. Peter-Clarke (PC) Algorithm
|
|
60
|
+
2. IDA Algorithm
|
|
61
|
+
3. Fast Causal Inference (FCI) Algorithm
|
|
62
|
+
You can find example R code for these integrations here: [FastCausalSHAP R code examples](https://github.com/woonyee28/CausalSHAP/tree/main/code/r)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Citation
|
|
66
|
+
If you use Fast Causal SHAP in your research, please cite:
|
|
67
|
+
```
|
|
68
|
+
@inproceedings{ng2025causal,
|
|
69
|
+
title={Causal SHAP: Feature Attribution with Dependency Awareness through Causal Discovery},
|
|
70
|
+
author={Ng, Woon Yee and Wang, Li Rong and Liu, Siyuan and Fan, Xiuyi},
|
|
71
|
+
booktitle={Proceedings of the International Joint Conference on Neural Networks (IJCNN)},
|
|
72
|
+
year={2025},
|
|
73
|
+
organization={IEEE}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
This project is licensed under the MIT License.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fast-causal-shap
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A Python package for efficient causal SHAP computations
|
|
5
|
+
Author-email: woonyee28 <ngnwy289@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/woonyee28/CausalSHAP
|
|
8
|
+
Project-URL: Issues, https://github.com/woonyee28/CausalSHAP/issues
|
|
9
|
+
Requires-Python: >=3.7
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: pandas>=1.0.0
|
|
13
|
+
Requires-Dist: networkx>=2.0
|
|
14
|
+
Requires-Dist: numpy>=1.18.0
|
|
15
|
+
Requires-Dist: scikit-learn>=0.24.0
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# Fast Causal SHAP
|
|
19
|
+
|
|
20
|
+
Fast Causal SHAP is a Python package designed for efficient and interpretable SHAP value computation in causal inference tasks. It integrates seamlessly with various causal inference frameworks and enables feature attribution with awareness of causal dependencies.
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- Fast computation of SHAP values for causal models
|
|
25
|
+
- Support for multiple causal inference frameworks
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
Install the stable version via PyPI:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install fast-causal-shap
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or, for the latest development version:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install git+https://github.com/woonyee28/CausalSHAP.git
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
```
|
|
43
|
+
from fast_causal_shap.core import FastCausalSHAP
|
|
44
|
+
|
|
45
|
+
# Predict probabilities and assign to training data
|
|
46
|
+
predicted_probabilities = model.predict_proba(X_train)[:,1]
|
|
47
|
+
X_train['target'] = predicted_probabilities
|
|
48
|
+
|
|
49
|
+
# Initialize FastCausalInference
|
|
50
|
+
ci = FastCausalInference(data=X_train, model=model, target_variable='target')
|
|
51
|
+
|
|
52
|
+
# Load causal strengths (precomputed using R packages)
|
|
53
|
+
ci.load_causal_strengths(result_dir + 'Causal_Effect.json')
|
|
54
|
+
|
|
55
|
+
# Compute modified SHAP values for a single instance
|
|
56
|
+
x_instance = X_train.iloc[33]
|
|
57
|
+
|
|
58
|
+
print(ci.compute_modified_shap_proba(x_instance, is_classifier=True))
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Format of the Causal_Effect.json:
|
|
62
|
+
```
|
|
63
|
+
[
|
|
64
|
+
{
|
|
65
|
+
"Pair": "Bacteroidia->Clostridia",
|
|
66
|
+
"Mean_Causal_Effect": 0.71292
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"Pair": "Clostridia->Alphaproteobacteria",
|
|
70
|
+
"Mean_Causal_Effect": 0.37652
|
|
71
|
+
}, ......
|
|
72
|
+
]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Fast Causal SHAP supports integration with structural algorithms such as:
|
|
76
|
+
1. Peter-Clarke (PC) Algorithm
|
|
77
|
+
2. IDA Algorithm
|
|
78
|
+
3. Fast Causal Inference (FCI) Algorithm
|
|
79
|
+
You can find example R code for these integrations here: [FastCausalSHAP R code examples](https://github.com/woonyee28/CausalSHAP/tree/main/code/r)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## Citation
|
|
83
|
+
If you use Fast Causal SHAP in your research, please cite:
|
|
84
|
+
```
|
|
85
|
+
@inproceedings{ng2025causal,
|
|
86
|
+
title={Causal SHAP: Feature Attribution with Dependency Awareness through Causal Discovery},
|
|
87
|
+
author={Ng, Woon Yee and Wang, Li Rong and Liu, Siyuan and Fan, Xiuyi},
|
|
88
|
+
booktitle={Proceedings of the International Joint Conference on Neural Networks (IJCNN)},
|
|
89
|
+
year={2025},
|
|
90
|
+
organization={IEEE}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
This project is licensed under the MIT License.
|
|
@@ -7,4 +7,5 @@ fast_causal_shap.egg-info/PKG-INFO
|
|
|
7
7
|
fast_causal_shap.egg-info/SOURCES.txt
|
|
8
8
|
fast_causal_shap.egg-info/dependency_links.txt
|
|
9
9
|
fast_causal_shap.egg-info/requires.txt
|
|
10
|
-
fast_causal_shap.egg-info/top_level.txt
|
|
10
|
+
fast_causal_shap.egg-info/top_level.txt
|
|
11
|
+
tests/test_core.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fast-causal-shap"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "A Python package for efficient causal SHAP computations"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.7"
|
|
@@ -21,4 +21,4 @@ dependencies = [
|
|
|
21
21
|
|
|
22
22
|
[project.urls]
|
|
23
23
|
Homepage = "https://github.com/woonyee28/CausalSHAP"
|
|
24
|
-
Issues = "https://github.com/woonyee28/CausalSHAP/issues"
|
|
24
|
+
Issues = "https://github.com/woonyee28/CausalSHAP/issues"
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import networkx as nx
|
|
5
|
+
import json
|
|
6
|
+
import tempfile
|
|
7
|
+
import os
|
|
8
|
+
from sklearn.linear_model import LinearRegression
|
|
9
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
10
|
+
from sklearn.datasets import make_classification, make_regression
|
|
11
|
+
|
|
12
|
+
from fast_causal_shap.core import FastCausalSHAP
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestFastCausalSHAP(unittest.TestCase):
|
|
16
|
+
|
|
17
|
+
def setUp(self):
|
|
18
|
+
"""Set up test fixtures before each test method."""
|
|
19
|
+
# Create sample data for testing
|
|
20
|
+
np.random.seed(42)
|
|
21
|
+
|
|
22
|
+
# Create synthetic regression data
|
|
23
|
+
X, y = make_regression(n_samples=100, n_features=4, noise=0.1, random_state=42)
|
|
24
|
+
self.regression_data = pd.DataFrame(X, columns=['feature1', 'feature2', 'feature3', 'feature4'])
|
|
25
|
+
self.regression_data['target'] = y
|
|
26
|
+
|
|
27
|
+
# Create synthetic classification data
|
|
28
|
+
X_clf, y_clf = make_classification(n_samples=100, n_features=4, n_classes=2, random_state=42)
|
|
29
|
+
self.classification_data = pd.DataFrame(X_clf, columns=['feature1', 'feature2', 'feature3', 'feature4'])
|
|
30
|
+
self.classification_data['target'] = y_clf
|
|
31
|
+
|
|
32
|
+
# Create mock models
|
|
33
|
+
self.regression_model = LinearRegression()
|
|
34
|
+
self.regression_model.fit(self.regression_data[['feature1', 'feature2', 'feature3', 'feature4']],
|
|
35
|
+
self.regression_data['target'])
|
|
36
|
+
|
|
37
|
+
self.classification_model = RandomForestClassifier(random_state=42)
|
|
38
|
+
self.classification_model.fit(self.classification_data[['feature1', 'feature2', 'feature3', 'feature4']],
|
|
39
|
+
self.classification_data['target'])
|
|
40
|
+
|
|
41
|
+
# Create test instance
|
|
42
|
+
self.fast_causal_shap = FastCausalSHAP(
|
|
43
|
+
data=self.regression_data,
|
|
44
|
+
model=self.regression_model,
|
|
45
|
+
target_variable='target'
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Create sample causal effects for testing
|
|
49
|
+
self.sample_causal_effects = [
|
|
50
|
+
{"Pair": "feature1->target", "Mean_Causal_Effect": 0.5},
|
|
51
|
+
{"Pair": "feature2->target", "Mean_Causal_Effect": 0.3},
|
|
52
|
+
{"Pair": "feature3->feature1", "Mean_Causal_Effect": 0.2},
|
|
53
|
+
{"Pair": "feature4->feature2", "Mean_Causal_Effect": 0.1}
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
def _create_test_graph(self, edges=None):
|
|
57
|
+
"""Helper method to create a test graph with all nodes from data."""
|
|
58
|
+
G = nx.DiGraph()
|
|
59
|
+
all_nodes = self.regression_data.columns.tolist()
|
|
60
|
+
G.add_nodes_from(all_nodes)
|
|
61
|
+
if edges:
|
|
62
|
+
G.add_edges_from(edges)
|
|
63
|
+
return G
|
|
64
|
+
|
|
65
|
+
def test_init(self):
|
|
66
|
+
"""Test FastCausalSHAP initialization."""
|
|
67
|
+
fcs = FastCausalSHAP(
|
|
68
|
+
data=self.regression_data,
|
|
69
|
+
model=self.regression_model,
|
|
70
|
+
target_variable='target'
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
self.assertEqual(fcs.target_variable, 'target')
|
|
74
|
+
self.assertIsNone(fcs.gamma)
|
|
75
|
+
self.assertIsNone(fcs.ida_graph)
|
|
76
|
+
self.assertEqual(fcs.regression_models, {})
|
|
77
|
+
self.assertEqual(fcs.feature_depths, {})
|
|
78
|
+
self.assertEqual(fcs.path_cache, {})
|
|
79
|
+
self.assertEqual(fcs.causal_paths, {})
|
|
80
|
+
|
|
81
|
+
def test_load_causal_strengths(self):
|
|
82
|
+
"""Test loading causal strengths from JSON file.
|
|
83
|
+
|
|
84
|
+
This test verifies that the class can correctly load causal strengths from
|
|
85
|
+
a JSON file, compute the gamma values, and create the causal graph.
|
|
86
|
+
|
|
87
|
+
It tests the following:
|
|
88
|
+
- Loading the JSON file correctly.
|
|
89
|
+
- Graph construction from causal effects.
|
|
90
|
+
- Normalization of causal strengths. (gamma values should sum to 1)
|
|
91
|
+
- Proper handling of the ida_graph attribute.
|
|
92
|
+
"""
|
|
93
|
+
# Create temporary JSON file
|
|
94
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
95
|
+
json.dump(self.sample_causal_effects, f)
|
|
96
|
+
temp_file = f.name
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
# Test loading causal strengths
|
|
100
|
+
gamma = self.fast_causal_shap.load_causal_strengths(temp_file)
|
|
101
|
+
|
|
102
|
+
# Check that gamma is computed
|
|
103
|
+
self.assertIsInstance(gamma, dict)
|
|
104
|
+
self.assertIsNotNone(self.fast_causal_shap.gamma)
|
|
105
|
+
self.assertIsNotNone(self.fast_causal_shap.ida_graph)
|
|
106
|
+
|
|
107
|
+
# Check that graph is created
|
|
108
|
+
self.assertIsInstance(self.fast_causal_shap.ida_graph, nx.DiGraph)
|
|
109
|
+
|
|
110
|
+
# Check that gamma values sum to 1 (approximately)
|
|
111
|
+
total_gamma = sum(abs(v) for v in gamma.values())
|
|
112
|
+
self.assertAlmostEqual(total_gamma, 1.0, places=5)
|
|
113
|
+
|
|
114
|
+
finally:
|
|
115
|
+
os.unlink(temp_file)
|
|
116
|
+
|
|
117
|
+
def test_remove_cycles(self):
|
|
118
|
+
"""Test cycle removal functionality.
|
|
119
|
+
|
|
120
|
+
This test ensures that the class can detect and remove cycles from causal
|
|
121
|
+
graph to create a Directed Acyclic Graph (DAG).
|
|
122
|
+
"""
|
|
123
|
+
# Create a graph with cycles
|
|
124
|
+
G = nx.DiGraph()
|
|
125
|
+
G.add_edge('A', 'B', weight=0.5)
|
|
126
|
+
G.add_edge('B', 'C', weight=0.3)
|
|
127
|
+
G.add_edge('C', 'A', weight=0.1) # Creates a cycle
|
|
128
|
+
|
|
129
|
+
self.fast_causal_shap.ida_graph = G
|
|
130
|
+
|
|
131
|
+
# Test cycle removal
|
|
132
|
+
removed_edges = self.fast_causal_shap.remove_cycles()
|
|
133
|
+
|
|
134
|
+
# Check that cycles are removed
|
|
135
|
+
self.assertTrue(len(removed_edges) > 0)
|
|
136
|
+
self.assertTrue(nx.is_directed_acyclic_graph(self.fast_causal_shap.ida_graph))
|
|
137
|
+
|
|
138
|
+
def test_get_topological_order(self):
|
|
139
|
+
"""Test topological ordering."""
|
|
140
|
+
# Set up a simple DAG
|
|
141
|
+
G = nx.DiGraph()
|
|
142
|
+
G.add_edges_from([('feature1', 'feature2'), ('feature2', 'target')])
|
|
143
|
+
self.fast_causal_shap.ida_graph = G
|
|
144
|
+
|
|
145
|
+
# Test topological order
|
|
146
|
+
order = self.fast_causal_shap.get_topological_order([])
|
|
147
|
+
self.assertIsInstance(order, list)
|
|
148
|
+
|
|
149
|
+
# Test with intervention
|
|
150
|
+
order_intervened = self.fast_causal_shap.get_topological_order(['feature1'])
|
|
151
|
+
self.assertIsInstance(order_intervened, list)
|
|
152
|
+
|
|
153
|
+
def test_get_parents(self):
|
|
154
|
+
"""Test getting parent nodes."""
|
|
155
|
+
# Set up a simple graph
|
|
156
|
+
G = nx.DiGraph()
|
|
157
|
+
G.add_edges_from([('feature1', 'feature2'), ('feature3', 'feature2')])
|
|
158
|
+
self.fast_causal_shap.ida_graph = G
|
|
159
|
+
|
|
160
|
+
parents = self.fast_causal_shap.get_parents('feature2')
|
|
161
|
+
self.assertEqual(set(parents), {'feature1', 'feature3'})
|
|
162
|
+
|
|
163
|
+
parents_no_parent = self.fast_causal_shap.get_parents('feature1')
|
|
164
|
+
self.assertEqual(parents_no_parent, [])
|
|
165
|
+
|
|
166
|
+
def test_sample_marginal(self):
|
|
167
|
+
"""Test marginal sampling."""
|
|
168
|
+
sampled_value = self.fast_causal_shap.sample_marginal('feature1')
|
|
169
|
+
self.assertIsInstance(sampled_value, (int, float, np.number))
|
|
170
|
+
|
|
171
|
+
# Check that sampled value is within reasonable range
|
|
172
|
+
feature_values = self.regression_data['feature1']
|
|
173
|
+
self.assertGreaterEqual(sampled_value, feature_values.min())
|
|
174
|
+
self.assertLessEqual(sampled_value, feature_values.max())
|
|
175
|
+
|
|
176
|
+
def test_sample_conditional(self):
|
|
177
|
+
"""Test conditional sampling."""
|
|
178
|
+
# Set up a simple graph
|
|
179
|
+
G = nx.DiGraph()
|
|
180
|
+
G.add_edge('feature1', 'feature2')
|
|
181
|
+
self.fast_causal_shap.ida_graph = G
|
|
182
|
+
|
|
183
|
+
parent_values = {'feature1': 0.5}
|
|
184
|
+
sampled_value = self.fast_causal_shap.sample_conditional('feature2', parent_values)
|
|
185
|
+
self.assertIsInstance(sampled_value, (int, float, np.number))
|
|
186
|
+
|
|
187
|
+
def test_compute_v_do(self):
|
|
188
|
+
"""Test interventional expectation computation.
|
|
189
|
+
|
|
190
|
+
This test verifies the core do-calculus computation. This test checks:
|
|
191
|
+
- Empty intervention handling.
|
|
192
|
+
- Intervention with specific values.
|
|
193
|
+
- Caching of results.
|
|
194
|
+
"""
|
|
195
|
+
# Set up a simple graph with ALL nodes from data
|
|
196
|
+
self.fast_causal_shap.ida_graph = self._create_test_graph([('feature1', 'feature2'), ('feature2', 'target')])
|
|
197
|
+
|
|
198
|
+
# Test with empty intervention
|
|
199
|
+
result = self.fast_causal_shap.compute_v_do([], {})
|
|
200
|
+
self.assertIsInstance(result, (int, float, np.number))
|
|
201
|
+
|
|
202
|
+
# Test with intervention
|
|
203
|
+
result_intervened = self.fast_causal_shap.compute_v_do(['feature1'], {'feature1': 0.5})
|
|
204
|
+
self.assertIsInstance(result_intervened, (int, float, np.number))
|
|
205
|
+
|
|
206
|
+
# Test caching
|
|
207
|
+
result_cached = self.fast_causal_shap.compute_v_do(['feature1'], {'feature1': 0.5})
|
|
208
|
+
self.assertEqual(result_intervened, result_cached)
|
|
209
|
+
|
|
210
|
+
def test_compute_modified_shap_proba_regression(self):
|
|
211
|
+
"""Test SHAP computation for regression."""
|
|
212
|
+
# Load causal strengths first
|
|
213
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
214
|
+
json.dump(self.sample_causal_effects, f)
|
|
215
|
+
temp_file = f.name
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
self.fast_causal_shap.load_causal_strengths(temp_file)
|
|
219
|
+
|
|
220
|
+
# Test SHAP computation
|
|
221
|
+
x = self.regression_data.iloc[0][['feature1', 'feature2', 'feature3', 'feature4']]
|
|
222
|
+
shap_values = self.fast_causal_shap.compute_modified_shap_proba(x, is_classifier=False)
|
|
223
|
+
|
|
224
|
+
# Check that result is a dictionary
|
|
225
|
+
self.assertIsInstance(shap_values, dict)
|
|
226
|
+
|
|
227
|
+
# Check that all features are present
|
|
228
|
+
expected_features = ['feature1', 'feature2', 'feature3', 'feature4']
|
|
229
|
+
for feature in expected_features:
|
|
230
|
+
self.assertIn(feature, shap_values)
|
|
231
|
+
self.assertIsInstance(shap_values[feature], (int, float, np.number))
|
|
232
|
+
|
|
233
|
+
# Check that not all SHAP values are zero
|
|
234
|
+
# At least one feature should have non-zero attribution
|
|
235
|
+
total_abs_shap = sum(abs(shap_values[feature]) for feature in expected_features)
|
|
236
|
+
self.assertGreater(total_abs_shap, 0, "All SHAP values are zero - algorithm may not be working correctly")
|
|
237
|
+
|
|
238
|
+
finally:
|
|
239
|
+
os.unlink(temp_file)
|
|
240
|
+
|
|
241
|
+
def test_compute_modified_shap_proba_classification(self):
|
|
242
|
+
"""Test SHAP computation for classification."""
|
|
243
|
+
# Create classification instance
|
|
244
|
+
fcs_clf = FastCausalSHAP(
|
|
245
|
+
data=self.classification_data,
|
|
246
|
+
model=self.classification_model,
|
|
247
|
+
target_variable='target'
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# Load causal strengths
|
|
251
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
252
|
+
json.dump(self.sample_causal_effects, f)
|
|
253
|
+
temp_file = f.name
|
|
254
|
+
|
|
255
|
+
try:
|
|
256
|
+
fcs_clf.load_causal_strengths(temp_file)
|
|
257
|
+
|
|
258
|
+
# Test SHAP computation for classification
|
|
259
|
+
x = self.classification_data.iloc[0][['feature1', 'feature2', 'feature3', 'feature4']]
|
|
260
|
+
shap_values = fcs_clf.compute_modified_shap_proba(x, is_classifier=True)
|
|
261
|
+
|
|
262
|
+
# Check that result is a dictionary
|
|
263
|
+
self.assertIsInstance(shap_values, dict)
|
|
264
|
+
|
|
265
|
+
# Check that all features are present
|
|
266
|
+
expected_features = ['feature1', 'feature2', 'feature3', 'feature4']
|
|
267
|
+
for feature in expected_features:
|
|
268
|
+
self.assertIn(feature, shap_values)
|
|
269
|
+
self.assertIsInstance(shap_values[feature], (int, float, np.number))
|
|
270
|
+
|
|
271
|
+
# Check that not all SHAP values are zero
|
|
272
|
+
# At least one feature should have non-zero attribution
|
|
273
|
+
total_abs_shap = sum(abs(shap_values[feature]) for feature in expected_features)
|
|
274
|
+
self.assertGreater(total_abs_shap, 0, "All SHAP values are zero - algorithm may not be working correctly")
|
|
275
|
+
|
|
276
|
+
finally:
|
|
277
|
+
os.unlink(temp_file)
|
|
278
|
+
|
|
279
|
+
def test_compute_feature_depths(self):
|
|
280
|
+
"""Test feature depth computation."""
|
|
281
|
+
# Set up a graph with known depths - include ALL nodes
|
|
282
|
+
self.fast_causal_shap.ida_graph = self._create_test_graph([
|
|
283
|
+
('feature1', 'feature2'),
|
|
284
|
+
('feature2', 'target'),
|
|
285
|
+
('feature3', 'target')
|
|
286
|
+
])
|
|
287
|
+
self.fast_causal_shap._compute_feature_depths()
|
|
288
|
+
|
|
289
|
+
# Check computed depths
|
|
290
|
+
self.assertEqual(self.fast_causal_shap.feature_depths['feature1'], 2)
|
|
291
|
+
self.assertEqual(self.fast_causal_shap.feature_depths['feature3'], 1)
|
|
292
|
+
|
|
293
|
+
def test_compute_causal_paths(self):
|
|
294
|
+
"""Test causal path computation."""
|
|
295
|
+
# Set up a graph with known paths - include ALL nodes
|
|
296
|
+
self.fast_causal_shap.ida_graph = self._create_test_graph([
|
|
297
|
+
('feature1', 'feature2'),
|
|
298
|
+
('feature2', 'target'),
|
|
299
|
+
('feature3', 'target')
|
|
300
|
+
])
|
|
301
|
+
self.fast_causal_shap._compute_causal_paths()
|
|
302
|
+
|
|
303
|
+
# Check computed paths
|
|
304
|
+
self.assertIn('feature1', self.fast_causal_shap.causal_paths)
|
|
305
|
+
self.assertIn('feature3', self.fast_causal_shap.causal_paths)
|
|
306
|
+
|
|
307
|
+
# Check that feature1 has a path through feature2
|
|
308
|
+
feature1_paths = self.fast_causal_shap.causal_paths['feature1']
|
|
309
|
+
self.assertTrue(any('feature2' in path for path in feature1_paths))
|
|
310
|
+
|
|
311
|
+
def test_invalid_json_file(self):
|
|
312
|
+
"""Test handling of invalid JSON file."""
|
|
313
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
314
|
+
f.write("invalid json content")
|
|
315
|
+
temp_file = f.name
|
|
316
|
+
|
|
317
|
+
try:
|
|
318
|
+
with self.assertRaises(json.JSONDecodeError):
|
|
319
|
+
self.fast_causal_shap.load_causal_strengths(temp_file)
|
|
320
|
+
finally:
|
|
321
|
+
os.unlink(temp_file)
|
|
322
|
+
|
|
323
|
+
def test_empty_causal_effects(self):
|
|
324
|
+
"""Test handling of empty causal effects."""
|
|
325
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
326
|
+
json.dump([], f)
|
|
327
|
+
temp_file = f.name
|
|
328
|
+
|
|
329
|
+
try:
|
|
330
|
+
gamma = self.fast_causal_shap.load_causal_strengths(temp_file)
|
|
331
|
+
self.assertIsInstance(gamma, dict)
|
|
332
|
+
# All gamma values should be 0 for empty effects
|
|
333
|
+
self.assertTrue(all(v == 0.0 for v in gamma.values()))
|
|
334
|
+
finally:
|
|
335
|
+
os.unlink(temp_file)
|
|
336
|
+
|
|
337
|
+
def test_compute_path_delta_v(self):
|
|
338
|
+
"""Test path delta V computation."""
|
|
339
|
+
# Set up a simple graph with ALL nodes
|
|
340
|
+
self.fast_causal_shap.ida_graph = self._create_test_graph([('feature1', 'target')])
|
|
341
|
+
self.fast_causal_shap._compute_feature_depths()
|
|
342
|
+
self.fast_causal_shap._compute_causal_paths()
|
|
343
|
+
|
|
344
|
+
# Create test data
|
|
345
|
+
x = self.regression_data.iloc[0][['feature1', 'feature2', 'feature3', 'feature4']]
|
|
346
|
+
path = ['feature1', 'target']
|
|
347
|
+
|
|
348
|
+
# Test delta V computation
|
|
349
|
+
delta_v = self.fast_causal_shap._compute_path_delta_v('feature1', path, 0, x, False)
|
|
350
|
+
self.assertIsInstance(delta_v, (int, float, np.number))
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
if __name__ == '__main__':
|
|
355
|
+
# Run the tests
|
|
356
|
+
unittest.main(verbosity=2)
|
fast_causal_shap-0.1.0/PKG-INFO
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: fast-causal-shap
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: A Python package for efficient causal SHAP computations
|
|
5
|
-
Author-email: woonyee28 <ngnwy289@gmail.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/woonyee28/CausalSHAP
|
|
8
|
-
Project-URL: Issues, https://github.com/woonyee28/CausalSHAP/issues
|
|
9
|
-
Requires-Python: >=3.7
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Requires-Dist: pandas>=1.0.0
|
|
13
|
-
Requires-Dist: networkx>=2.0
|
|
14
|
-
Requires-Dist: numpy>=1.18.0
|
|
15
|
-
Requires-Dist: scikit-learn>=0.24.0
|
|
16
|
-
Dynamic: license-file
|
|
17
|
-
|
|
18
|
-
# Fast Causal SHAP
|
|
19
|
-
|
|
20
|
-
This folder contains the core modules and components for the **Fast Causal SHAP** Python package. Fast Causal SHAP provides efficient and interpretable SHAP value computation for causal inference tasks.
|
|
21
|
-
|
|
22
|
-
## Features
|
|
23
|
-
|
|
24
|
-
- Fast computation of SHAP values for causal models
|
|
25
|
-
- Support for multiple causal inference frameworks
|
|
26
|
-
|
|
27
|
-
## Installation
|
|
28
|
-
|
|
29
|
-
Install Fast Causal SHAP using pip:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
pip install fast-causal-shap
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Or, for the latest development version:
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
pip install git+https://github.com/woonyee28/CausalSHAP.git
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Usage
|
|
42
|
-
// To be added
|
|
43
|
-
|
|
44
|
-
## Citation
|
|
45
|
-
If you use this package in your research, please cite:
|
|
46
|
-
// To be added
|
|
47
|
-
|
|
48
|
-
## License
|
|
49
|
-
|
|
50
|
-
This project is licensed under the MIT License.
|
fast_causal_shap-0.1.0/README.md
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# Fast Causal SHAP
|
|
2
|
-
|
|
3
|
-
This folder contains the core modules and components for the **Fast Causal SHAP** Python package. Fast Causal SHAP provides efficient and interpretable SHAP value computation for causal inference tasks.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- Fast computation of SHAP values for causal models
|
|
8
|
-
- Support for multiple causal inference frameworks
|
|
9
|
-
|
|
10
|
-
## Installation
|
|
11
|
-
|
|
12
|
-
Install Fast Causal SHAP using pip:
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
pip install fast-causal-shap
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Or, for the latest development version:
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
pip install git+https://github.com/woonyee28/CausalSHAP.git
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Usage
|
|
25
|
-
// To be added
|
|
26
|
-
|
|
27
|
-
## Citation
|
|
28
|
-
If you use this package in your research, please cite:
|
|
29
|
-
// To be added
|
|
30
|
-
|
|
31
|
-
## License
|
|
32
|
-
|
|
33
|
-
This project is licensed under the MIT License.
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: fast-causal-shap
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: A Python package for efficient causal SHAP computations
|
|
5
|
-
Author-email: woonyee28 <ngnwy289@gmail.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/woonyee28/CausalSHAP
|
|
8
|
-
Project-URL: Issues, https://github.com/woonyee28/CausalSHAP/issues
|
|
9
|
-
Requires-Python: >=3.7
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Requires-Dist: pandas>=1.0.0
|
|
13
|
-
Requires-Dist: networkx>=2.0
|
|
14
|
-
Requires-Dist: numpy>=1.18.0
|
|
15
|
-
Requires-Dist: scikit-learn>=0.24.0
|
|
16
|
-
Dynamic: license-file
|
|
17
|
-
|
|
18
|
-
# Fast Causal SHAP
|
|
19
|
-
|
|
20
|
-
This folder contains the core modules and components for the **Fast Causal SHAP** Python package. Fast Causal SHAP provides efficient and interpretable SHAP value computation for causal inference tasks.
|
|
21
|
-
|
|
22
|
-
## Features
|
|
23
|
-
|
|
24
|
-
- Fast computation of SHAP values for causal models
|
|
25
|
-
- Support for multiple causal inference frameworks
|
|
26
|
-
|
|
27
|
-
## Installation
|
|
28
|
-
|
|
29
|
-
Install Fast Causal SHAP using pip:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
pip install fast-causal-shap
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Or, for the latest development version:
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
pip install git+https://github.com/woonyee28/CausalSHAP.git
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Usage
|
|
42
|
-
// To be added
|
|
43
|
-
|
|
44
|
-
## Citation
|
|
45
|
-
If you use this package in your research, please cite:
|
|
46
|
-
// To be added
|
|
47
|
-
|
|
48
|
-
## License
|
|
49
|
-
|
|
50
|
-
This project is licensed under the MIT License.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fast_causal_shap-0.1.0 → fast_causal_shap-0.1.2}/fast_causal_shap.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|