timeawarepc 1.2.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,55 @@
1
+ """Helper functions for PC algorithm.
2
+ """
3
+ import numpy as np
4
+ from scipy import stats, linalg
5
+ def ci_test_gauss(data,A,B,S,**kwargs):
6
+ """Conduct Conditional Independence Test using Fisher's Z-transform
7
+ for node A conditionally independent of node B given set of nodes in S.
8
+
9
+ Args:
10
+ data: (numpy.array) of shape (n,p) with n samples for p nodes
11
+ A: (int) node index in data
12
+ B: (int) node index in data
13
+ S: (set) set of node indices in data
14
+
15
+ Returns:
16
+ pval: (float) p-value of the conditional independence test for A and B given S.
17
+ """
18
+ r = partial_corr(data,A,B,S)
19
+ #print(r)
20
+ if r==1:
21
+ pval = 0
22
+ else:
23
+ z = 0.5 * np.log((1+r)/(1-r))
24
+ T = np.sqrt(data.shape[0]-len(S)-3)*np.abs(z)
25
+ pval = 2*(1 - stats.norm.cdf(T))
26
+ return pval
27
+
28
+ def partial_corr(data,A,B,S):
29
+ """Find Partial Correlation of var A and var B given set of vars in S.
30
+
31
+ Args:
32
+ data: (numpy.array) of shape (n,p) with n samples for p nodes
33
+ A: (int) node index in data
34
+ B: (int) node index in data
35
+ S: (set) set of node indices in data
36
+
37
+ Returns:
38
+ p_corr: Partial correlation between A and B given S.
39
+ """
40
+ p = data.shape[1]
41
+ idx = np.zeros(p, dtype=bool)
42
+
43
+ for i in range(p):
44
+ if i in S:
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]
49
+
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
@@ -0,0 +1,82 @@
1
+ """Create simulated time series dataset from three different paradigms:
2
+ Linear Gaussian Vector-AutoRegressive (VAR) Model,
3
+ Non-linear Non-Gaussian VAR Model, and
4
+ Continuous Time Recurrent Neural Networks (CTRNN).
5
+ """
6
+ import numpy as np
7
+ from numpy.random import default_rng
8
+ rng = default_rng(seed=111)
9
+
10
+ def simulate_data(model, T = 1000, noise = 1):
11
+ """Simulate time seris data from one of three simulation paradigms with 4 neurons.
12
+
13
+ Args:
14
+ model: (string)
15
+ 'lingauss': Linear Gaussian VAR model
16
+ 'nonlinnongauss': Non-linear Non-Gaussian VAR model
17
+ 'ctrnn': Continuous Time Recurrent Neural Networks
18
+ T: (int) Number of time recordings in the time series
19
+ noise: (float) Noise standard deviation
20
+
21
+ Returns:
22
+ data: (numpy.array) Time series data of shape (n,p) with n time recordings for p nodes
23
+ CFCtruth: (numpy.array) Ground Truth adjacency matrix of shape (p,p).
24
+ """
25
+ n_neurons = 4
26
+ CFCtruth = np.zeros((4,4),dtype = int)
27
+ if model == "lingauss":
28
+ smspikes=np.zeros((n_neurons,T))
29
+ lag=1
30
+ for iter1 in range(n_neurons):
31
+ smspikes[iter1,0]=rng.normal(scale=noise)
32
+ for t in range(1,T):
33
+ smspikes[0,t]=rng.normal(scale=noise)+1
34
+ smspikes[1,t]=rng.normal(scale=noise)-1
35
+ smspikes[2,t]=2*np.sum(smspikes[0,np.max((t-lag,0)):t])+np.sum(smspikes[1,np.max((t-lag,0)):t])+rng.normal(scale=noise)
36
+ smspikes[3,t]=2*np.sum(smspikes[2,np.max((t-lag,0)):t])+rng.normal(scale=noise)
37
+ CFCtruth[0,2]=CFCtruth[1,2]=CFCtruth[2,3]=1
38
+ elif model == "nonlinnongauss":
39
+ smspikes=np.zeros((n_neurons,T))
40
+ lag=1
41
+ for iter1 in range(n_neurons):
42
+ smspikes[iter1,0]=np.random.uniform()
43
+ for t in range(1,T):
44
+ smspikes[0,t]=np.random.uniform(high = noise)
45
+ smspikes[1,t]=np.random.uniform(high = noise)
46
+ smspikes[2,t]=4*np.sum(np.sin(smspikes[0,np.max((t-lag,0)):t]))+3*np.sum(np.sin(smspikes[1,np.max((t-lag,0)):t]))+np.random.uniform(high = noise)
47
+ smspikes[3,t]=3*np.sum(np.sin(smspikes[2,np.max((t-lag,0)):t]))+np.random.uniform(high = noise)
48
+ CFCtruth[0,2]=CFCtruth[1,2]=CFCtruth[2,3]=1
49
+ elif model == 'ctrnn':
50
+ lag=1
51
+ p=4
52
+ w=np.zeros((p,p))
53
+ w[0,2]=10
54
+ w[1,2]=10
55
+ w[2,3]=10
56
+ n_ctrnn=T
57
+ tau=10
58
+ smspikes=simulate_ctrnn(n_ctrnn,p,w,tau,noise)
59
+ CFCtruth[0,2]=CFCtruth[1,2]=CFCtruth[2,3]=CFCtruth[0,0]=CFCtruth[1,1]=CFCtruth[2,2]=CFCtruth[3,3]=1
60
+ data = smspikes.T
61
+ return data,CFCtruth
62
+
63
+ def simulate_ctrnn(n_ctrnn, p, w, tau, noise=1):
64
+ """Simulates a CTRNN.
65
+
66
+ Args:
67
+ n_ctrnn: Number of time points in the CTRNN
68
+ p: Number of nodes.
69
+ w: Weight matrix.
70
+ tau: Time constant of the CTRNN.
71
+ noise: Noise level in the CTRNN.
72
+
73
+ Returns:
74
+ u: (numpy.array) CTRNN time series of shape (p,n_ctrnn).
75
+ """
76
+ e=np.exp(1)
77
+ u=np.zeros((p,n_ctrnn))
78
+ for n in range(n_ctrnn-1):
79
+ for i in range(p):
80
+ In=np.random.normal(1,noise)
81
+ u[i,(n+1)] = u[i,n] - ((e*u[i,n])/tau) + e*np.sum(w[:,i]*np.tanh(u[:,n]))/tau + e*In/tau#np.tanh(u[:,n])
82
+ return u
timeawarepc/tpc.py ADDED
@@ -0,0 +1,155 @@
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
+ # %%
@@ -0,0 +1,129 @@
1
+ """Helper functions for TPC Algorithm
2
+ """
3
+ import numpy as np
4
+ import networkx as nx
5
+ def data_transformed(data, maxdelay):
6
+ """Implements Step 1 (Time Delay) of TPC,
7
+ to construct the data with time-delayed samples.
8
+
9
+ Args:
10
+ data: (numpy.array) of shape (n,p) with n time recordings of p nodes.
11
+ maxdelay: (int) Maximum time-delay of interactions
12
+
13
+ Returns:
14
+ data2: (numpy.array) Data with columns as Unrolled DAG nodes (column index v*maxdelay+t = node (v,t)) and rows having time-delayed samples.
15
+ """
16
+ n = data.shape[0]
17
+ p = data.shape[1]
18
+ maxdelay1=maxdelay+1
19
+ new_n = int(np.floor((n-maxdelay)/(2*maxdelay1))*(2*maxdelay1))
20
+ data=data[:new_n,:]
21
+ data2=np.zeros((int(new_n/(2*maxdelay1)),p*maxdelay1))
22
+ for i in range(p):
23
+ for j in range(maxdelay1):
24
+ data2[:,maxdelay1*i+j]=data[j::(2*maxdelay1),i]
25
+ return data2
26
+
27
+ def orient(g,maxdelay,m):
28
+ """Implements Step 4 (Orient) of TPC,
29
+ to correct future to past edges in Estimated Unrolled DAG
30
+
31
+ Args:
32
+ g: (networkx.DiGraph) Unrolled Causal DAG estimate outputted by Step 3 (PC) of TPC.
33
+ maxdelay: (int) Maximum time-delay of interaction
34
+ m: (int) Number of neurons/nodes in original data
35
+
36
+ Returns:
37
+ g1: (networkx.DiGraph) Re-Oriented Unrolled Causal DAG estimate.
38
+ """
39
+ labels=np.arange(0,(maxdelay+1)*m)
40
+
41
+ edge=set([])
42
+ labelmat=labels.reshape((m,maxdelay+1))
43
+ for i in range(m):
44
+ for k in range(m):
45
+ for j in range(maxdelay+1):
46
+ for l in range(maxdelay+1):
47
+ if j<=l:# and j>=l-maxdelay:
48
+ #if j==l-1:
49
+ if (labelmat[i,j],labelmat[k,l]) in g.edges or (labelmat[k,l],labelmat[i,j]) in g.edges:
50
+ edge = edge | {(labelmat[i,j],labelmat[k,l])}
51
+ g1 = nx.DiGraph()
52
+ g1.add_nodes_from(g.nodes)
53
+ g1.add_edges_from(edge)
54
+ return g1
55
+
56
+ def causaleff_ida(g,data):
57
+ """Estimate interventional causal effects in the Unrolled DAG in TPC.
58
+
59
+ Args:
60
+ g: (networkx.DiGraph) Estimated Unrolled Causal DAG outputted by Step 4 (Orient) of TPC.
61
+ data: (numpy.array) with variables for Unrolled Causal DAG in columns and time-delayed samples in rows
62
+
63
+ Returns:
64
+ causaleff: (numpy.array) Interventional Causal Effects among pairs of nodes in the Unrolled Causal DAG.
65
+ """
66
+ Nodes = list(g.nodes)
67
+ causaleff=np.zeros((len(Nodes),len(Nodes)))
68
+
69
+ for x in Nodes:
70
+ for y in Nodes:
71
+ if x!=y:
72
+ pa_x = list(g.predecessors(x))
73
+ pa_y = list(g.predecessors(y))
74
+ if x not in pa_x:
75
+ regressors = pa_x + [x]
76
+ else:
77
+ regressors = pa_x
78
+ if y in pa_x:
79
+ # causaleff[x,y] = 0#uncomment this and comment below for legacy version
80
+ regressors = [item for item in regressors if item != y]
81
+ # else:#uncomment for legacy version
82
+ X=np.asarray(data[:,regressors])
83
+ Y=np.asarray(data[:,y])
84
+ X0=np.hstack((np.ones((X.shape[0],1)),X))
85
+ lm_out = np.linalg.lstsq(X0,Y,rcond=None)[0]
86
+ causaleff[x,y] = lm_out[regressors.index(x)+1]
87
+ return causaleff
88
+
89
+ def return_finaledges(g,causaleff,maxdelay,m):
90
+ """Implements Step 5 (Rolled CFC-DPGM) of TPC, to obtain
91
+ the Rolled CFC-DPGM and its weights from the Unrolled Causal DAG
92
+
93
+ Args:
94
+ g: (networkx.DiGraph) Oriented Unrolled Causal DAG estimate outputted by Step 4 (Orient) of TPC.
95
+ causaleff: (numpy.array) Estimated Interventional Causal Effects in Unrolled DAG
96
+ maxdelay: (int) Maximum time-delay of interaction
97
+ m: (int) Number of neurons/nodes in original data
98
+
99
+ Returns:
100
+ adjacency: (numpy.array) Adjacency matrix for the Rolled CFC-DPGM estimate.
101
+ wdir: (numpy.array) Weights for direct connections in the Rolled CFC-DPGM.
102
+ windir: (numpy.array) Weights for indirect/direct connections in the Rolled CFC-DPGM.
103
+ """
104
+ labels=np.arange(0,(maxdelay+1)*m)
105
+ labelmat=labels.reshape((m,maxdelay+1))
106
+ causaleff1 = np.zeros((m,m))
107
+ causaleff2 = np.zeros((m,m))
108
+ edge_mat = np.zeros((m,m))==1
109
+ g1=g
110
+ for i in range(m):
111
+ for k in range(m):
112
+ acc=[]
113
+ ansacc=[]
114
+ for j in range(maxdelay+1):
115
+ for l in range(maxdelay+1):
116
+ if j<=l:
117
+ if (labelmat[i,j],labelmat[k,l]) in g1.edges:
118
+ edge_mat[i,k]=True
119
+ acc.append(causaleff[labelmat[i,j],labelmat[k,l]])
120
+ if labelmat[i,j] in nx.ancestors(g1,labelmat[k,l]):
121
+ ansacc.append(causaleff[labelmat[i,j],labelmat[k,l]])
122
+ if len(acc)>0:
123
+ causaleff1[i,k] = np.mean(acc)
124
+ if len(ansacc)>0:
125
+ causaleff2[i,k] = np.mean(ansacc)
126
+ adjacency = edge_mat.astype(int)
127
+ wdir = causaleff1
128
+ windir = causaleff2
129
+ return adjacency, wdir, windir
@@ -0,0 +1,60 @@
1
+ """
2
+ Use this tutorial to try out main functionalities of this library and test if it has installed properly.
3
+ In this tutorial, you will be estimating causal functional connectivity (CFC) from simulated datasets.
4
+ """
5
+ #%%
6
+ import networkx as nx
7
+ import numpy as np
8
+ from numpy.random import default_rng
9
+ rng = default_rng(seed=111)
10
+ from timeawarepc.tpc import cfc_tpc, cfc_pc
11
+ from timeawarepc.simulate_data import *
12
+ from timeawarepc.find_cfc import *
13
+ #%%
14
+ # Simulate a dataset.
15
+ # Specify a data generating model: model = "lingauss" for Linear Gaussian VAR, "nonlinnongauss" for Non-Linear Non-Gaussian VAR, "ctrnn" for Continuous Time Recurrent Neural Network.
16
+ # T is the number of time recordings (default 1000), noise is the noise std. deviation (default 1).
17
+ # Note that number of neurons = 4 and max delay of interaction = 1 indices are fixed in this tutorial.
18
+ model = 'lingauss'
19
+ T=1000
20
+ noise = 1
21
+
22
+ data, CFCtruth = simulate_data(model, T = 1000, noise = 1)
23
+
24
+ #%%Estimate CFC by find_cfc(), a convenient wrapper around different methods in this library.
25
+ #TODO: Specify a method_name. 'TPC' for Time-Aware PC Algorithm, 'PC' for PC Algorithm, 'GC' for Granger Causality.
26
+ method_name = 'TPC'#'GC'#'PC'
27
+
28
+ alpha = 0.05
29
+ maxdelay=1
30
+ isgauss = (model == 'lingauss')
31
+ adjmat, causaleffmat = find_cfc(data,method_name,alpha=alpha,maxdelay=maxdelay,isgauss=isgauss)
32
+
33
+ #%%Compare the Ground Truth and Estimated CFC.
34
+ print('Ground Truth CFC adjacency:')
35
+ print(CFCtruth)
36
+ print('Estimated CFC adjacency by '+method_name+':')
37
+ print(adjmat)
38
+
39
+ #%%Alternatively, you may estimate CFC using the python functions for individual methods.
40
+ #TODO: Specify a method_name. 'TPC' for Time-Aware PC Algorithm, 'PC' for PC Algorithm, 'GC' for Granger Causality.
41
+ method_name = 'TPC'#'GC'#'PC'
42
+
43
+ alpha = 0.05
44
+ isgauss = (model == 'lingauss')
45
+ if method_name == 'TPC':
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)
50
+ elif method_name == 'PC':
51
+ adjmat, causaleffmat = cfc_pc(data,alpha,isgauss=isgauss)
52
+ elif method_name == 'GC':
53
+ from timeawarepc.gc import cfc_gc
54
+ adjmat, causaleffmat = cfc_gc(data,maxdelay,alpha)
55
+ #%%Compare the Ground Truth and Estimated CFC.
56
+ print('Ground Truth CFC adjacency:')
57
+ print(CFCtruth)
58
+ print('Estimated CFC adjacency by '+method_name+':')
59
+ print(adjmat)
60
+
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: timeawarepc
3
+ Version: 1.2.0
4
+ Summary: Time-Aware PC Python Package
5
+ Home-page: https://github.com/biswasr/TimeAwarePC
6
+ Author: Rahul Biswas
7
+ Author-email: rahul.biswas@ucsf.edu
8
+ License: MIT
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Requires-Python: ==3.10.*
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: numpy
17
+ Requires-Dist: pandas
18
+ Requires-Dist: rpy2==3.5.11
19
+ Requires-Dist: networkx
20
+ Requires-Dist: scipy
21
+ Dynamic: author
22
+ Dynamic: author-email
23
+ Dynamic: classifier
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: home-page
27
+ Dynamic: license
28
+ Dynamic: license-file
29
+ Dynamic: requires-dist
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # TimeAwarePC: A Python Package for Finding Causal Connectivity from Time Series Data [![image](https://img.shields.io/pypi/v/timeawarepc.svg)](https://pypi.python.org/pypi/timeawarepc) [![Documentation Status](https://readthedocs.org/projects/timeawarepc/badge/?version=latest)](https://timeawarepc.readthedocs.io/en/latest/?badge=latest)
34
+ **TimeAwarePC** is a Python package that implements the Time-Aware PC Algorithm for finding the **Causal Functional Connectivity** from time series data, based on recent research in directed probabilistic graphical modeling with time series [[1]](#Citation). The package also includes implementations of Granger Causality and the PC algorithm.
35
+
36
+ <div align="center">
37
+ <a href="https://github.com/shlizee/TimeAwarePC"><img src="https://github.com/shlizee/TimeAwarePC/blob/main/imgs/Schematic-msgs.png?raw=true" width="500px" height="250px"></a>
38
+ </div>
39
+
40
+ ## Installation
41
+
42
+ You can get the latest version of TimeAwarePC as follows.
43
+
44
+ ```
45
+ $ pip install timeawarepc
46
+ ```
47
+
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:
53
+ ```
54
+ > install.packages("BiocManager")
55
+ > BiocManager::install("graph")
56
+ > BiocManager::install("RBGL")
57
+ > install.packages("pcalg")
58
+ > install.packages("https://cran.r-project.org/src/contrib/Archive/kpcalg/kpcalg_1.0.1.tar.gz")
59
+ ```
60
+ <!-- - In addition, if you like to use Granger Causality functions in this package, please separately install nitime as follows:
61
+ ```
62
+ pip install nitime
63
+ ``` -->
64
+
65
+ ## Documentation
66
+
67
+ [Documentation is available at readthedocs.org](https://timeawarepc.readthedocs.io/en/latest/)
68
+
69
+ ## Tutorial
70
+
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.
72
+ <!--
73
+ ## Documentation
74
+
75
+ [Documentation is available at readthedocs.org](https://timeaware-pc.readthedocs.io/en/latest/) -->
76
+
77
+ ## Contributing
78
+
79
+ Your help is absolutely welcome! Please do reach out or create a feature branch!
80
+
81
+ ## Citation
82
+
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
84
+
85
+ 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
+
87
+
88
+ ## References
89
+
90
+ R Clay Reid. (2012) From functional architecture to functional connectomics. Neuron, 75(2):209–217.
91
+
92
+ Smith, S. M., Miller, K. L., Salimi-Khorshidi, G., Webster, M., Beckmann, C. F., Nichols, T. E., ... & Woolrich, M. W. (2011). Network modelling methods for FMRI. Neuroimage, 54(2), 875-891.
93
+
94
+ Judea Pearl. (2009) Causality. Cambridge University press.
95
+
96
+ Markus Kalisch and Peter Bhlmann. (2007) Estimating high-dimensional directed acyclic graphs with the pc-algorithm. In The Journal of Machine Learning Research, Vol. 8, pp. 613-636.
97
+
98
+ Peter Spirtes, Clark N Glymour, Richard Scheines, and David Heckerman. (2000) Causation, prediction, and search. MIT press.
99
+
100
+
101
+
@@ -0,0 +1,14 @@
1
+ timeawarepc/__init__.py,sha256=F2EpzcGvg2i3vTRyKh06f2vUseuzW7lJdTk3qCIoGhY,200
2
+ timeawarepc/find_cfc.py,sha256=pdAyJZLs83lJqnOhmX7TVYSSue2GUhUVK2MbKmB8SWk,1811
3
+ timeawarepc/gc.py,sha256=X7TsZEL5Yx7DN6Df25s_tBJjpk0rBvxI62nmqt7UOic,1222
4
+ timeawarepc/pcalg.py,sha256=ymhW0Cm4ooeDFIEt116zFeU0qThykCUWFlJnKGzo9y0,73496
5
+ timeawarepc/pcalg_helpers.py,sha256=0QtTlyPco-4840a-Nf0ctP-BT7ANOOdCh7LTKO2pQe0,1613
6
+ timeawarepc/simulate_data.py,sha256=17uI0zj5ubOtj9wEbKxcVdAPy-ZjKvNlwkCqUUCiAVQ,3267
7
+ timeawarepc/tpc.py,sha256=_VheKz4fVNm369q-bj77Esdf_ylru983VmI1_vIreVM,6780
8
+ timeawarepc/tpc_helpers.py,sha256=Y-DWGNcnUuUvlZ8a25s4KWX-_PEsMFPJXkNhbRdl21g,5124
9
+ timeawarepc/tutorial.py,sha256=8vo2sqhgQh01aSk_6y0FrO2_x0YhVOObGIlFj09E65c,2362
10
+ timeawarepc-1.2.0.dist-info/licenses/LICENSE,sha256=XF8u-3WEhRnvXx2JgSJBDcZkBybyCxMJdohxsmylehM,1069
11
+ timeawarepc-1.2.0.dist-info/METADATA,sha256=A_5G0RjyogE65D9oyHjjhUybCiG5fWQNkpWeYnaEJ2E,4199
12
+ timeawarepc-1.2.0.dist-info/WHEEL,sha256=7mLNlhqgufj7fxENZhtzRraBDfDi3AITas5GqZGp0zk,109
13
+ timeawarepc-1.2.0.dist-info/top_level.txt,sha256=fWRzwrasctBHFwZkVXNFkWwllx1vLtaQyUBHfAJSzjQ,12
14
+ timeawarepc-1.2.0.dist-info/RECORD,,
@@ -0,0 +1,6 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py2-none-any
5
+ Tag: py3-none-any
6
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Rahul Biswas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ timeawarepc