Skip to content

Commit 258fa0f

Browse files
authored
Merge pull request #102 from justinehansen/metrics
[ENH] Added rich_feeder_peripheral function
2 parents 7be5d95 + 5ca4610 commit 258fa0f

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed

netneurotools/metrics.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
import numpy as np
88
from scipy.linalg import expm
9+
from scipy.stats import ttest_ind
10+
from bct import degrees_und
911

1012

1113
def communicability_bin(adjacency, normalize=False):
@@ -99,3 +101,86 @@ def communicability_wei(adjacency):
99101
cmc[np.diag_indices_from(cmc)] = 0
100102

101103
return cmc
104+
105+
106+
def rich_feeder_peripheral(x, sc, stat='median'):
107+
"""
108+
Calculates connectivity values in rich, feeder, and peripheral edges.
109+
110+
Parameters
111+
----------
112+
x : (N, N) numpy.ndarray
113+
Symmetric correlation or connectivity matrix
114+
sc : (N, N) numpy.ndarray
115+
Binary structural connectivity matrix
116+
stat : {'mean', 'median'}, optional
117+
Statistic to use over rich/feeder/peripheral links. Default: 'median'
118+
119+
Returns
120+
-------
121+
rfp : (3, k) numpy.ndarray
122+
Array of median rich (0), feeder (1), and peripheral (2)
123+
values, defined by `x`. `k` is the maximum degree defined on `sc`.
124+
pvals : (3, k) numpy.ndarray
125+
p-value for each link, computed using Welch's t-test.
126+
Rich links are compared against non-rich links. Feeder links are
127+
compared against peripheral links. Peripheral links are compared
128+
against feeder links. T-test is one-sided.
129+
130+
Author
131+
------
132+
This code was written by Justine Hansen who promises to fix and even
133+
optimize the code should any issues arise, provided you let her know.
134+
"""
135+
136+
stats = ['mean', 'median']
137+
if stat not in stats:
138+
raise ValueError(f'Provided stat {stat} not valid.\
139+
Must be one of {stats}')
140+
141+
nnodes = len(sc)
142+
mask = np.triu(np.ones(nnodes), 1) > 0
143+
node_degree = degrees_und(sc)
144+
k = np.max(node_degree).astype(np.int64)
145+
rfp_label = np.zeros((len(sc[mask]), k))
146+
147+
for degthresh in range(k): # for each degree threshold
148+
hub_idx = np.where(node_degree >= degthresh) # find the hubs
149+
hub = np.zeros([nnodes, 1])
150+
hub[hub_idx, :] = 1
151+
152+
rfp = np.zeros([nnodes, nnodes]) # for each link, define rfp
153+
for edge1 in range(nnodes):
154+
for edge2 in range(nnodes):
155+
if hub[edge1] + hub[edge2] == 2:
156+
rfp[edge1, edge2] = 1 # rich
157+
if hub[edge1] + hub[edge2] == 1:
158+
rfp[edge1, edge2] = 2 # feeder
159+
if hub[edge1] + hub[edge2] == 0:
160+
rfp[edge1, edge2] = 3 # peripheral
161+
rfp_label[:, degthresh] = rfp[mask]
162+
163+
rfp = np.zeros([3, k])
164+
pvals = np.zeros([3, k])
165+
for degthresh in range(k):
166+
167+
redfunc = np.median if stat == 'median' else np.mean
168+
for linktype in range(3):
169+
rfp[linktype, degthresh] = redfunc(x[mask][rfp_label[:, degthresh]
170+
== linktype + 1])
171+
172+
# p-value (one-sided Welch's t-test)
173+
_, pvals[0, degthresh] = ttest_ind(
174+
x[mask][rfp_label[:, degthresh] == 1],
175+
x[mask][rfp_label[:, degthresh] != 1],
176+
equal_var=False, alternative='greater')
177+
_, pvals[1, degthresh] = ttest_ind(
178+
x[mask][rfp_label[:, degthresh] == 2],
179+
x[mask][rfp_label[:, degthresh] == 3],
180+
equal_var=False, alternative='greater')
181+
_, pvals[2, degthresh] = ttest_ind(
182+
x[mask][rfp_label[:, degthresh] == 3],
183+
x[mask][rfp_label[:, degthresh] == 2],
184+
equal_var=False, alternative='greater')
185+
186+
return rfp, pvals

0 commit comments

Comments
 (0)