Skip to content

Commit dbb0add

Browse files
author
cindeem
committed
Merge pull request #5 from dabliss/dan-dev
Clean up some functions in util and metrics
2 parents c8f67e5 + 98aea63 commit dbb0add

File tree

4 files changed

+520
-173
lines changed

4 files changed

+520
-173
lines changed

brainx/metrics.py

Lines changed: 159 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,48 @@
55
# Imports
66
#-----------------------------------------------------------------------------
77

8-
98
import networkx as nx
109
import numpy as np
1110
from scipy import sparse
1211

1312
#-----------------------------------------------------------------------------
1413
# Functions
1514
#-----------------------------------------------------------------------------
15+
16+
def inter_node_distances(graph):
17+
"""Compute the shortest path lengths between all nodes in graph.
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+
graph: networkx Graph
29+
An undirected graph.
30+
31+
Returns
32+
-------
33+
lengths: dictionary
34+
Dictionary of shortest path lengths keyed by source and target.
35+
36+
"""
37+
lengths = nx.all_pairs_shortest_path_length(graph)
38+
node_labels = sorted(lengths)
39+
for src in node_labels:
40+
lengths[src].pop(src)
41+
for targ in node_labels:
42+
if src != targ:
43+
try:
44+
lengths[src][targ]
45+
except KeyError:
46+
lengths[src][targ] = np.inf
47+
return lengths
48+
49+
1650
def compute_sigma(arr,clustarr,lparr):
1751
""" Function for computing sigma given a graph array arr and clust and lp
1852
arrays from a pseudorandom graph for a particular block b."""
@@ -24,35 +58,41 @@ def compute_sigma(arr,clustarr,lparr):
2458

2559
return out
2660

27-
def nodal_pathlengths(G,n_nodes):
28-
""" Compute mean path length for each node.
29-
Note: it is unclear how to treat infinite path lengths. For now, I replace them with np.inf, but this may make taking the mean later on difficult
30-
Inputs: G graph data output from mkgraph; n_nodes number of nodes in graph"""
31-
nodal_means=np.zeros((n_nodes),dtype=float)
32-
lengths= nx.all_pairs_shortest_path_length(G)
33-
for src,pair in lengths.iteritems():
34-
source_paths=[]
35-
source_arr=np.array([])
36-
for targ,val in pair.items():
37-
if src==targ:
38-
continue # we want to include src,target repeats, right?
39-
source_paths.append(float(val))
40-
source_arr=np.array(source_paths)
41-
42-
if source_arr.size==0: #make the mean path length 0 if node is disconnected
43-
source_arr=np.array([np.nan])
44-
nodal_means[src]=source_arr.mean()
45-
#nodal_array=np.array(nodal_means)
46-
return nodal_means
47-
48-
def assert_no_selfloops(G):
49-
"""Raise an error if the graph G has any selfloops.
61+
62+
def nodal_pathlengths(graph):
63+
"""Compute mean path length for each node.
64+
65+
Parameters
66+
----------
67+
graph: networkx Graph
68+
An undirected graph.
69+
70+
Returns
71+
-------
72+
nodal_means: numpy array
73+
An array with each node's mean shortest path length to all other
74+
nodes. The array is in ascending order of node labels.
75+
76+
Notes
77+
-----
78+
Per the Brain Connectivity Toolbox for Matlab, the distance between
79+
one node and another that cannot be reached from it is set to
80+
infinity.
81+
5082
"""
51-
if G.nodes_with_selfloops():
83+
lengths = inter_node_distances(graph)
84+
nodal_means = [np.mean(lengths[src].values()) for src in sorted(lengths)]
85+
return np.array(nodal_means)
86+
87+
88+
def assert_no_selfloops(graph):
89+
"""Raise an error if the graph graph has any selfloops.
90+
"""
91+
if graph.nodes_with_selfloops():
5292
raise ValueError("input graph can not have selfloops")
5393

54-
#@profile
55-
def path_lengths(G):
94+
95+
def path_lengths(graph):
5696
"""Compute array of all shortest path lengths for the given graph.
5797
5898
The length of the output array is the number of unique pairs of nodes that
@@ -64,12 +104,12 @@ def path_lengths(G):
64104
65105
Parameters
66106
----------
67-
G : an undirected graph object.
107+
graph : an undirected graph object.
68108
"""
69109

70-
assert_no_selfloops(G)
110+
assert_no_selfloops(graph)
71111

72-
length = nx.all_pairs_shortest_path_length(G)
112+
length = nx.all_pairs_shortest_path_length(graph)
73113
paths = []
74114
seen = set()
75115
for src,targets in length.iteritems():
@@ -82,7 +122,7 @@ def path_lengths(G):
82122

83123

84124
#@profile
85-
def path_lengthsSPARSE(G):
125+
def path_lengthsSPARSE(graph):
86126
"""Compute array of all shortest path lengths for the given graph.
87127
88128
XXX - implementation using scipy.sparse. This might be faster for very
@@ -99,14 +139,14 @@ def path_lengthsSPARSE(G):
99139
100140
Parameters
101141
----------
102-
G : an undirected graph object.
142+
graph : an undirected graph object.
103143
"""
104144

105-
assert_no_selfloops(G)
145+
assert_no_selfloops(graph)
106146

107-
length = nx.all_pairs_shortest_path_length(G)
147+
length = nx.all_pairs_shortest_path_length(graph)
108148

109-
nnod = G.number_of_nodes()
149+
nnod = graph.number_of_nodes()
110150
paths_mat = sparse.dok_matrix((nnod,nnod))
111151

112152
for src,targets in length.iteritems():
@@ -116,45 +156,59 @@ def path_lengthsSPARSE(G):
116156
return sparse.triu(paths_mat,1).data
117157

118158

119-
def glob_efficiency(G):
159+
def glob_efficiency(graph):
120160
"""Compute array of global efficiency for the given graph.
121161
122162
Global efficiency: returns a list of the inverse path length matrix
123163
across all nodes The mean of this value is equal to the global efficiency
124164
of the network."""
125165

126-
return 1.0/path_lengths(G)
127-
128-
def nodal_efficiency(G):
129-
"""Compute array of global efficiency for the given graph.
166+
return 1.0/path_lengths(graph)
130167

131-
Nodal efficiency: XXX - define."""
132-
133-
nodepaths=[]
134-
length = nx.all_pairs_shortest_path_length(G)
135-
for src,targets in length.iteritems():
136-
paths=[]
137-
for targ,val in targets.items():
138-
if src==targ:
139-
continue
140-
141-
paths.append(1.0/val)
142-
143-
nodepaths.append(np.mean(paths))
144-
145-
return np.array(nodepaths)
146168

147-
#@profile
148-
def local_efficiency(G):
169+
def nodal_efficiency(graph):
170+
"""Return array with nodal efficiency for each node in graph.
171+
172+
See Achard and Bullmore (2007, PLoS Comput Biol) for the definition
173+
of nodal efficiency.
174+
175+
Parameters
176+
----------
177+
graph: networkx Graph
178+
An undirected graph.
179+
180+
Returns
181+
-------
182+
nodal_efficiencies: numpy array
183+
An array with the nodal efficiency for each node in graph, in
184+
the order specified by node_labels. The array is in ascending
185+
order of node labels.
186+
187+
Notes
188+
-----
189+
Per the Brain Connectivity Toolbox for Matlab, the distance between
190+
one node and another that cannot be reached from it is set to
191+
infinity.
192+
193+
"""
194+
lengths = inter_node_distances(graph)
195+
nodal_efficiencies = np.zeros(len(lengths), dtype=float)
196+
for src in sorted(lengths):
197+
inverse_paths = [1.0 / val for val in lengths[src].itervalues()]
198+
nodal_efficiencies[src] = np.mean(inverse_paths)
199+
return nodal_efficiencies
200+
201+
202+
def local_efficiency(graph):
149203
"""Compute array of global efficiency for the given grap.h
150204
151205
Local efficiency: returns a list of paths that represent the nodal
152206
efficiencies across all nodes with their direct neighbors"""
153207

154208
nodepaths=[]
155-
length=nx.all_pairs_shortest_path_length(G)
156-
for n in G.nodes():
157-
nneighb= nx.neighbors(G,n)
209+
length=nx.all_pairs_shortest_path_length(graph)
210+
for n in graph.nodes():
211+
nneighb= nx.neighbors(graph,n)
158212

159213
paths=[]
160214
for src,targets in length.iteritems():
@@ -176,19 +230,18 @@ def local_efficiency(G):
176230
return np.array(nodepaths)
177231

178232

179-
#@profile
180-
def local_efficiency(G):
233+
def local_efficiency(graph):
181234
"""Compute array of local efficiency for the given graph.
182235
183236
Local efficiency: returns a list of paths that represent the nodal
184237
efficiencies across all nodes with their direct neighbors"""
185238

186-
assert_no_selfloops(G)
239+
assert_no_selfloops(graph)
187240

188241
nodepaths = []
189-
length = nx.all_pairs_shortest_path_length(G)
190-
for n in G:
191-
nneighb = set(nx.neighbors(G,n))
242+
length = nx.all_pairs_shortest_path_length(graph)
243+
for n in graph:
244+
nneighb = set(nx.neighbors(graph,n))
192245

193246
paths = []
194247
for nei in nneighb:
@@ -205,20 +258,20 @@ def local_efficiency(G):
205258
return np.array(nodepaths)
206259

207260

208-
def dynamical_importance(G):
209-
"""Compute dynamical importance for G.
261+
def dynamical_importance(graph):
262+
"""Compute dynamical importance for graph.
210263
211264
Ref: Restrepo, Ott, Hunt. Phys. Rev. Lett. 97, 094102 (2006)
212265
"""
213266
# spectrum of the original graph
214-
eigvals = nx.adjacency_spectrum(G)
267+
eigvals = nx.adjacency_spectrum(graph)
215268
lambda0 = eigvals[0]
216-
# Now, loop over all nodes in G, and for each, make a copy of G, remove
269+
# Now, loop over all nodes in graph, and for each, make a copy of graph, remove
217270
# that node, and compute the change in lambda.
218-
nnod = G.number_of_nodes()
271+
nnod = graph.number_of_nodes()
219272
dyimp = np.empty(nnod,float)
220273
for n in range(nnod):
221-
gn = G.copy()
274+
gn = graph.copy()
222275
gn.remove_node(n)
223276
lambda_n = nx.adjacency_spectrum(gn)[0]
224277
dyimp[n] = lambda0 - lambda_n
@@ -227,22 +280,22 @@ def dynamical_importance(G):
227280
return dyimp
228281

229282

230-
def weighted_degree(G):
283+
def weighted_degree(graph):
231284
"""Return an array of degrees that takes weights into account.
232285
233286
For unweighted graphs, this is the same as the normal degree() method
234287
(though we return an array instead of a list).
235288
"""
236-
amat = nx.adj_matrix(G).A # get a normal array out of it
289+
amat = nx.adj_matrix(graph).A # get a normal array out of it
237290
return abs(amat).sum(0) # weights are sums across rows
238291

239292

240-
def graph_summary(G):
293+
def graph_summary(graph):
241294
"""Compute a set of statistics summarizing the structure of a graph.
242295
243296
Parameters
244297
----------
245-
G : a graph object.
298+
graph : a graph object.
246299
247300
threshold : float, optional
248301
@@ -252,36 +305,44 @@ def graph_summary(G):
252305
"""
253306

254307
# Average path length
255-
lp = path_lengths(G)
256-
clust = np.array(nx.clustering(G).values())
257-
glob_eff = glob_efficiency(G)
258-
loc_eff = local_efficiency(G)
308+
lp = path_lengths(graph)
309+
clust = np.array(nx.clustering(graph).values())
310+
glob_eff = glob_efficiency(graph)
311+
loc_eff = local_efficiency(graph)
259312

260313
return dict( lp=lp.mean(), clust=clust.mean(), glob_eff=glob_eff.mean(),
261314
loc_eff=loc_eff.mean() )
262315

263-
def nodal_summaryOut(G, n_nodes):
264-
""" Compute statistics for individual nodes
316+
317+
def nodal_summaryOut(graph):
318+
"""Compute statistics for individual nodes.
265319
266320
Parameters
267321
----------
268-
G: graph data output from mkgraph
269-
out: array output from nodal_summaryOut, so can keep appending
270-
cost: cost value for these calculations
271-
n_nodes: number of nodes in graph.
272-
322+
graph: networkx graph
323+
An undirected graph.
324+
273325
Returns
274326
-------
327+
dictionary
328+
The keys of this dictionary are lp (which refers to path
329+
length), clust (clustering coefficient), b_cen (betweenness
330+
centrality), c_cen (closeness centrality), nod_eff (nodal
331+
efficiency), loc_eff (local efficiency), and deg (degree). The
332+
values are arrays (or lists, in some cases) of metrics, in
333+
ascending order of node labels.
275334
276-
A dict with: lp, clust, b_cen, c_cen, nod_eff, loc_eff, degree."""
277-
278-
lp = nodal_pathlengths(G,n_nodes) #can't use the regular one, because it substitutes [] for disconnected nodes
279-
clust = np.array(nx.clustering(G).values())
280-
b_cen = np.array(nx.betweenness_centrality(G).values())
281-
c_cen = np.array(nx.closeness_centrality(G).values())
282-
nod_eff=nodal_efficiency(G)
283-
loc_eff=local_efficiency(G)
284-
deg = G.degree().values()
285-
335+
"""
336+
lp = nodal_pathlengths(graph)
337+
clust_dict = nx.clustering(graph)
338+
clust = np.array([clust_dict[n] for n in sorted(clust_dict)])
339+
b_cen_dict = nx.betweenness_centrality(graph)
340+
b_cen = np.array([b_cen_dict[n] for n in sorted(b_cen_dict)])
341+
c_cen_dict = nx.closeness_centrality(graph)
342+
c_cen = np.array([c_cen_dict[n] for n in sorted(c_cen_dict)])
343+
nod_eff = nodal_efficiency(graph)
344+
loc_eff = local_efficiency(graph)
345+
deg_dict = graph.degree()
346+
deg = [deg_dict[n] for n in sorted(deg_dict)]
286347
return dict(lp=lp, clust=clust, b_cen=b_cen, c_cen=c_cen, nod_eff=nod_eff,
287-
loc_eff=loc_eff,deg=deg)
348+
loc_eff=loc_eff, deg=deg)

0 commit comments

Comments
 (0)