timeawarepc 1.2.4__tar.gz → 2.0.1__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.
Files changed (23) hide show
  1. {timeawarepc-1.2.4/timeawarepc.egg-info → timeawarepc-2.0.1}/PKG-INFO +32 -14
  2. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/README.md +30 -12
  3. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/setup.cfg +2 -2
  4. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/setup.py +2 -2
  5. timeawarepc-2.0.1/test/test_optional_bootstrap.py +107 -0
  6. timeawarepc-2.0.1/test/test_partial_corr_shift_invariance.py +61 -0
  7. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc/__init__.py +1 -1
  8. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc/find_cfc.py +15 -7
  9. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc/pcalg.py +13 -9
  10. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc/pcalg_helpers.py +13 -9
  11. timeawarepc-2.0.1/timeawarepc/tpc.py +194 -0
  12. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc/tutorial.py +3 -3
  13. {timeawarepc-1.2.4 → timeawarepc-2.0.1/timeawarepc.egg-info}/PKG-INFO +32 -14
  14. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc.egg-info/SOURCES.txt +2 -0
  15. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc.egg-info/requires.txt +1 -1
  16. timeawarepc-1.2.4/timeawarepc/tpc.py +0 -155
  17. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/LICENSE +0 -0
  18. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/pyproject.toml +0 -0
  19. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc/gc.py +0 -0
  20. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc/simulate_data.py +0 -0
  21. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc/tpc_helpers.py +0 -0
  22. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc.egg-info/dependency_links.txt +0 -0
  23. {timeawarepc-1.2.4 → timeawarepc-2.0.1}/timeawarepc.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: timeawarepc
3
- Version: 1.2.4
3
+ Version: 2.0.1
4
4
  Summary: Time-Aware PC Python Package
5
5
  Home-page: https://github.com/biswasr/TimeAwarePC
6
6
  Author: Rahul Biswas
@@ -15,7 +15,7 @@ Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: numpy
17
17
  Requires-Dist: pandas
18
- Requires-Dist: rpy2==3.5.11
18
+ Requires-Dist: rpy2>=3.5.11
19
19
  Requires-Dist: networkx
20
20
  Requires-Dist: scipy
21
21
  Dynamic: author
@@ -39,17 +39,25 @@ Dynamic: summary
39
39
 
40
40
  ## Installation
41
41
 
42
- You can get the latest version of TimeAwarePC as follows.
42
+ ### Recommended: conda environment (handles R + kpcalg automatically)
43
43
 
44
44
  ```
45
- $ pip install timeawarepc
45
+ $ git clone https://github.com/shlizee/TimeAwarePC.git
46
+ $ cd TimeAwarePC
47
+ $ conda env create -f environment.yml
48
+ $ conda activate timeawarepc
49
+ $ Rscript install_r_deps.R # installs kpcalg from CRAN archive
46
50
  ```
47
51
 
48
- ## Requirements
49
- - Python == 3.10
50
- - Python packages automatically checked and installed as part of the setup. To use Granger Causality, additional dependency of ```nitime``` which can be installed by ```pip install nitime```.
51
- - R == 4.4.2
52
- - R package ```kpcalg``` and its dependencies. They can be installed in R or RStudio as follows:
52
+ This installs Python, R, rpy2, all required R packages (graph, RBGL, pcalg), and TimeAwarePC v2.0.0 in a single isolated environment.
53
+
54
+ ### Manual install (alternative)
55
+
56
+ If you prefer to install without conda:
57
+
58
+ - Python >=3.9, <3.11
59
+ - R >= 4.0
60
+ - R package ```kpcalg``` and its dependencies, installed via R or RStudio:
53
61
  ```
54
62
  > install.packages("BiocManager")
55
63
  > BiocManager::install("graph")
@@ -57,10 +65,12 @@ $ pip install timeawarepc
57
65
  > install.packages("pcalg")
58
66
  > install.packages("https://cran.r-project.org/src/contrib/Archive/kpcalg/kpcalg_1.0.1.tar.gz")
59
67
  ```
60
- <!-- - In addition, if you like to use Granger Causality functions in this package, please separately install nitime as follows:
68
+ - Then:
61
69
  ```
62
- pip install nitime
63
- ``` -->
70
+ $ pip install timeawarepc
71
+ ```
72
+
73
+ To use Granger Causality, also install `nitime` (`pip install nitime`).
64
74
 
65
75
  ## Documentation
66
76
 
@@ -68,7 +78,15 @@ pip install nitime
68
78
 
69
79
  ## Tutorial
70
80
 
71
- See the [Quick Start Guide](https://timeawarepc.readthedocs.io/en/latest/gettingstarted.html) for a quick tutorial of the main functionalities of this library and check if it is installed properly.
81
+ See the [Quick Start Guide](https://timeawarepc.readthedocs.io/en/latest/gettingstarted.html) for a quick tutorial of the main functionalities of this library and check if it is installed properly.
82
+
83
+ ## What's new in v2.0.0
84
+
85
+ - `cfc_tpc` now defaults to **no bootstrap subsampling**: a single PC run is performed on the full time-delayed data.
86
+ - To use bootstrap stability scoring, pass `subsampsize` and `niter` together (e.g., `subsampsize=50, niter=25`).
87
+ - Both arguments must be specified together (or both left as the default `None`).
88
+ - `partial_corr` now fits an intercept and is shift-invariant. Previously the regression was forced through the origin, biasing residuals when the data was not mean-centered.
89
+ - See [CHANGELOG.md](CHANGELOG.md) for the full list of changes and migration notes.
72
90
  <!--
73
91
  ## Documentation
74
92
 
@@ -80,7 +98,7 @@ Your help is absolutely welcome! Please do reach out or create a feature branch!
80
98
 
81
99
  ## Citation
82
100
 
83
- Biswas, R., & Shlizerman, E. (2022). Statistical Perspective on Functional and Causal Neural Connectomics: The Time-Aware PC Algorithm. https://arxiv.org/abs/2204.04845
101
+ Biswas, R., & Shlizerman, E. (2022). Statistical Perspective on Functional and Causal Neural Connectomics: The Time-Aware PC Algorithm. https://doi.org/10.1371/journal.pcbi.1010653
84
102
 
85
103
  Biswas, R., & Shlizerman, E. (2021). Statistical Perspective on Functional and Causal Neural Connectomics: A Comparative Study. Frontiers in Systems Neuroscience. https://doi.org/10.3389/fnsys.2022.817962
86
104
 
@@ -7,17 +7,25 @@
7
7
 
8
8
  ## Installation
9
9
 
10
- You can get the latest version of TimeAwarePC as follows.
10
+ ### Recommended: conda environment (handles R + kpcalg automatically)
11
11
 
12
12
  ```
13
- $ pip install timeawarepc
13
+ $ git clone https://github.com/shlizee/TimeAwarePC.git
14
+ $ cd TimeAwarePC
15
+ $ conda env create -f environment.yml
16
+ $ conda activate timeawarepc
17
+ $ Rscript install_r_deps.R # installs kpcalg from CRAN archive
14
18
  ```
15
19
 
16
- ## Requirements
17
- - Python == 3.10
18
- - Python packages automatically checked and installed as part of the setup. To use Granger Causality, additional dependency of ```nitime``` which can be installed by ```pip install nitime```.
19
- - R == 4.4.2
20
- - R package ```kpcalg``` and its dependencies. They can be installed in R or RStudio as follows:
20
+ This installs Python, R, rpy2, all required R packages (graph, RBGL, pcalg), and TimeAwarePC v2.0.0 in a single isolated environment.
21
+
22
+ ### Manual install (alternative)
23
+
24
+ If you prefer to install without conda:
25
+
26
+ - Python >=3.9, <3.11
27
+ - R >= 4.0
28
+ - R package ```kpcalg``` and its dependencies, installed via R or RStudio:
21
29
  ```
22
30
  > install.packages("BiocManager")
23
31
  > BiocManager::install("graph")
@@ -25,10 +33,12 @@ $ pip install timeawarepc
25
33
  > install.packages("pcalg")
26
34
  > install.packages("https://cran.r-project.org/src/contrib/Archive/kpcalg/kpcalg_1.0.1.tar.gz")
27
35
  ```
28
- <!-- - In addition, if you like to use Granger Causality functions in this package, please separately install nitime as follows:
36
+ - Then:
29
37
  ```
30
- pip install nitime
31
- ``` -->
38
+ $ pip install timeawarepc
39
+ ```
40
+
41
+ To use Granger Causality, also install `nitime` (`pip install nitime`).
32
42
 
33
43
  ## Documentation
34
44
 
@@ -36,7 +46,15 @@ pip install nitime
36
46
 
37
47
  ## Tutorial
38
48
 
39
- See the [Quick Start Guide](https://timeawarepc.readthedocs.io/en/latest/gettingstarted.html) for a quick tutorial of the main functionalities of this library and check if it is installed properly.
49
+ See the [Quick Start Guide](https://timeawarepc.readthedocs.io/en/latest/gettingstarted.html) for a quick tutorial of the main functionalities of this library and check if it is installed properly.
50
+
51
+ ## What's new in v2.0.0
52
+
53
+ - `cfc_tpc` now defaults to **no bootstrap subsampling**: a single PC run is performed on the full time-delayed data.
54
+ - To use bootstrap stability scoring, pass `subsampsize` and `niter` together (e.g., `subsampsize=50, niter=25`).
55
+ - Both arguments must be specified together (or both left as the default `None`).
56
+ - `partial_corr` now fits an intercept and is shift-invariant. Previously the regression was forced through the origin, biasing residuals when the data was not mean-centered.
57
+ - See [CHANGELOG.md](CHANGELOG.md) for the full list of changes and migration notes.
40
58
  <!--
41
59
  ## Documentation
42
60
 
@@ -48,7 +66,7 @@ Your help is absolutely welcome! Please do reach out or create a feature branch!
48
66
 
49
67
  ## Citation
50
68
 
51
- Biswas, R., & Shlizerman, E. (2022). Statistical Perspective on Functional and Causal Neural Connectomics: The Time-Aware PC Algorithm. https://arxiv.org/abs/2204.04845
69
+ Biswas, R., & Shlizerman, E. (2022). Statistical Perspective on Functional and Causal Neural Connectomics: The Time-Aware PC Algorithm. https://doi.org/10.1371/journal.pcbi.1010653
52
70
 
53
71
  Biswas, R., & Shlizerman, E. (2021). Statistical Perspective on Functional and Causal Neural Connectomics: A Comparative Study. Frontiers in Systems Neuroscience. https://doi.org/10.3389/fnsys.2022.817962
54
72
 
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = timeawarepc
3
- version = 1.2.4
3
+ version = 2.0.1
4
4
  author = Rahul Biswas
5
5
  author_email = rahul.biswas@ucsf.edu
6
6
  description = Time-Aware PC Python Package
@@ -24,7 +24,7 @@ python_requires = >=3.7, <3.11
24
24
  install_requires =
25
25
  numpy
26
26
  pandas
27
- rpy2==3.5.11
27
+ rpy2>=3.5.11
28
28
  networkx
29
29
  scipy
30
30
 
@@ -3,7 +3,7 @@ import setuptools
3
3
  with open('README.md','r') as fh:
4
4
  README = fh.read()
5
5
 
6
- VERSION = "1.2.4"
6
+ VERSION = "2.0.1"
7
7
 
8
8
  setuptools.setup(
9
9
  name = 'timeawarepc',
@@ -19,7 +19,7 @@ setuptools.setup(
19
19
  install_requires=[
20
20
  'numpy',
21
21
  'pandas',
22
- 'rpy2==3.5.11',
22
+ 'rpy2>=3.5.11',
23
23
  'networkx',
24
24
  'scipy'
25
25
  ],
@@ -0,0 +1,107 @@
1
+ """Tests for the optional bootstrap behavior of cfc_tpc.
2
+
3
+ By default (subsampsize=None, niter=None), cfc_tpc should run PC once on the
4
+ full time-delayed data without bootstrap subsampling. When both subsampsize
5
+ and niter are specified, it should run bootstrap subsampling as before.
6
+ """
7
+ import os
8
+ import sys
9
+
10
+ # Insert project root before any installed timeawarepc so tests run against
11
+ # the in-repo source, not any pre-installed package version.
12
+ _PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
13
+ if _PROJECT_ROOT not in sys.path:
14
+ sys.path.insert(0, _PROJECT_ROOT)
15
+
16
+ import numpy as np
17
+
18
+
19
+ # All cfc_tpc paths import rpy2. If R/rpy2 isn't available on this system, we
20
+ # fall back to testing the no-bootstrap signature directly via _run_pc_inner.
21
+ try:
22
+ from timeawarepc.tpc import cfc_tpc
23
+ HAS_RPY2 = True
24
+ except OSError:
25
+ HAS_RPY2 = False
26
+
27
+
28
+ def _make_test_data(seed: int = 0, T: int = 400, p: int = 4):
29
+ rng = np.random.default_rng(seed)
30
+ data = rng.standard_normal((T, p))
31
+ # Inject lagged causal structure on the first 4 vars when available.
32
+ for t in range(1, T):
33
+ if p >= 2:
34
+ data[t, 1] = 0.7 * data[t - 1, 0] + 0.3 * rng.standard_normal()
35
+ if p >= 3:
36
+ data[t, 2] = 0.6 * data[t - 1, 1] + 0.3 * rng.standard_normal()
37
+ if p >= 4:
38
+ data[t, 3] = 0.5 * data[t - 1, 2] + 0.3 * rng.standard_normal()
39
+ return data
40
+
41
+
42
+ def test_cfc_tpc_default_no_bootstrap():
43
+ """Default call (no subsampsize / niter) should succeed without bootstrap."""
44
+ if not HAS_RPY2:
45
+ print("SKIP test_cfc_tpc_default_no_bootstrap: rpy2/R not available")
46
+ return
47
+ data = _make_test_data()
48
+ adjacency, weights = cfc_tpc(data, maxdelay=1, alpha=0.1, isgauss=True)
49
+ assert adjacency.shape == (4, 4)
50
+ assert weights.shape == (4, 4)
51
+
52
+
53
+ def test_cfc_tpc_bootstrap_path():
54
+ """Explicit bootstrap (subsampsize + niter specified) should still work."""
55
+ if not HAS_RPY2:
56
+ print("SKIP test_cfc_tpc_bootstrap_path: rpy2/R not available")
57
+ return
58
+ data = _make_test_data()
59
+ adjacency, weights = cfc_tpc(
60
+ data, maxdelay=1, alpha=0.1, isgauss=True,
61
+ subsampsize=50, niter=5,
62
+ )
63
+ assert adjacency.shape == (4, 4)
64
+ assert weights.shape == (4, 4)
65
+
66
+
67
+ def test_cfc_tpc_partial_bootstrap_args_raises():
68
+ """Specifying only one of subsampsize/niter should raise ValueError."""
69
+ if not HAS_RPY2:
70
+ print("SKIP test_cfc_tpc_partial_bootstrap_args_raises: rpy2/R not available")
71
+ return
72
+ data = _make_test_data()
73
+ try:
74
+ cfc_tpc(data, isgauss=True, subsampsize=50)
75
+ except ValueError:
76
+ pass
77
+ else:
78
+ raise AssertionError("Expected ValueError when only subsampsize is given")
79
+
80
+ try:
81
+ cfc_tpc(data, isgauss=True, niter=5)
82
+ except ValueError:
83
+ pass
84
+ else:
85
+ raise AssertionError("Expected ValueError when only niter is given")
86
+
87
+
88
+ def test_cfc_tpc_subsampsize_too_large_raises():
89
+ """subsampsize >= number of time-delayed samples should raise ValueError."""
90
+ if not HAS_RPY2:
91
+ print("SKIP test_cfc_tpc_subsampsize_too_large_raises: rpy2/R not available")
92
+ return
93
+ data = _make_test_data(T=20, p=3)
94
+ try:
95
+ cfc_tpc(data, isgauss=True, subsampsize=1000, niter=1)
96
+ except ValueError:
97
+ pass
98
+ else:
99
+ raise AssertionError("Expected ValueError when subsampsize too large")
100
+
101
+
102
+ if __name__ == "__main__":
103
+ test_cfc_tpc_default_no_bootstrap()
104
+ test_cfc_tpc_bootstrap_path()
105
+ test_cfc_tpc_partial_bootstrap_args_raises()
106
+ test_cfc_tpc_subsampsize_too_large_raises()
107
+ print("All tests passed.")
@@ -0,0 +1,61 @@
1
+ """Regression test: partial_corr should be shift-invariant.
2
+
3
+ Adding a constant to the input data should not change the partial correlation,
4
+ because the underlying linear regression now includes an intercept term.
5
+ """
6
+ import os
7
+ import sys
8
+
9
+ # Insert project root before any installed timeawarepc so the tests run
10
+ # against the in-repo source, not any pre-installed package version.
11
+ _PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
12
+ if _PROJECT_ROOT not in sys.path:
13
+ sys.path.insert(0, _PROJECT_ROOT)
14
+
15
+ import numpy as np
16
+
17
+ from timeawarepc.pcalg import partial_corr as partial_corr_pcalg
18
+ from timeawarepc.pcalg_helpers import partial_corr as partial_corr_helpers
19
+
20
+
21
+ def _make_test_data(seed: int = 0, n: int = 200):
22
+ rng = np.random.default_rng(seed)
23
+ # 3 variables; var 2 is a linear combination of vars 0 and 1 plus noise
24
+ data = rng.standard_normal((n, 3))
25
+ data[:, 2] = data[:, 0] + 0.5 * data[:, 1] + 0.1 * rng.standard_normal(n)
26
+ return data
27
+
28
+
29
+ def test_partial_corr_pcalg_shift_invariant():
30
+ data = _make_test_data()
31
+ r_orig = partial_corr_pcalg(0, 2, {1}, data)
32
+ r_shift = partial_corr_pcalg(0, 2, {1}, data + 10.0)
33
+ assert np.isclose(r_orig, r_shift, atol=1e-8), (
34
+ f"partial_corr (pcalg) not shift-invariant: {r_orig} vs {r_shift}"
35
+ )
36
+
37
+
38
+ def test_partial_corr_helpers_shift_invariant():
39
+ data = _make_test_data()
40
+ r_orig = partial_corr_helpers(data, 0, 2, {1})
41
+ r_shift = partial_corr_helpers(data + 10.0, 0, 2, {1})
42
+ assert np.isclose(r_orig, r_shift, atol=1e-8), (
43
+ f"partial_corr (helpers) not shift-invariant: {r_orig} vs {r_shift}"
44
+ )
45
+
46
+
47
+ def test_partial_corr_pcalg_matches_helpers():
48
+ """Both partial_corr implementations should agree."""
49
+ data = _make_test_data()
50
+ r1 = partial_corr_pcalg(0, 2, {1}, data)
51
+ r2 = partial_corr_helpers(data, 0, 2, {1})
52
+ assert np.isclose(r1, r2, atol=1e-8), (
53
+ f"partial_corr disagreement between pcalg and helpers: {r1} vs {r2}"
54
+ )
55
+
56
+
57
+ if __name__ == "__main__":
58
+ test_partial_corr_pcalg_shift_invariant()
59
+ test_partial_corr_helpers_shift_invariant()
60
+ test_partial_corr_pcalg_matches_helpers()
61
+ print("All tests passed.")
@@ -2,5 +2,5 @@
2
2
  Time-Aware PC Algorithm for estimating Causal Functional Connectivity from Time Series
3
3
  """
4
4
 
5
- __version__ = "0.0.1"
5
+ __version__ = "2.0.1"
6
6
  __all__ = ["tpc_helpers","tpc","find_cfc","pcalg","pcalg_helpers","simulate_data"]
@@ -2,7 +2,8 @@
2
2
  """
3
3
  __all__ = ["find_cfc"]
4
4
  from timeawarepc.tpc import *
5
- def find_cfc(data,method_name,alpha=0.05,maxdelay=1,niter=50,thresh=0.25,isgauss=False):
5
+ def find_cfc(data, method_name, alpha=0.05, maxdelay=1, isgauss=False,
6
+ subsampsize=None, niter=None, thresh=0.25):
6
7
  """Estimate Causal Functional Connectivity (CFC) between nodes from time series.
7
8
  This is a wrapper for functions cfc_tpc, cfc_pc, cfc_gc in tpc.py.
8
9
  Refer to the individual functions for their details.
@@ -15,12 +16,16 @@ def find_cfc(data,method_name,alpha=0.05,maxdelay=1,niter=50,thresh=0.25,isgauss
15
16
  'GC': Granger Causality.
16
17
  alpha: (float) Significance level
17
18
  isgauss: (boolean) Arg used for method_name == 'PC' or 'TPC'.
18
- True: Assume Gaussian Noise distribution,
19
+ True: Assume Gaussian Noise distribution,
19
20
  False: Distribution free.
20
21
  maxdelay: (int) Maximum time-delay of interactions. Arg used for method_name == 'GC' or 'TPC'.
21
- subsampsize: (int) Bootstrap window width in TPC. Arg used for method_name == 'TPC'.
22
- niter: (int) Number of bootstrap iterations in TPC. Arg used for method_name == 'TPC'.
23
- thresh: (float) Bootstrap stability cut-off in TPC. Arg used for method_name == 'TPC'.
22
+ subsampsize: (int, optional) Bootstrap window width in TPC. If None
23
+ (default), no bootstrap is performed and the full time-delayed
24
+ data is used. Must be specified together with niter.
25
+ niter: (int, optional) Number of bootstrap iterations in TPC. Must be
26
+ specified together with subsampsize.
27
+ thresh: (float) Bootstrap stability cut-off in TPC. Only used when
28
+ bootstrap is active.
24
29
 
25
30
  Returns:
26
31
  adjacency: (numpy.array) Adcajency matrix of estimated CFC by chosen method.
@@ -29,10 +34,13 @@ def find_cfc(data,method_name,alpha=0.05,maxdelay=1,niter=50,thresh=0.25,isgauss
29
34
  """
30
35
 
31
36
  if method_name == 'TPC':
32
- adjacency, weights = cfc_tpc(data,maxdelay=maxdelay,alpha=alpha,niter=niter,thresh=thresh,isgauss=isgauss)
37
+ adjacency, weights = cfc_tpc(
38
+ data, maxdelay=maxdelay, alpha=alpha, isgauss=isgauss,
39
+ subsampsize=subsampsize, niter=niter, thresh=thresh,
40
+ )
33
41
  elif method_name == 'PC':
34
42
  adjacency, weights = cfc_pc(data,alpha,isgauss=isgauss)
35
43
  elif method_name == 'GC':
36
- from timeawarepc.gc import cfc_gc
44
+ from timeawarepc.gc import cfc_gc
37
45
  adjacency, weights = cfc_gc(data,maxdelay,alpha)
38
46
  return adjacency,weights
@@ -419,16 +419,20 @@ def partial_corr(A,B,S,data):
419
419
  for i in range(p):
420
420
  if i in S:
421
421
  idx[i]=True
422
- C=data
423
- beta_A = linalg.lstsq(C[:,idx], C[:,A])[0]
424
- beta_B = linalg.lstsq(C[:,idx], C[:,B])[0]
422
+ C = data
425
423
 
426
- res_A = C[:,A] - C[:, idx].dot(beta_A)
427
- res_B = C[:,B] - C[:, idx].dot(beta_B)
428
-
429
- p_corr = stats.pearsonr(res_A, res_B)[0]
430
-
431
- return p_corr
424
+ # conditioning matrix: (n_samples, n_conditioning_vars)
425
+ # prepend a column of ones so lstsq fits an intercept -> shift-invariant
426
+ Z = C[:, idx]
427
+ Z = np.column_stack([np.ones(Z.shape[0]), Z])
428
+
429
+ beta_A = linalg.lstsq(Z, C[:, A])[0]
430
+ beta_B = linalg.lstsq(Z, C[:, B])[0]
431
+
432
+ res_A = C[:, A] - Z.dot(beta_A)
433
+ res_B = C[:, B] - Z.dot(beta_B)
434
+
435
+ return stats.pearsonr(res_A, res_B)[0]
432
436
  if __name__ == '__main__':
433
437
  import networkx as nx
434
438
  import numpy as np
@@ -43,13 +43,17 @@ def partial_corr(data,A,B,S):
43
43
  for i in range(p):
44
44
  if i in S:
45
45
  idx[i]=True
46
- C=data
47
- beta_A = linalg.lstsq(C[:,idx], C[:,A])[0]
48
- beta_B = linalg.lstsq(C[:,idx], C[:,B])[0]
46
+ C = data
49
47
 
50
- res_A = C[:,A] - C[:, idx].dot(beta_A)
51
- res_B = C[:,B] - C[:, idx].dot(beta_B)
52
-
53
- p_corr = stats.pearsonr(res_A, res_B)[0]
54
-
55
- return p_corr
48
+ # conditioning matrix: (n_samples, n_conditioning_vars)
49
+ # prepend a column of ones so lstsq fits an intercept -> shift-invariant
50
+ Z = C[:, idx]
51
+ Z = np.column_stack([np.ones(Z.shape[0]), Z])
52
+
53
+ beta_A = linalg.lstsq(Z, C[:, A])[0]
54
+ beta_B = linalg.lstsq(Z, C[:, B])[0]
55
+
56
+ res_A = C[:, A] - Z.dot(beta_A)
57
+ res_B = C[:, B] - Z.dot(beta_B)
58
+
59
+ return stats.pearsonr(res_A, res_B)[0]
@@ -0,0 +1,194 @@
1
+ """Implements the Time-Aware PC (TPC) Algorithm for finding Causal Functional Connectivity from Time Series.
2
+ """
3
+ import time
4
+ import numpy as np
5
+ import pandas as pd
6
+ from timeawarepc.tpc_helpers import *
7
+ from timeawarepc.pcalg import estimate_skeleton, estimate_cpdag, ci_test_gauss
8
+ import rpy2.robjects as robjects
9
+ from rpy2.robjects.packages import importr
10
+ import rpy2.rlike.container as rlc
11
+ from rpy2.robjects import pandas2ri
12
+ import random
13
+ import networkx as nx
14
+ import re
15
+ import numpy as np
16
+ import warnings
17
+ import logging
18
+ _logger = logging.getLogger(__name__)
19
+ #%%
20
+ def _run_pc_inner(sample, alpha, isgauss):
21
+ """Run PC on a single (sub)sample of time-delayed data, return CPDAG as nx.DiGraph."""
22
+ if not isgauss:
23
+ d = {'print.me': 'print_dot_me', 'print_me': 'print_uscore_me'}
24
+ kpcalg = importr('kpcalg', robject_translations=d)
25
+ sample_pd = pd.DataFrame(sample)
26
+ pandas2ri.activate()
27
+ df = robjects.conversion.py2rpy(sample_pd)
28
+ base = importr("base")
29
+ out = kpcalg.kpc(**{
30
+ 'suffStat': rlc.TaggedList((df, "hsic.perm"), tags=('data', 'ic.method')),
31
+ 'indepTest': kpcalg.kernelCItest,
32
+ 'alpha': alpha,
33
+ 'labels': sample_pd.columns.astype(str),
34
+ 'u2pd': "relaxed",
35
+ 'skel.method': "stable",
36
+ 'verbose': robjects.r('F'),
37
+ })
38
+ dollar = base.__dict__["@"]
39
+ graphobj = dollar(out, "graph")
40
+ graph = importr("graph")
41
+ graphedges = graph.edges(graphobj)
42
+ graphedgespy = {int(key): np.array(re.findall(r'-?\d+\.?\d*', str(graphedges.rx2(str(key))))).astype(int)
43
+ for key in graphedges.names}
44
+ g = nx.DiGraph(graphedgespy)
45
+ else:
46
+ (g, sep_set) = estimate_skeleton(indep_test_func=ci_test_gauss,
47
+ data_matrix=sample,
48
+ alpha=alpha, method='stable')
49
+ g = estimate_cpdag(skel_graph=g, sep_set=sep_set)
50
+ return g
51
+
52
+
53
+ def cfc_tpc(data, maxdelay=1, alpha=0.1, isgauss=False,
54
+ subsampsize=None, niter=None, thresh=0.25):
55
+ """Estimate Causal Functional Connectivity using TPC Algorithm.
56
+
57
+ By default (subsampsize=None, niter=None), TPC runs PC once on the full
58
+ time-delayed data — no bootstrap subsampling. To enable bootstrap
59
+ subsampling for stability scoring, pass both `subsampsize` and `niter`.
60
+
61
+ Args:
62
+ data: (numpy.array) of shape (n,p) with n time-recordings for p nodes.
63
+ maxdelay: (int) Maximum time-delay of interactions.
64
+ alpha: (float) Significance level for conditional independence tests.
65
+ isgauss: (boolean)
66
+ True: Assume Gaussian Noise distribution.
67
+ False: Distribution-free.
68
+ subsampsize: (int, optional) Bootstrap window width. If None (default),
69
+ no bootstrap is performed and the full time-delayed data is used.
70
+ Must be specified together with `niter`.
71
+ niter: (int, optional) Number of bootstrap iterations. Must be specified
72
+ together with `subsampsize`. Ignored when subsampsize is None.
73
+ thresh: (float) Bootstrap stability cut-off. Only used when bootstrap
74
+ is active (both subsampsize and niter specified).
75
+
76
+ Returns:
77
+ adjacency: (numpy.array) Adjacency matrix of shape (p,p) for estimated CFC by TPC Algorithm.
78
+ weights: (numpy.array) Connectivity Weight matrix of shape (p,p).
79
+
80
+ Biswas, Rahul and Shlizerman, Eli (2022). Statistical perspective on functional and causal neural connectomics: the time-aware pc algorithm. arXiv preprint arXiv:2204.04845.
81
+ """
82
+ if (subsampsize is None) != (niter is None):
83
+ raise ValueError(
84
+ "subsampsize and niter must both be specified (for bootstrap) "
85
+ "or both be None (for no bootstrap)."
86
+ )
87
+ use_bootstrap = subsampsize is not None
88
+
89
+ start_time = time.time()
90
+ data_trans = data_transformed(data, maxdelay) # Step 1: Time-Delayed Samples
91
+ _logger.debug("Data transformed in " + str(time.time() - start_time))
92
+
93
+ if not use_bootstrap:
94
+ # No-bootstrap path: run PC once on the full time-delayed data.
95
+ g = _run_pc_inner(data_trans, alpha, isgauss)
96
+ causaleff = causaleff_ida(g, data_trans)
97
+ G, causaleffin, _ = return_finaledges(g, causaleff, maxdelay, data.shape[1])
98
+ adjacency = np.asarray(G).copy()
99
+ weights = np.asarray(causaleffin, dtype=float)
100
+ # Step 8: Pruning (keep same magnitude-based pruning as bootstrap path)
101
+ with warnings.catch_warnings():
102
+ warnings.simplefilter("ignore", category=RuntimeWarning)
103
+ cutoff = np.nanmax(np.abs(weights)) / 10
104
+ adjacency[np.abs(weights) <= cutoff] = 0
105
+ return adjacency, weights
106
+
107
+ # Bootstrap path (legacy / stability-scored).
108
+ C_iter, C_cf_iter, C_cf2_iter = [], [], []
109
+ n = data_trans.shape[0]
110
+ if n - subsampsize <= 0:
111
+ raise ValueError(
112
+ f"subsampsize ({subsampsize}) must be smaller than the "
113
+ f"number of time-delayed samples ({n})."
114
+ )
115
+
116
+ for inneriter in range(niter):
117
+ start_btrstrp = time.time()
118
+ _logger.debug("Starting bootstrap " + str(inneriter))
119
+
120
+ # Step 2: Select random Bootstrap window
121
+ r_idx = random.sample(range(n - subsampsize), 1)[0]
122
+ sample = data_trans[r_idx:(r_idx + subsampsize), :]
123
+
124
+ # Step 3: PC
125
+ g = _run_pc_inner(sample, alpha, isgauss)
126
+
127
+ # Step 5: Rolled CFC-DPGM
128
+ causaleff = causaleff_ida(g, data_trans)
129
+ G, causaleffin, causaleffin2 = return_finaledges(g, causaleff, maxdelay, data.shape[1])
130
+
131
+ C_iter.append(G)
132
+ C_cf_iter.append(causaleffin)
133
+ C_cf2_iter.append(causaleffin2)
134
+ _logger.debug("Bootstrap done in " + str(time.time() - start_btrstrp))
135
+
136
+ # Step 6b-c: Robust Edges and Connectivity Weights
137
+ adjacency = (np.mean(np.asarray(C_iter), axis=0) >= thresh).astype(int)
138
+ with warnings.catch_warnings():
139
+ warnings.simplefilter("ignore", category=RuntimeWarning)
140
+ weights = np.nanmean(np.where(np.asarray(C_cf_iter) != 0,
141
+ np.asarray(C_cf_iter), np.nan), axis=0)
142
+ _logger.debug("CE shape " + str(weights.shape))
143
+
144
+ # Step 8: Pruning
145
+ adjacency[np.abs(weights) <= np.nanmax(np.abs(weights)) / 10] = 0
146
+
147
+ return adjacency, weights
148
+
149
+ def cfc_pc(data,alpha,isgauss=False):
150
+ """Estimate Causal Functional Connectivity using PC Algorithm.
151
+
152
+ Args:
153
+ data: (numpy.array) of shape (n,p) with n samples for p nodes
154
+ alpha: (float) Significance level for conditional independence tests
155
+ isgauss: (boolean)
156
+ True: Assume Gaussian Noise distribution,
157
+ False: Distribution free.
158
+
159
+ Returns:
160
+ adjacency: (numpy.array) Adcajency matrix of shape (p,p) of estimated CFC by PC Algorithm.
161
+ weights: (numpy.array) Connectivity Weight matrix of shape (p,p).
162
+
163
+ """
164
+ if not isgauss:
165
+ d = {'print.me': 'print_dot_me', 'print_me': 'print_uscore_me'}
166
+ kpcalg = importr('kpcalg', robject_translations = d)
167
+ data_trans_pd = pd.DataFrame(data)
168
+ pandas2ri.activate()
169
+ df = robjects.conversion.py2rpy(data_trans_pd)
170
+ out=kpcalg.kpc(**{'suffStat' : rlc.TaggedList((df,"hsic.perm"),tags=('data','ic.method')),#robjects.r('list(data=data_trans, ic.method="hsic.perm")'),#list(data=data_trans, ic.method="hsic.perm"),
171
+ 'indepTest' : kpcalg.kernelCItest,
172
+ 'alpha' : alpha,
173
+ 'labels' : data_trans_pd.columns.astype(str),
174
+ 'u2pd' : "relaxed",
175
+ 'skel.method' : "stable",
176
+ 'verbose' : robjects.r('F')})
177
+ base=importr("base")
178
+ dollar = base.__dict__["@"]
179
+ graphobj=dollar(out, "graph")
180
+ graph=importr("graph")
181
+ graphedges=graph.edges(graphobj)#, "matrix")
182
+ import re
183
+ graphedgespy={int(key): np.array(re.findall(r'-?\d+\.?\d*', str(graphedges.rx2(key)))[1:]).astype(int) for key in graphedges.names}
184
+ g=nx.DiGraph(graphedgespy)
185
+ else:
186
+ (g, sep_set) = estimate_skeleton(indep_test_func=ci_test_gauss,
187
+ data_matrix=data,
188
+ alpha=alpha,method='stable')
189
+ g = estimate_cpdag(skel_graph=g, sep_set=sep_set)
190
+
191
+ weights = causaleff_ida(g,data)
192
+ adjacency=nx.adjacency_matrix(g).toarray()
193
+ return adjacency, weights*adjacency
194
+ # %%
@@ -44,9 +44,9 @@ alpha = 0.05
44
44
  isgauss = (model == 'lingauss')
45
45
  if method_name == 'TPC':
46
46
  maxdelay=1
47
- niter = 50
48
- thresh = 0.25
49
- adjmat, causaleffmat = cfc_tpc(data,maxdelay=maxdelay,alpha=alpha,niter=niter,thresh=thresh,isgauss=isgauss)
47
+ # Default: no bootstrap, use full time-delayed data.
48
+ # To enable bootstrap stability scoring, pass subsampsize and niter together.
49
+ adjmat, causaleffmat = cfc_tpc(data, maxdelay=maxdelay, alpha=alpha, isgauss=isgauss)
50
50
  elif method_name == 'PC':
51
51
  adjmat, causaleffmat = cfc_pc(data,alpha,isgauss=isgauss)
52
52
  elif method_name == 'GC':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: timeawarepc
3
- Version: 1.2.4
3
+ Version: 2.0.1
4
4
  Summary: Time-Aware PC Python Package
5
5
  Home-page: https://github.com/biswasr/TimeAwarePC
6
6
  Author: Rahul Biswas
@@ -15,7 +15,7 @@ Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: numpy
17
17
  Requires-Dist: pandas
18
- Requires-Dist: rpy2==3.5.11
18
+ Requires-Dist: rpy2>=3.5.11
19
19
  Requires-Dist: networkx
20
20
  Requires-Dist: scipy
21
21
  Dynamic: author
@@ -39,17 +39,25 @@ Dynamic: summary
39
39
 
40
40
  ## Installation
41
41
 
42
- You can get the latest version of TimeAwarePC as follows.
42
+ ### Recommended: conda environment (handles R + kpcalg automatically)
43
43
 
44
44
  ```
45
- $ pip install timeawarepc
45
+ $ git clone https://github.com/shlizee/TimeAwarePC.git
46
+ $ cd TimeAwarePC
47
+ $ conda env create -f environment.yml
48
+ $ conda activate timeawarepc
49
+ $ Rscript install_r_deps.R # installs kpcalg from CRAN archive
46
50
  ```
47
51
 
48
- ## Requirements
49
- - Python == 3.10
50
- - Python packages automatically checked and installed as part of the setup. To use Granger Causality, additional dependency of ```nitime``` which can be installed by ```pip install nitime```.
51
- - R == 4.4.2
52
- - R package ```kpcalg``` and its dependencies. They can be installed in R or RStudio as follows:
52
+ This installs Python, R, rpy2, all required R packages (graph, RBGL, pcalg), and TimeAwarePC v2.0.0 in a single isolated environment.
53
+
54
+ ### Manual install (alternative)
55
+
56
+ If you prefer to install without conda:
57
+
58
+ - Python >=3.9, <3.11
59
+ - R >= 4.0
60
+ - R package ```kpcalg``` and its dependencies, installed via R or RStudio:
53
61
  ```
54
62
  > install.packages("BiocManager")
55
63
  > BiocManager::install("graph")
@@ -57,10 +65,12 @@ $ pip install timeawarepc
57
65
  > install.packages("pcalg")
58
66
  > install.packages("https://cran.r-project.org/src/contrib/Archive/kpcalg/kpcalg_1.0.1.tar.gz")
59
67
  ```
60
- <!-- - In addition, if you like to use Granger Causality functions in this package, please separately install nitime as follows:
68
+ - Then:
61
69
  ```
62
- pip install nitime
63
- ``` -->
70
+ $ pip install timeawarepc
71
+ ```
72
+
73
+ To use Granger Causality, also install `nitime` (`pip install nitime`).
64
74
 
65
75
  ## Documentation
66
76
 
@@ -68,7 +78,15 @@ pip install nitime
68
78
 
69
79
  ## Tutorial
70
80
 
71
- See the [Quick Start Guide](https://timeawarepc.readthedocs.io/en/latest/gettingstarted.html) for a quick tutorial of the main functionalities of this library and check if it is installed properly.
81
+ See the [Quick Start Guide](https://timeawarepc.readthedocs.io/en/latest/gettingstarted.html) for a quick tutorial of the main functionalities of this library and check if it is installed properly.
82
+
83
+ ## What's new in v2.0.0
84
+
85
+ - `cfc_tpc` now defaults to **no bootstrap subsampling**: a single PC run is performed on the full time-delayed data.
86
+ - To use bootstrap stability scoring, pass `subsampsize` and `niter` together (e.g., `subsampsize=50, niter=25`).
87
+ - Both arguments must be specified together (or both left as the default `None`).
88
+ - `partial_corr` now fits an intercept and is shift-invariant. Previously the regression was forced through the origin, biasing residuals when the data was not mean-centered.
89
+ - See [CHANGELOG.md](CHANGELOG.md) for the full list of changes and migration notes.
72
90
  <!--
73
91
  ## Documentation
74
92
 
@@ -80,7 +98,7 @@ Your help is absolutely welcome! Please do reach out or create a feature branch!
80
98
 
81
99
  ## Citation
82
100
 
83
- Biswas, R., & Shlizerman, E. (2022). Statistical Perspective on Functional and Causal Neural Connectomics: The Time-Aware PC Algorithm. https://arxiv.org/abs/2204.04845
101
+ Biswas, R., & Shlizerman, E. (2022). Statistical Perspective on Functional and Causal Neural Connectomics: The Time-Aware PC Algorithm. https://doi.org/10.1371/journal.pcbi.1010653
84
102
 
85
103
  Biswas, R., & Shlizerman, E. (2021). Statistical Perspective on Functional and Causal Neural Connectomics: A Comparative Study. Frontiers in Systems Neuroscience. https://doi.org/10.3389/fnsys.2022.817962
86
104
 
@@ -3,6 +3,8 @@ README.md
3
3
  pyproject.toml
4
4
  setup.cfg
5
5
  setup.py
6
+ test/test_optional_bootstrap.py
7
+ test/test_partial_corr_shift_invariance.py
6
8
  timeawarepc/__init__.py
7
9
  timeawarepc/find_cfc.py
8
10
  timeawarepc/gc.py
@@ -1,5 +1,5 @@
1
1
  numpy
2
2
  pandas
3
- rpy2==3.5.11
3
+ rpy2>=3.5.11
4
4
  networkx
5
5
  scipy
@@ -1,155 +0,0 @@
1
- """Implements the Time-Aware PC (TPC) Algorithm for finding Causal Functional Connectivity from Time Series.
2
- """
3
- import time
4
- import numpy as np
5
- import pandas as pd
6
- from timeawarepc.tpc_helpers import *
7
- from timeawarepc.pcalg import estimate_skeleton, estimate_cpdag, ci_test_gauss
8
- import rpy2.robjects as robjects
9
- from rpy2.robjects.packages import importr
10
- import rpy2.rlike.container as rlc
11
- from rpy2.robjects import pandas2ri
12
- import random
13
- import networkx as nx
14
- import re
15
- import numpy as np
16
- import warnings
17
- import logging
18
- _logger = logging.getLogger(__name__)
19
- #%%
20
- def cfc_tpc(data,maxdelay=1,subsampsize=50,niter=25,alpha=0.1,thresh=0.25,isgauss=False):
21
- """Estimate Causal Functional Connectivity using TPC Algorithm.
22
-
23
- Args:
24
- data: (numpy.array) of shape (n,p) with n time-recordings for p nodes .
25
- maxdelay: (int) Maximum time-delay of interactions.
26
- subsampsize: (int) Bootstrap window width.
27
- niter: (int) Number of bootstrap iterations.
28
- alpha: (float) Significance level for conditional independence tests.
29
- thresh: (float) Bootstrap stability cut-off.
30
- isgauss: (boolean)
31
- True: Assume Gaussian Noise distribution.
32
- False: Distribution-free.
33
-
34
- Returns:
35
- adjacency: (numpy.array) Adcajency matrix of shape (p,p) for estimated CFC by TPC Algorithm.
36
- weights: (numpy.array) Connectivity Weight matrix of shape (p,p).
37
-
38
- Biswas, Rahul and Shlizerman, Eli (2022). Statistical perspective on functional and causal neural connectomics: the time-aware pc algorithm. arXiv preprint arXiv:2204.04845.
39
- """
40
- C_iter=[]
41
- C_cf_iter=[]
42
- C_cf2_iter=[]
43
- start_time = time.time()
44
- data_trans = data_transformed(data, maxdelay)#Step 1: Time-Delayed Samples
45
- _logger.debug("Data transformed in "+str(time.time()-start_time))
46
-
47
- #Steps 2-6a:
48
- for inneriter in range(niter):
49
- start_btrstrp = time.time()
50
- _logger.debug("Starting bootstrap "+str(inneriter))
51
- n=data_trans.shape[0]
52
-
53
- #Step 2: Select random Bootstrap window
54
- r_idx = random.sample(range(n-subsampsize),1)[0]
55
-
56
- #Step 3: PC
57
- if not isgauss:
58
- d = {'print.me': 'print_dot_me', 'print_me': 'print_uscore_me'}
59
- kpcalg = importr('kpcalg', robject_translations = d)
60
- data_trans_pd=pd.DataFrame(data_trans[r_idx:(r_idx+subsampsize),:])
61
- pandas2ri.activate()
62
- df = robjects.conversion.py2rpy(data_trans_pd)
63
- base=importr("base")
64
- out=kpcalg.kpc(**{'suffStat' : rlc.TaggedList((df,"hsic.perm"),tags=('data','ic.method')),
65
- 'indepTest' : kpcalg.kernelCItest,
66
- 'alpha' : alpha,
67
- 'labels' : data_trans_pd.columns.astype(str),
68
- 'u2pd' : "relaxed",
69
- 'skel.method' : "stable",
70
- 'verbose' : robjects.r('F')})
71
- dollar = base.__dict__["@"]
72
- graphobj=dollar(out, "graph")
73
- graph=importr("graph")
74
- graphedges=graph.edges(graphobj)#, "matrix")
75
- graphedgespy={int(key): np.array(re.findall(r'-?\d+\.?\d*', str(graphedges.rx2(str(key))))).astype(int) for key in graphedges.names}
76
- g=nx.DiGraph(graphedgespy)
77
- else:
78
- data_trans_pd=data_trans[r_idx:(r_idx+subsampsize),:]
79
- (g, sep_set) = estimate_skeleton(indep_test_func=ci_test_gauss,
80
- data_matrix=data_trans_pd,
81
- alpha=alpha,method='stable')
82
- g = estimate_cpdag(skel_graph=g, sep_set=sep_set)
83
-
84
- #Step 4: Orient - update: not needed
85
- #g=orient(g,maxdelay,data.shape[1])
86
-
87
- #Step 5: Rolled CFC-DPGM
88
- causaleff = causaleff_ida(g,data_trans)#Interventional Causal Effects in Unrolled DAG
89
- G,causaleffin, causaleffin2=return_finaledges(g,causaleff,maxdelay,data.shape[1])#Rolled CFC-DPGM
90
-
91
- A_rr=G
92
- C_iter.append(A_rr)
93
- C_cf_iter.append(causaleffin)
94
- C_cf2_iter.append(causaleffin2)
95
- _logger.debug("Bootstrap done in "+str(time.time()-start_btrstrp))
96
- #Step 6a: Repeat Steps 2-5
97
-
98
- #Step 6b-c: Robust Edges and Connectivity Weights
99
- adjacency=(np.mean(np.asarray(C_iter),axis=0)>=thresh).astype(int)#Robust Edges
100
- with warnings.catch_warnings():
101
- warnings.simplefilter("ignore", category=RuntimeWarning)
102
- weights=np.nanmean(np.where(np.asarray(C_cf_iter)!=0,np.asarray(C_cf_iter),np.nan),axis=0)#Robust Connectivity Weights
103
- _logger.debug("CE shape "+str(weights.shape))
104
-
105
- #Step 8: Pruning
106
- adjacency[np.abs(weights) <= np.nanmax(np.abs(weights))/10]=0
107
-
108
- return adjacency, weights
109
-
110
- def cfc_pc(data,alpha,isgauss=False):
111
- """Estimate Causal Functional Connectivity using PC Algorithm.
112
-
113
- Args:
114
- data: (numpy.array) of shape (n,p) with n samples for p nodes
115
- alpha: (float) Significance level for conditional independence tests
116
- isgauss: (boolean)
117
- True: Assume Gaussian Noise distribution,
118
- False: Distribution free.
119
-
120
- Returns:
121
- adjacency: (numpy.array) Adcajency matrix of shape (p,p) of estimated CFC by PC Algorithm.
122
- weights: (numpy.array) Connectivity Weight matrix of shape (p,p).
123
-
124
- """
125
- if not isgauss:
126
- d = {'print.me': 'print_dot_me', 'print_me': 'print_uscore_me'}
127
- kpcalg = importr('kpcalg', robject_translations = d)
128
- data_trans_pd = pd.DataFrame(data)
129
- pandas2ri.activate()
130
- df = robjects.conversion.py2rpy(data_trans_pd)
131
- out=kpcalg.kpc(**{'suffStat' : rlc.TaggedList((df,"hsic.perm"),tags=('data','ic.method')),#robjects.r('list(data=data_trans, ic.method="hsic.perm")'),#list(data=data_trans, ic.method="hsic.perm"),
132
- 'indepTest' : kpcalg.kernelCItest,
133
- 'alpha' : alpha,
134
- 'labels' : data_trans_pd.columns.astype(str),
135
- 'u2pd' : "relaxed",
136
- 'skel.method' : "stable",
137
- 'verbose' : robjects.r('F')})
138
- base=importr("base")
139
- dollar = base.__dict__["@"]
140
- graphobj=dollar(out, "graph")
141
- graph=importr("graph")
142
- graphedges=graph.edges(graphobj)#, "matrix")
143
- import re
144
- graphedgespy={int(key): np.array(re.findall(r'-?\d+\.?\d*', str(graphedges.rx2(key)))[1:]).astype(int) for key in graphedges.names}
145
- g=nx.DiGraph(graphedgespy)
146
- else:
147
- (g, sep_set) = estimate_skeleton(indep_test_func=ci_test_gauss,
148
- data_matrix=data,
149
- alpha=alpha,method='stable')
150
- g = estimate_cpdag(skel_graph=g, sep_set=sep_set)
151
-
152
- weights = causaleff_ida(g,data)
153
- adjacency=nx.adjacency_matrix(g).toarray()
154
- return adjacency, weights*adjacency
155
- # %%
File without changes
File without changes