Skip to content

Commit af0b03a

Browse files
committed
Write inter_node_distances w/ test, update nodal metric functions.
1 parent fe7ce47 commit af0b03a

File tree

2 files changed

+85
-45
lines changed

2 files changed

+85
-45
lines changed

brainx/metrics.py

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,48 @@
1212
#-----------------------------------------------------------------------------
1313
# Functions
1414
#-----------------------------------------------------------------------------
15+
16+
def inter_node_distances(G, n_nodes):
17+
"""Compute the shortest path lengths between all nodes in G.
18+
19+
This performs the same operation as NetworkX's
20+
all_pairs_shortest_path_lengths with two exceptions: Here, self
21+
paths are excluded from the dictionary returned, and the distance
22+
between disconnected nodes is set to infinity. The latter
23+
difference is consistent with the Brain Connectivity Toolbox for
24+
Matlab.
25+
26+
Parameters
27+
----------
28+
G: networkx Graph
29+
An undirected graph.
30+
31+
n_nodes: integer
32+
Number of nodes in G.
33+
34+
Returns
35+
-------
36+
lengths: dictionary
37+
Dictionary of shortest path lengths keyed by source and target.
38+
39+
Notes
40+
-----
41+
This function assumes the nodes are labeled 0 to n_nodes - 1.
42+
43+
"""
44+
lengths = nx.all_pairs_shortest_path_length(G)
45+
node_labels = range(n_nodes)
46+
for src in node_labels:
47+
lengths[src].pop(src)
48+
for targ in node_labels:
49+
if src != targ:
50+
try:
51+
lengths[src][targ]
52+
except KeyError:
53+
lengths[src][targ] = np.inf
54+
return lengths
55+
56+
1557
def compute_sigma(arr,clustarr,lparr):
1658
""" Function for computing sigma given a graph array arr and clust and lp
1759
arrays from a pseudorandom graph for a particular block b."""
@@ -50,27 +92,11 @@ def nodal_pathlengths(G, n_nodes):
5092
infinity.
5193
5294
"""
53-
# float is the default dtype for np.zeros, but we'll choose it explicitly
54-
# in case numpy ever changes the default to something else.
55-
nodal_means = np.zeros(n_nodes, dtype=float)
56-
lengths = nx.all_pairs_shortest_path_length(G)
57-
# As stated in the Python documentation, "Keys and values are listed in an
58-
# arbitrary order which is non-random, varies across Python
59-
# implementations, and depends on the dictionary's history of insertions
60-
# and deletions." Thus, we cannot assume we'd traverse the nodes in
61-
# ascending order if we were to iterate through 'lengths'.
62-
node_labels = range(n_nodes)
63-
for src in node_labels:
64-
source_lengths = []
65-
for targ in node_labels:
66-
if src != targ:
67-
try:
68-
val = lengths[src][targ]
69-
except KeyError:
70-
val = np.inf
71-
source_lengths.append(val)
72-
nodal_means[src] = np.mean(source_lengths)
73-
return nodal_means
95+
lengths = inter_node_distances(G, n_nodes)
96+
# It's important we iterate through range(n_nodes) rather than the keys
97+
# of lengths, as the keys are not guaranteed to be in ascending order.
98+
nodal_means = [np.mean(lengths[src].values()) for src in range(n_nodes)]
99+
return np.array(nodal_means)
74100

75101

76102
def assert_no_selfloops(G):
@@ -185,17 +211,11 @@ def nodal_efficiency(G, n_nodes):
185211
186212
"""
187213
nodal_efficiencies = np.zeros(n_nodes, dtype=float)
188-
lengths = nx.all_pairs_shortest_path_length(G)
189-
node_labels = range(n_nodes)
190-
for src in node_labels:
191-
inverse_paths = []
192-
for targ in node_labels:
193-
if src != targ:
194-
try:
195-
val = lengths[src][targ]
196-
except KeyError:
197-
val = np.inf
198-
inverse_paths.append(1.0 / val)
214+
lengths = inter_node_distances(G, n_nodes)
215+
# It's important we iterate through range(n_nodes) rather than the keys
216+
# of lengths, as the keys are not guaranteed to be in ascending order.
217+
for src in range(n_nodes):
218+
inverse_paths = [1.0 / val for val in lengths[src].itervalues()]
199219
nodal_efficiencies[src] = np.mean(inverse_paths)
200220
return nodal_efficiencies
201221

@@ -231,7 +251,6 @@ def local_efficiency(G):
231251
return np.array(nodepaths)
232252

233253

234-
#@profile
235254
def local_efficiency(G):
236255
"""Compute array of local efficiency for the given graph.
237256

brainx/tests/test_metrics.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
class NodalMetricsTestCase(TestCase):
2323

2424
def setUp(self):
25+
# Distances for all node pairs:
26+
# 0-1: 1 1-2: 1 2-3: 1 3-4: 1
27+
# 0-2: 1 1-3: 2 2-4: 2
28+
# 0-3: 2 1-4: 3
29+
# 0-4: 3
2530
self.corr_mat = np.array([[0.0, 0.0, 0.0, 0.0, 0.0],
2631
[0.5, 0.0, 0.0, 0.0, 0.0],
2732
[0.3, 0.4, 0.0, 0.0, 0.0],
@@ -30,19 +35,40 @@ def setUp(self):
3035
self.n_nodes = self.corr_mat.shape[0]
3136
self.g = nx.from_numpy_matrix(self.corr_mat)
3237

33-
def test_nodal_pathlengths_conn(self):
34-
path_lengths = metrics.nodal_pathlengths(self.g, self.n_nodes)
38+
def test_inter_node_distances_conn(self):
39+
distances = metrics.inter_node_distances(self.g, self.n_nodes)
40+
desired = {0: {1: 1, 2: 1, 3: 2, 4: 3},
41+
1: {0: 1, 2: 1, 3: 2, 4: 3},
42+
2: {0: 1, 1: 1, 3: 1, 4: 2},
43+
3: {0: 2, 1: 2, 2: 1, 4: 1},
44+
4: {0: 3, 1: 3, 2: 2, 3: 1}}
45+
self.assertEqual(distances, desired)
46+
47+
def test_inter_node_distances_disconn(self):
48+
self.g.remove_edge(2, 3)
49+
# Now all nodes still have at least one edge, but not all nodes are
50+
# reachable from all others.
51+
distances = metrics.inter_node_distances(self.g, self.n_nodes)
3552
# Distances for all node pairs:
36-
# 0-1: 1 1-2: 1 2-3: 1 3-4: 1
37-
# 0-2: 1 1-3: 2 2-4: 2
38-
# 0-3: 2 1-4: 3
39-
# 0-4: 3
53+
# 0-1: 1 1-2: 1 2-3: Inf 3-4: 1
54+
# 0-2: 1 1-3: Inf 2-4: Inf
55+
# 0-3: Inf 1-4: Inf
56+
# 0-4: Inf
57+
desired = {0: {1: 1, 2: 1, 3: np.inf, 4: np.inf},
58+
1: {0: 1, 2: 1, 3: np.inf, 4: np.inf},
59+
2: {0: 1, 1: 1, 3: np.inf, 4: np.inf},
60+
3: {0: np.inf, 1: np.inf, 2: np.inf, 4: 1},
61+
4: {0: np.inf, 1: np.inf, 2: np.inf, 3: 1}}
62+
self.assertEqual(distances, desired)
63+
64+
def test_nodal_pathlengths_conn(self):
65+
mean_path_lengths = metrics.nodal_pathlengths(self.g, self.n_nodes)
4066
desired = 1.0 / (self.n_nodes - 1) * np.array([1 + 1 + 2 + 3,
4167
1 + 1 + 2 + 3,
4268
1 + 1 + 1 + 2,
4369
2 + 2 + 1 + 1,
4470
3 + 3 + 2 + 1])
45-
npt.assert_array_almost_equal(path_lengths, desired)
71+
npt.assert_array_almost_equal(mean_path_lengths, desired)
4672

4773
def test_nodal_pathlengths_disconn(self):
4874
self.g.remove_edge(2, 3)
@@ -64,11 +90,6 @@ def test_nodal_pathlengths_disconn(self):
6490

6591
def test_nodal_efficiency_conn(self):
6692
n_eff_array = metrics.nodal_efficiency(self.g, self.n_nodes)
67-
# Distances for all node pairs:
68-
# 0-1: 1 1-2: 1 2-3: 1 3-4: 1
69-
# 0-2: 1 1-3: 2 2-4: 2
70-
# 0-3: 2 1-4: 3
71-
# 0-4: 3
7293
desired = (1.0 / (self.n_nodes - 1) *
7394
np.array([1 + 1 + 1 / 2.0 + 1 / 3.0,
7495
1 + 1 + 1 / 2.0 + 1 / 3.0,

0 commit comments

Comments
 (0)