update
This commit is contained in:
20
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__init__.py
vendored
Normal file
20
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__init__.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
from .betweenness import *
|
||||
from .betweenness_subset import *
|
||||
from .closeness import *
|
||||
from .current_flow_betweenness import *
|
||||
from .current_flow_betweenness_subset import *
|
||||
from .current_flow_closeness import *
|
||||
from .degree_alg import *
|
||||
from .dispersion import *
|
||||
from .eigenvector import *
|
||||
from .group import *
|
||||
from .harmonic import *
|
||||
from .katz import *
|
||||
from .load import *
|
||||
from .percolation import *
|
||||
from .reaching import *
|
||||
from .second_order import *
|
||||
from .subgraph_alg import *
|
||||
from .trophic import *
|
||||
from .voterank_alg import *
|
||||
from .laplacian import *
|
||||
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/closeness.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/closeness.cpython-312.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/group.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/group.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/harmonic.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/harmonic.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/katz.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/katz.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/laplacian.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/laplacian.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/load.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/load.cpython-312.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/reaching.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/reaching.cpython-312.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/trophic.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/__pycache__/trophic.cpython-312.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
435
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/betweenness.py
vendored
Normal file
435
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/betweenness.py
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
"""Betweenness centrality measures."""
|
||||
from collections import deque
|
||||
from heapq import heappop, heappush
|
||||
from itertools import count
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms.shortest_paths.weighted import _weight_function
|
||||
from networkx.utils import py_random_state
|
||||
from networkx.utils.decorators import not_implemented_for
|
||||
|
||||
__all__ = ["betweenness_centrality", "edge_betweenness_centrality"]
|
||||
|
||||
|
||||
@py_random_state(5)
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def betweenness_centrality(
|
||||
G, k=None, normalized=True, weight=None, endpoints=False, seed=None
|
||||
):
|
||||
r"""Compute the shortest-path betweenness centrality for nodes.
|
||||
|
||||
Betweenness centrality of a node $v$ is the sum of the
|
||||
fraction of all-pairs shortest paths that pass through $v$
|
||||
|
||||
.. math::
|
||||
|
||||
c_B(v) =\sum_{s,t \in V} \frac{\sigma(s, t|v)}{\sigma(s, t)}
|
||||
|
||||
where $V$ is the set of nodes, $\sigma(s, t)$ is the number of
|
||||
shortest $(s, t)$-paths, and $\sigma(s, t|v)$ is the number of
|
||||
those paths passing through some node $v$ other than $s, t$.
|
||||
If $s = t$, $\sigma(s, t) = 1$, and if $v \in {s, t}$,
|
||||
$\sigma(s, t|v) = 0$ [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
k : int, optional (default=None)
|
||||
If k is not None use k node samples to estimate betweenness.
|
||||
The value of k <= n where n is the number of nodes in the graph.
|
||||
Higher values give better approximation.
|
||||
|
||||
normalized : bool, optional
|
||||
If True the betweenness values are normalized by `2/((n-1)(n-2))`
|
||||
for graphs, and `1/((n-1)(n-2))` for directed graphs where `n`
|
||||
is the number of nodes in G.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
Weights are used to calculate weighted shortest paths, so they are
|
||||
interpreted as distances.
|
||||
|
||||
endpoints : bool, optional
|
||||
If True include the endpoints in the shortest path counts.
|
||||
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
Note that this is only used if k is not None.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with betweenness centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
edge_betweenness_centrality
|
||||
load_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The algorithm is from Ulrik Brandes [1]_.
|
||||
See [4]_ for the original first published version and [2]_ for details on
|
||||
algorithms for variations and related metrics.
|
||||
|
||||
For approximate betweenness calculations set k=#samples to use
|
||||
k nodes ("pivots") to estimate the betweenness values. For an estimate
|
||||
of the number of pivots needed see [3]_.
|
||||
|
||||
For weighted graphs the edge weights must be greater than zero.
|
||||
Zero edge weights can produce an infinite number of equal length
|
||||
paths between pairs of nodes.
|
||||
|
||||
The total number of paths between source and target is counted
|
||||
differently for directed and undirected graphs. Directed paths
|
||||
are easy to count. Undirected paths are tricky: should a path
|
||||
from "u" to "v" count as 1 undirected path or as 2 directed paths?
|
||||
|
||||
For betweenness_centrality we report the number of undirected
|
||||
paths when G is undirected.
|
||||
|
||||
For betweenness_centrality_subset the reporting is different.
|
||||
If the source and target subsets are the same, then we want
|
||||
to count undirected paths. But if the source and target subsets
|
||||
differ -- for example, if sources is {0} and targets is {1},
|
||||
then we are only counting the paths in one direction. They are
|
||||
undirected paths but we are counting them in a directed way.
|
||||
To count them as undirected paths, each should count as half a path.
|
||||
|
||||
This algorithm is not guaranteed to be correct if edge weights
|
||||
are floating point numbers. As a workaround you can use integer
|
||||
numbers by multiplying the relevant edge attributes by a convenient
|
||||
constant factor (eg 100) and converting to integers.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Ulrik Brandes:
|
||||
A Faster Algorithm for Betweenness Centrality.
|
||||
Journal of Mathematical Sociology 25(2):163-177, 2001.
|
||||
https://doi.org/10.1080/0022250X.2001.9990249
|
||||
.. [2] Ulrik Brandes:
|
||||
On Variants of Shortest-Path Betweenness
|
||||
Centrality and their Generic Computation.
|
||||
Social Networks 30(2):136-145, 2008.
|
||||
https://doi.org/10.1016/j.socnet.2007.11.001
|
||||
.. [3] Ulrik Brandes and Christian Pich:
|
||||
Centrality Estimation in Large Networks.
|
||||
International Journal of Bifurcation and Chaos 17(7):2303-2318, 2007.
|
||||
https://dx.doi.org/10.1142/S0218127407018403
|
||||
.. [4] Linton C. Freeman:
|
||||
A set of measures of centrality based on betweenness.
|
||||
Sociometry 40: 35–41, 1977
|
||||
https://doi.org/10.2307/3033543
|
||||
"""
|
||||
betweenness = dict.fromkeys(G, 0.0) # b[v]=0 for v in G
|
||||
if k is None:
|
||||
nodes = G
|
||||
else:
|
||||
nodes = seed.sample(list(G.nodes()), k)
|
||||
for s in nodes:
|
||||
# single source shortest paths
|
||||
if weight is None: # use BFS
|
||||
S, P, sigma, _ = _single_source_shortest_path_basic(G, s)
|
||||
else: # use Dijkstra's algorithm
|
||||
S, P, sigma, _ = _single_source_dijkstra_path_basic(G, s, weight)
|
||||
# accumulation
|
||||
if endpoints:
|
||||
betweenness, _ = _accumulate_endpoints(betweenness, S, P, sigma, s)
|
||||
else:
|
||||
betweenness, _ = _accumulate_basic(betweenness, S, P, sigma, s)
|
||||
# rescaling
|
||||
betweenness = _rescale(
|
||||
betweenness,
|
||||
len(G),
|
||||
normalized=normalized,
|
||||
directed=G.is_directed(),
|
||||
k=k,
|
||||
endpoints=endpoints,
|
||||
)
|
||||
return betweenness
|
||||
|
||||
|
||||
@py_random_state(4)
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def edge_betweenness_centrality(G, k=None, normalized=True, weight=None, seed=None):
|
||||
r"""Compute betweenness centrality for edges.
|
||||
|
||||
Betweenness centrality of an edge $e$ is the sum of the
|
||||
fraction of all-pairs shortest paths that pass through $e$
|
||||
|
||||
.. math::
|
||||
|
||||
c_B(e) =\sum_{s,t \in V} \frac{\sigma(s, t|e)}{\sigma(s, t)}
|
||||
|
||||
where $V$ is the set of nodes, $\sigma(s, t)$ is the number of
|
||||
shortest $(s, t)$-paths, and $\sigma(s, t|e)$ is the number of
|
||||
those paths passing through edge $e$ [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
k : int, optional (default=None)
|
||||
If k is not None use k node samples to estimate betweenness.
|
||||
The value of k <= n where n is the number of nodes in the graph.
|
||||
Higher values give better approximation.
|
||||
|
||||
normalized : bool, optional
|
||||
If True the betweenness values are normalized by $2/(n(n-1))$
|
||||
for graphs, and $1/(n(n-1))$ for directed graphs where $n$
|
||||
is the number of nodes in G.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
Weights are used to calculate weighted shortest paths, so they are
|
||||
interpreted as distances.
|
||||
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
Note that this is only used if k is not None.
|
||||
|
||||
Returns
|
||||
-------
|
||||
edges : dictionary
|
||||
Dictionary of edges with betweenness centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality
|
||||
edge_load
|
||||
|
||||
Notes
|
||||
-----
|
||||
The algorithm is from Ulrik Brandes [1]_.
|
||||
|
||||
For weighted graphs the edge weights must be greater than zero.
|
||||
Zero edge weights can produce an infinite number of equal length
|
||||
paths between pairs of nodes.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] A Faster Algorithm for Betweenness Centrality. Ulrik Brandes,
|
||||
Journal of Mathematical Sociology 25(2):163-177, 2001.
|
||||
https://doi.org/10.1080/0022250X.2001.9990249
|
||||
.. [2] Ulrik Brandes: On Variants of Shortest-Path Betweenness
|
||||
Centrality and their Generic Computation.
|
||||
Social Networks 30(2):136-145, 2008.
|
||||
https://doi.org/10.1016/j.socnet.2007.11.001
|
||||
"""
|
||||
betweenness = dict.fromkeys(G, 0.0) # b[v]=0 for v in G
|
||||
# b[e]=0 for e in G.edges()
|
||||
betweenness.update(dict.fromkeys(G.edges(), 0.0))
|
||||
if k is None:
|
||||
nodes = G
|
||||
else:
|
||||
nodes = seed.sample(list(G.nodes()), k)
|
||||
for s in nodes:
|
||||
# single source shortest paths
|
||||
if weight is None: # use BFS
|
||||
S, P, sigma, _ = _single_source_shortest_path_basic(G, s)
|
||||
else: # use Dijkstra's algorithm
|
||||
S, P, sigma, _ = _single_source_dijkstra_path_basic(G, s, weight)
|
||||
# accumulation
|
||||
betweenness = _accumulate_edges(betweenness, S, P, sigma, s)
|
||||
# rescaling
|
||||
for n in G: # remove nodes to only return edges
|
||||
del betweenness[n]
|
||||
betweenness = _rescale_e(
|
||||
betweenness, len(G), normalized=normalized, directed=G.is_directed()
|
||||
)
|
||||
if G.is_multigraph():
|
||||
betweenness = _add_edge_keys(G, betweenness, weight=weight)
|
||||
return betweenness
|
||||
|
||||
|
||||
# helpers for betweenness centrality
|
||||
|
||||
|
||||
def _single_source_shortest_path_basic(G, s):
|
||||
S = []
|
||||
P = {}
|
||||
for v in G:
|
||||
P[v] = []
|
||||
sigma = dict.fromkeys(G, 0.0) # sigma[v]=0 for v in G
|
||||
D = {}
|
||||
sigma[s] = 1.0
|
||||
D[s] = 0
|
||||
Q = deque([s])
|
||||
while Q: # use BFS to find shortest paths
|
||||
v = Q.popleft()
|
||||
S.append(v)
|
||||
Dv = D[v]
|
||||
sigmav = sigma[v]
|
||||
for w in G[v]:
|
||||
if w not in D:
|
||||
Q.append(w)
|
||||
D[w] = Dv + 1
|
||||
if D[w] == Dv + 1: # this is a shortest path, count paths
|
||||
sigma[w] += sigmav
|
||||
P[w].append(v) # predecessors
|
||||
return S, P, sigma, D
|
||||
|
||||
|
||||
def _single_source_dijkstra_path_basic(G, s, weight):
|
||||
weight = _weight_function(G, weight)
|
||||
# modified from Eppstein
|
||||
S = []
|
||||
P = {}
|
||||
for v in G:
|
||||
P[v] = []
|
||||
sigma = dict.fromkeys(G, 0.0) # sigma[v]=0 for v in G
|
||||
D = {}
|
||||
sigma[s] = 1.0
|
||||
push = heappush
|
||||
pop = heappop
|
||||
seen = {s: 0}
|
||||
c = count()
|
||||
Q = [] # use Q as heap with (distance,node id) tuples
|
||||
push(Q, (0, next(c), s, s))
|
||||
while Q:
|
||||
(dist, _, pred, v) = pop(Q)
|
||||
if v in D:
|
||||
continue # already searched this node.
|
||||
sigma[v] += sigma[pred] # count paths
|
||||
S.append(v)
|
||||
D[v] = dist
|
||||
for w, edgedata in G[v].items():
|
||||
vw_dist = dist + weight(v, w, edgedata)
|
||||
if w not in D and (w not in seen or vw_dist < seen[w]):
|
||||
seen[w] = vw_dist
|
||||
push(Q, (vw_dist, next(c), v, w))
|
||||
sigma[w] = 0.0
|
||||
P[w] = [v]
|
||||
elif vw_dist == seen[w]: # handle equal paths
|
||||
sigma[w] += sigma[v]
|
||||
P[w].append(v)
|
||||
return S, P, sigma, D
|
||||
|
||||
|
||||
def _accumulate_basic(betweenness, S, P, sigma, s):
|
||||
delta = dict.fromkeys(S, 0)
|
||||
while S:
|
||||
w = S.pop()
|
||||
coeff = (1 + delta[w]) / sigma[w]
|
||||
for v in P[w]:
|
||||
delta[v] += sigma[v] * coeff
|
||||
if w != s:
|
||||
betweenness[w] += delta[w]
|
||||
return betweenness, delta
|
||||
|
||||
|
||||
def _accumulate_endpoints(betweenness, S, P, sigma, s):
|
||||
betweenness[s] += len(S) - 1
|
||||
delta = dict.fromkeys(S, 0)
|
||||
while S:
|
||||
w = S.pop()
|
||||
coeff = (1 + delta[w]) / sigma[w]
|
||||
for v in P[w]:
|
||||
delta[v] += sigma[v] * coeff
|
||||
if w != s:
|
||||
betweenness[w] += delta[w] + 1
|
||||
return betweenness, delta
|
||||
|
||||
|
||||
def _accumulate_edges(betweenness, S, P, sigma, s):
|
||||
delta = dict.fromkeys(S, 0)
|
||||
while S:
|
||||
w = S.pop()
|
||||
coeff = (1 + delta[w]) / sigma[w]
|
||||
for v in P[w]:
|
||||
c = sigma[v] * coeff
|
||||
if (v, w) not in betweenness:
|
||||
betweenness[(w, v)] += c
|
||||
else:
|
||||
betweenness[(v, w)] += c
|
||||
delta[v] += c
|
||||
if w != s:
|
||||
betweenness[w] += delta[w]
|
||||
return betweenness
|
||||
|
||||
|
||||
def _rescale(betweenness, n, normalized, directed=False, k=None, endpoints=False):
|
||||
if normalized:
|
||||
if endpoints:
|
||||
if n < 2:
|
||||
scale = None # no normalization
|
||||
else:
|
||||
# Scale factor should include endpoint nodes
|
||||
scale = 1 / (n * (n - 1))
|
||||
elif n <= 2:
|
||||
scale = None # no normalization b=0 for all nodes
|
||||
else:
|
||||
scale = 1 / ((n - 1) * (n - 2))
|
||||
else: # rescale by 2 for undirected graphs
|
||||
if not directed:
|
||||
scale = 0.5
|
||||
else:
|
||||
scale = None
|
||||
if scale is not None:
|
||||
if k is not None:
|
||||
scale = scale * n / k
|
||||
for v in betweenness:
|
||||
betweenness[v] *= scale
|
||||
return betweenness
|
||||
|
||||
|
||||
def _rescale_e(betweenness, n, normalized, directed=False, k=None):
|
||||
if normalized:
|
||||
if n <= 1:
|
||||
scale = None # no normalization b=0 for all nodes
|
||||
else:
|
||||
scale = 1 / (n * (n - 1))
|
||||
else: # rescale by 2 for undirected graphs
|
||||
if not directed:
|
||||
scale = 0.5
|
||||
else:
|
||||
scale = None
|
||||
if scale is not None:
|
||||
if k is not None:
|
||||
scale = scale * n / k
|
||||
for v in betweenness:
|
||||
betweenness[v] *= scale
|
||||
return betweenness
|
||||
|
||||
|
||||
@not_implemented_for("graph")
|
||||
def _add_edge_keys(G, betweenness, weight=None):
|
||||
r"""Adds the corrected betweenness centrality (BC) values for multigraphs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph.
|
||||
|
||||
betweenness : dictionary
|
||||
Dictionary mapping adjacent node tuples to betweenness centrality values.
|
||||
|
||||
weight : string or function
|
||||
See `_weight_function` for details. Defaults to `None`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
edges : dictionary
|
||||
The parameter `betweenness` including edges with keys and their
|
||||
betweenness centrality values.
|
||||
|
||||
The BC value is divided among edges of equal weight.
|
||||
"""
|
||||
_weight = _weight_function(G, weight)
|
||||
|
||||
edge_bc = dict.fromkeys(G.edges, 0.0)
|
||||
for u, v in betweenness:
|
||||
d = G[u][v]
|
||||
wt = _weight(u, v, d)
|
||||
keys = [k for k in d if _weight(u, v, {k: d[k]}) == wt]
|
||||
bc = betweenness[(u, v)] / len(keys)
|
||||
for k in keys:
|
||||
edge_bc[(u, v, k)] = bc
|
||||
|
||||
return edge_bc
|
||||
274
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/betweenness_subset.py
vendored
Normal file
274
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/betweenness_subset.py
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
"""Betweenness centrality measures for subsets of nodes."""
|
||||
import networkx as nx
|
||||
from networkx.algorithms.centrality.betweenness import (
|
||||
_add_edge_keys,
|
||||
)
|
||||
from networkx.algorithms.centrality.betweenness import (
|
||||
_single_source_dijkstra_path_basic as dijkstra,
|
||||
)
|
||||
from networkx.algorithms.centrality.betweenness import (
|
||||
_single_source_shortest_path_basic as shortest_path,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"betweenness_centrality_subset",
|
||||
"edge_betweenness_centrality_subset",
|
||||
]
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def betweenness_centrality_subset(G, sources, targets, normalized=False, weight=None):
|
||||
r"""Compute betweenness centrality for a subset of nodes.
|
||||
|
||||
.. math::
|
||||
|
||||
c_B(v) =\sum_{s\in S, t \in T} \frac{\sigma(s, t|v)}{\sigma(s, t)}
|
||||
|
||||
where $S$ is the set of sources, $T$ is the set of targets,
|
||||
$\sigma(s, t)$ is the number of shortest $(s, t)$-paths,
|
||||
and $\sigma(s, t|v)$ is the number of those paths
|
||||
passing through some node $v$ other than $s, t$.
|
||||
If $s = t$, $\sigma(s, t) = 1$,
|
||||
and if $v \in {s, t}$, $\sigma(s, t|v) = 0$ [2]_.
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
sources: list of nodes
|
||||
Nodes to use as sources for shortest paths in betweenness
|
||||
|
||||
targets: list of nodes
|
||||
Nodes to use as targets for shortest paths in betweenness
|
||||
|
||||
normalized : bool, optional
|
||||
If True the betweenness values are normalized by $2/((n-1)(n-2))$
|
||||
for graphs, and $1/((n-1)(n-2))$ for directed graphs where $n$
|
||||
is the number of nodes in G.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
Weights are used to calculate weighted shortest paths, so they are
|
||||
interpreted as distances.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with betweenness centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
edge_betweenness_centrality
|
||||
load_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The basic algorithm is from [1]_.
|
||||
|
||||
For weighted graphs the edge weights must be greater than zero.
|
||||
Zero edge weights can produce an infinite number of equal length
|
||||
paths between pairs of nodes.
|
||||
|
||||
The normalization might seem a little strange but it is
|
||||
designed to make betweenness_centrality(G) be the same as
|
||||
betweenness_centrality_subset(G,sources=G.nodes(),targets=G.nodes()).
|
||||
|
||||
The total number of paths between source and target is counted
|
||||
differently for directed and undirected graphs. Directed paths
|
||||
are easy to count. Undirected paths are tricky: should a path
|
||||
from "u" to "v" count as 1 undirected path or as 2 directed paths?
|
||||
|
||||
For betweenness_centrality we report the number of undirected
|
||||
paths when G is undirected.
|
||||
|
||||
For betweenness_centrality_subset the reporting is different.
|
||||
If the source and target subsets are the same, then we want
|
||||
to count undirected paths. But if the source and target subsets
|
||||
differ -- for example, if sources is {0} and targets is {1},
|
||||
then we are only counting the paths in one direction. They are
|
||||
undirected paths but we are counting them in a directed way.
|
||||
To count them as undirected paths, each should count as half a path.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Ulrik Brandes, A Faster Algorithm for Betweenness Centrality.
|
||||
Journal of Mathematical Sociology 25(2):163-177, 2001.
|
||||
https://doi.org/10.1080/0022250X.2001.9990249
|
||||
.. [2] Ulrik Brandes: On Variants of Shortest-Path Betweenness
|
||||
Centrality and their Generic Computation.
|
||||
Social Networks 30(2):136-145, 2008.
|
||||
https://doi.org/10.1016/j.socnet.2007.11.001
|
||||
"""
|
||||
b = dict.fromkeys(G, 0.0) # b[v]=0 for v in G
|
||||
for s in sources:
|
||||
# single source shortest paths
|
||||
if weight is None: # use BFS
|
||||
S, P, sigma, _ = shortest_path(G, s)
|
||||
else: # use Dijkstra's algorithm
|
||||
S, P, sigma, _ = dijkstra(G, s, weight)
|
||||
b = _accumulate_subset(b, S, P, sigma, s, targets)
|
||||
b = _rescale(b, len(G), normalized=normalized, directed=G.is_directed())
|
||||
return b
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def edge_betweenness_centrality_subset(
|
||||
G, sources, targets, normalized=False, weight=None
|
||||
):
|
||||
r"""Compute betweenness centrality for edges for a subset of nodes.
|
||||
|
||||
.. math::
|
||||
|
||||
c_B(v) =\sum_{s\in S,t \in T} \frac{\sigma(s, t|e)}{\sigma(s, t)}
|
||||
|
||||
where $S$ is the set of sources, $T$ is the set of targets,
|
||||
$\sigma(s, t)$ is the number of shortest $(s, t)$-paths,
|
||||
and $\sigma(s, t|e)$ is the number of those paths
|
||||
passing through edge $e$ [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph.
|
||||
|
||||
sources: list of nodes
|
||||
Nodes to use as sources for shortest paths in betweenness
|
||||
|
||||
targets: list of nodes
|
||||
Nodes to use as targets for shortest paths in betweenness
|
||||
|
||||
normalized : bool, optional
|
||||
If True the betweenness values are normalized by `2/(n(n-1))`
|
||||
for graphs, and `1/(n(n-1))` for directed graphs where `n`
|
||||
is the number of nodes in G.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
Weights are used to calculate weighted shortest paths, so they are
|
||||
interpreted as distances.
|
||||
|
||||
Returns
|
||||
-------
|
||||
edges : dictionary
|
||||
Dictionary of edges with Betweenness centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality
|
||||
edge_load
|
||||
|
||||
Notes
|
||||
-----
|
||||
The basic algorithm is from [1]_.
|
||||
|
||||
For weighted graphs the edge weights must be greater than zero.
|
||||
Zero edge weights can produce an infinite number of equal length
|
||||
paths between pairs of nodes.
|
||||
|
||||
The normalization might seem a little strange but it is the same
|
||||
as in edge_betweenness_centrality() and is designed to make
|
||||
edge_betweenness_centrality(G) be the same as
|
||||
edge_betweenness_centrality_subset(G,sources=G.nodes(),targets=G.nodes()).
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Ulrik Brandes, A Faster Algorithm for Betweenness Centrality.
|
||||
Journal of Mathematical Sociology 25(2):163-177, 2001.
|
||||
https://doi.org/10.1080/0022250X.2001.9990249
|
||||
.. [2] Ulrik Brandes: On Variants of Shortest-Path Betweenness
|
||||
Centrality and their Generic Computation.
|
||||
Social Networks 30(2):136-145, 2008.
|
||||
https://doi.org/10.1016/j.socnet.2007.11.001
|
||||
"""
|
||||
b = dict.fromkeys(G, 0.0) # b[v]=0 for v in G
|
||||
b.update(dict.fromkeys(G.edges(), 0.0)) # b[e] for e in G.edges()
|
||||
for s in sources:
|
||||
# single source shortest paths
|
||||
if weight is None: # use BFS
|
||||
S, P, sigma, _ = shortest_path(G, s)
|
||||
else: # use Dijkstra's algorithm
|
||||
S, P, sigma, _ = dijkstra(G, s, weight)
|
||||
b = _accumulate_edges_subset(b, S, P, sigma, s, targets)
|
||||
for n in G: # remove nodes to only return edges
|
||||
del b[n]
|
||||
b = _rescale_e(b, len(G), normalized=normalized, directed=G.is_directed())
|
||||
if G.is_multigraph():
|
||||
b = _add_edge_keys(G, b, weight=weight)
|
||||
return b
|
||||
|
||||
|
||||
def _accumulate_subset(betweenness, S, P, sigma, s, targets):
|
||||
delta = dict.fromkeys(S, 0.0)
|
||||
target_set = set(targets) - {s}
|
||||
while S:
|
||||
w = S.pop()
|
||||
if w in target_set:
|
||||
coeff = (delta[w] + 1.0) / sigma[w]
|
||||
else:
|
||||
coeff = delta[w] / sigma[w]
|
||||
for v in P[w]:
|
||||
delta[v] += sigma[v] * coeff
|
||||
if w != s:
|
||||
betweenness[w] += delta[w]
|
||||
return betweenness
|
||||
|
||||
|
||||
def _accumulate_edges_subset(betweenness, S, P, sigma, s, targets):
|
||||
"""edge_betweenness_centrality_subset helper."""
|
||||
delta = dict.fromkeys(S, 0)
|
||||
target_set = set(targets)
|
||||
while S:
|
||||
w = S.pop()
|
||||
for v in P[w]:
|
||||
if w in target_set:
|
||||
c = (sigma[v] / sigma[w]) * (1.0 + delta[w])
|
||||
else:
|
||||
c = delta[w] / len(P[w])
|
||||
if (v, w) not in betweenness:
|
||||
betweenness[(w, v)] += c
|
||||
else:
|
||||
betweenness[(v, w)] += c
|
||||
delta[v] += c
|
||||
if w != s:
|
||||
betweenness[w] += delta[w]
|
||||
return betweenness
|
||||
|
||||
|
||||
def _rescale(betweenness, n, normalized, directed=False):
|
||||
"""betweenness_centrality_subset helper."""
|
||||
if normalized:
|
||||
if n <= 2:
|
||||
scale = None # no normalization b=0 for all nodes
|
||||
else:
|
||||
scale = 1.0 / ((n - 1) * (n - 2))
|
||||
else: # rescale by 2 for undirected graphs
|
||||
if not directed:
|
||||
scale = 0.5
|
||||
else:
|
||||
scale = None
|
||||
if scale is not None:
|
||||
for v in betweenness:
|
||||
betweenness[v] *= scale
|
||||
return betweenness
|
||||
|
||||
|
||||
def _rescale_e(betweenness, n, normalized, directed=False):
|
||||
"""edge_betweenness_centrality_subset helper."""
|
||||
if normalized:
|
||||
if n <= 1:
|
||||
scale = None # no normalization b=0 for all nodes
|
||||
else:
|
||||
scale = 1.0 / (n * (n - 1))
|
||||
else: # rescale by 2 for undirected graphs
|
||||
if not directed:
|
||||
scale = 0.5
|
||||
else:
|
||||
scale = None
|
||||
if scale is not None:
|
||||
for v in betweenness:
|
||||
betweenness[v] *= scale
|
||||
return betweenness
|
||||
281
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/closeness.py
vendored
Normal file
281
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/closeness.py
vendored
Normal file
@@ -0,0 +1,281 @@
|
||||
"""
|
||||
Closeness centrality measures.
|
||||
"""
|
||||
import functools
|
||||
|
||||
import networkx as nx
|
||||
from networkx.exception import NetworkXError
|
||||
from networkx.utils.decorators import not_implemented_for
|
||||
|
||||
__all__ = ["closeness_centrality", "incremental_closeness_centrality"]
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="distance")
|
||||
def closeness_centrality(G, u=None, distance=None, wf_improved=True):
|
||||
r"""Compute closeness centrality for nodes.
|
||||
|
||||
Closeness centrality [1]_ of a node `u` is the reciprocal of the
|
||||
average shortest path distance to `u` over all `n-1` reachable nodes.
|
||||
|
||||
.. math::
|
||||
|
||||
C(u) = \frac{n - 1}{\sum_{v=1}^{n-1} d(v, u)},
|
||||
|
||||
where `d(v, u)` is the shortest-path distance between `v` and `u`,
|
||||
and `n-1` is the number of nodes reachable from `u`. Notice that the
|
||||
closeness distance function computes the incoming distance to `u`
|
||||
for directed graphs. To use outward distance, act on `G.reverse()`.
|
||||
|
||||
Notice that higher values of closeness indicate higher centrality.
|
||||
|
||||
Wasserman and Faust propose an improved formula for graphs with
|
||||
more than one connected component. The result is "a ratio of the
|
||||
fraction of actors in the group who are reachable, to the average
|
||||
distance" from the reachable actors [2]_. You might think this
|
||||
scale factor is inverted but it is not. As is, nodes from small
|
||||
components receive a smaller closeness value. Letting `N` denote
|
||||
the number of nodes in the graph,
|
||||
|
||||
.. math::
|
||||
|
||||
C_{WF}(u) = \frac{n-1}{N-1} \frac{n - 1}{\sum_{v=1}^{n-1} d(v, u)},
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
u : node, optional
|
||||
Return only the value for node u
|
||||
|
||||
distance : edge attribute key, optional (default=None)
|
||||
Use the specified edge attribute as the edge distance in shortest
|
||||
path calculations. If `None` (the default) all edges have a distance of 1.
|
||||
Absent edge attributes are assigned a distance of 1. Note that no check
|
||||
is performed to ensure that edges have the provided attribute.
|
||||
|
||||
wf_improved : bool, optional (default=True)
|
||||
If True, scale by the fraction of nodes reachable. This gives the
|
||||
Wasserman and Faust improved formula. For single component graphs
|
||||
it is the same as the original formula.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with closeness centrality as the value.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)])
|
||||
>>> nx.closeness_centrality(G)
|
||||
{0: 1.0, 1: 1.0, 2: 0.75, 3: 0.75}
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality, load_centrality, eigenvector_centrality,
|
||||
degree_centrality, incremental_closeness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The closeness centrality is normalized to `(n-1)/(|G|-1)` where
|
||||
`n` is the number of nodes in the connected part of graph
|
||||
containing the node. If the graph is not completely connected,
|
||||
this algorithm computes the closeness centrality for each
|
||||
connected part separately scaled by that parts size.
|
||||
|
||||
If the 'distance' keyword is set to an edge attribute key then the
|
||||
shortest-path length will be computed using Dijkstra's algorithm with
|
||||
that edge attribute as the edge weight.
|
||||
|
||||
The closeness centrality uses *inward* distance to a node, not outward.
|
||||
If you want to use outword distances apply the function to `G.reverse()`
|
||||
|
||||
In NetworkX 2.2 and earlier a bug caused Dijkstra's algorithm to use the
|
||||
outward distance rather than the inward distance. If you use a 'distance'
|
||||
keyword and a DiGraph, your results will change between v2.2 and v2.3.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Linton C. Freeman: Centrality in networks: I.
|
||||
Conceptual clarification. Social Networks 1:215-239, 1979.
|
||||
https://doi.org/10.1016/0378-8733(78)90021-7
|
||||
.. [2] pg. 201 of Wasserman, S. and Faust, K.,
|
||||
Social Network Analysis: Methods and Applications, 1994,
|
||||
Cambridge University Press.
|
||||
"""
|
||||
if G.is_directed():
|
||||
G = G.reverse() # create a reversed graph view
|
||||
|
||||
if distance is not None:
|
||||
# use Dijkstra's algorithm with specified attribute as edge weight
|
||||
path_length = functools.partial(
|
||||
nx.single_source_dijkstra_path_length, weight=distance
|
||||
)
|
||||
else:
|
||||
path_length = nx.single_source_shortest_path_length
|
||||
|
||||
if u is None:
|
||||
nodes = G.nodes
|
||||
else:
|
||||
nodes = [u]
|
||||
closeness_dict = {}
|
||||
for n in nodes:
|
||||
sp = path_length(G, n)
|
||||
totsp = sum(sp.values())
|
||||
len_G = len(G)
|
||||
_closeness_centrality = 0.0
|
||||
if totsp > 0.0 and len_G > 1:
|
||||
_closeness_centrality = (len(sp) - 1.0) / totsp
|
||||
# normalize to number of nodes-1 in connected part
|
||||
if wf_improved:
|
||||
s = (len(sp) - 1.0) / (len_G - 1)
|
||||
_closeness_centrality *= s
|
||||
closeness_dict[n] = _closeness_centrality
|
||||
if u is not None:
|
||||
return closeness_dict[u]
|
||||
return closeness_dict
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@nx._dispatchable(mutates_input=True)
|
||||
def incremental_closeness_centrality(
|
||||
G, edge, prev_cc=None, insertion=True, wf_improved=True
|
||||
):
|
||||
r"""Incremental closeness centrality for nodes.
|
||||
|
||||
Compute closeness centrality for nodes using level-based work filtering
|
||||
as described in Incremental Algorithms for Closeness Centrality by Sariyuce et al.
|
||||
|
||||
Level-based work filtering detects unnecessary updates to the closeness
|
||||
centrality and filters them out.
|
||||
|
||||
---
|
||||
From "Incremental Algorithms for Closeness Centrality":
|
||||
|
||||
Theorem 1: Let :math:`G = (V, E)` be a graph and u and v be two vertices in V
|
||||
such that there is no edge (u, v) in E. Let :math:`G' = (V, E \cup uv)`
|
||||
Then :math:`cc[s] = cc'[s]` if and only if :math:`\left|dG(s, u) - dG(s, v)\right| \leq 1`.
|
||||
|
||||
Where :math:`dG(u, v)` denotes the length of the shortest path between
|
||||
two vertices u, v in a graph G, cc[s] is the closeness centrality for a
|
||||
vertex s in V, and cc'[s] is the closeness centrality for a
|
||||
vertex s in V, with the (u, v) edge added.
|
||||
---
|
||||
|
||||
We use Theorem 1 to filter out updates when adding or removing an edge.
|
||||
When adding an edge (u, v), we compute the shortest path lengths from all
|
||||
other nodes to u and to v before the node is added. When removing an edge,
|
||||
we compute the shortest path lengths after the edge is removed. Then we
|
||||
apply Theorem 1 to use previously computed closeness centrality for nodes
|
||||
where :math:`\left|dG(s, u) - dG(s, v)\right| \leq 1`. This works only for
|
||||
undirected, unweighted graphs; the distance argument is not supported.
|
||||
|
||||
Closeness centrality [1]_ of a node `u` is the reciprocal of the
|
||||
sum of the shortest path distances from `u` to all `n-1` other nodes.
|
||||
Since the sum of distances depends on the number of nodes in the
|
||||
graph, closeness is normalized by the sum of minimum possible
|
||||
distances `n-1`.
|
||||
|
||||
.. math::
|
||||
|
||||
C(u) = \frac{n - 1}{\sum_{v=1}^{n-1} d(v, u)},
|
||||
|
||||
where `d(v, u)` is the shortest-path distance between `v` and `u`,
|
||||
and `n` is the number of nodes in the graph.
|
||||
|
||||
Notice that higher values of closeness indicate higher centrality.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
edge : tuple
|
||||
The modified edge (u, v) in the graph.
|
||||
|
||||
prev_cc : dictionary
|
||||
The previous closeness centrality for all nodes in the graph.
|
||||
|
||||
insertion : bool, optional
|
||||
If True (default) the edge was inserted, otherwise it was deleted from the graph.
|
||||
|
||||
wf_improved : bool, optional (default=True)
|
||||
If True, scale by the fraction of nodes reachable. This gives the
|
||||
Wasserman and Faust improved formula. For single component graphs
|
||||
it is the same as the original formula.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with closeness centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality, load_centrality, eigenvector_centrality,
|
||||
degree_centrality, closeness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The closeness centrality is normalized to `(n-1)/(|G|-1)` where
|
||||
`n` is the number of nodes in the connected part of graph
|
||||
containing the node. If the graph is not completely connected,
|
||||
this algorithm computes the closeness centrality for each
|
||||
connected part separately.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Freeman, L.C., 1979. Centrality in networks: I.
|
||||
Conceptual clarification. Social Networks 1, 215--239.
|
||||
https://doi.org/10.1016/0378-8733(78)90021-7
|
||||
.. [2] Sariyuce, A.E. ; Kaya, K. ; Saule, E. ; Catalyiirek, U.V. Incremental
|
||||
Algorithms for Closeness Centrality. 2013 IEEE International Conference on Big Data
|
||||
http://sariyuce.com/papers/bigdata13.pdf
|
||||
"""
|
||||
if prev_cc is not None and set(prev_cc.keys()) != set(G.nodes()):
|
||||
raise NetworkXError("prev_cc and G do not have the same nodes")
|
||||
|
||||
# Unpack edge
|
||||
(u, v) = edge
|
||||
path_length = nx.single_source_shortest_path_length
|
||||
|
||||
if insertion:
|
||||
# For edge insertion, we want shortest paths before the edge is inserted
|
||||
du = path_length(G, u)
|
||||
dv = path_length(G, v)
|
||||
|
||||
G.add_edge(u, v)
|
||||
else:
|
||||
G.remove_edge(u, v)
|
||||
|
||||
# For edge removal, we want shortest paths after the edge is removed
|
||||
du = path_length(G, u)
|
||||
dv = path_length(G, v)
|
||||
|
||||
if prev_cc is None:
|
||||
return nx.closeness_centrality(G)
|
||||
|
||||
nodes = G.nodes()
|
||||
closeness_dict = {}
|
||||
for n in nodes:
|
||||
if n in du and n in dv and abs(du[n] - dv[n]) <= 1:
|
||||
closeness_dict[n] = prev_cc[n]
|
||||
else:
|
||||
sp = path_length(G, n)
|
||||
totsp = sum(sp.values())
|
||||
len_G = len(G)
|
||||
_closeness_centrality = 0.0
|
||||
if totsp > 0.0 and len_G > 1:
|
||||
_closeness_centrality = (len(sp) - 1.0) / totsp
|
||||
# normalize to number of nodes-1 in connected part
|
||||
if wf_improved:
|
||||
s = (len(sp) - 1.0) / (len_G - 1)
|
||||
_closeness_centrality *= s
|
||||
closeness_dict[n] = _closeness_centrality
|
||||
|
||||
# Leave the graph as we found it
|
||||
if insertion:
|
||||
G.remove_edge(u, v)
|
||||
else:
|
||||
G.add_edge(u, v)
|
||||
|
||||
return closeness_dict
|
||||
341
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/current_flow_betweenness.py
vendored
Normal file
341
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/current_flow_betweenness.py
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
"""Current-flow betweenness centrality measures."""
|
||||
import networkx as nx
|
||||
from networkx.algorithms.centrality.flow_matrix import (
|
||||
CGInverseLaplacian,
|
||||
FullInverseLaplacian,
|
||||
SuperLUInverseLaplacian,
|
||||
flow_matrix_row,
|
||||
)
|
||||
from networkx.utils import (
|
||||
not_implemented_for,
|
||||
py_random_state,
|
||||
reverse_cuthill_mckee_ordering,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"current_flow_betweenness_centrality",
|
||||
"approximate_current_flow_betweenness_centrality",
|
||||
"edge_current_flow_betweenness_centrality",
|
||||
]
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@py_random_state(7)
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def approximate_current_flow_betweenness_centrality(
|
||||
G,
|
||||
normalized=True,
|
||||
weight=None,
|
||||
dtype=float,
|
||||
solver="full",
|
||||
epsilon=0.5,
|
||||
kmax=10000,
|
||||
seed=None,
|
||||
):
|
||||
r"""Compute the approximate current-flow betweenness centrality for nodes.
|
||||
|
||||
Approximates the current-flow betweenness centrality within absolute
|
||||
error of epsilon with high probability [1]_.
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
normalized : bool, optional (default=True)
|
||||
If True the betweenness values are normalized by 2/[(n-1)(n-2)] where
|
||||
n is the number of nodes in G.
|
||||
|
||||
weight : string or None, optional (default=None)
|
||||
Key for edge data used as the edge weight.
|
||||
If None, then use 1 as each edge weight.
|
||||
The weight reflects the capacity or the strength of the
|
||||
edge.
|
||||
|
||||
dtype : data type (float)
|
||||
Default data type for internal matrices.
|
||||
Set to np.float32 for lower memory consumption.
|
||||
|
||||
solver : string (default='full')
|
||||
Type of linear solver to use for computing the flow matrix.
|
||||
Options are "full" (uses most memory), "lu" (recommended), and
|
||||
"cg" (uses least memory).
|
||||
|
||||
epsilon: float
|
||||
Absolute error tolerance.
|
||||
|
||||
kmax: int
|
||||
Maximum number of sample node pairs to use for approximation.
|
||||
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with betweenness centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
current_flow_betweenness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The running time is $O((1/\epsilon^2)m{\sqrt k} \log n)$
|
||||
and the space required is $O(m)$ for $n$ nodes and $m$ edges.
|
||||
|
||||
If the edges have a 'weight' attribute they will be used as
|
||||
weights in this algorithm. Unspecified weights are set to 1.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Ulrik Brandes and Daniel Fleischer:
|
||||
Centrality Measures Based on Current Flow.
|
||||
Proc. 22nd Symp. Theoretical Aspects of Computer Science (STACS '05).
|
||||
LNCS 3404, pp. 533-544. Springer-Verlag, 2005.
|
||||
https://doi.org/10.1007/978-3-540-31856-9_44
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
if not nx.is_connected(G):
|
||||
raise nx.NetworkXError("Graph not connected.")
|
||||
solvername = {
|
||||
"full": FullInverseLaplacian,
|
||||
"lu": SuperLUInverseLaplacian,
|
||||
"cg": CGInverseLaplacian,
|
||||
}
|
||||
n = G.number_of_nodes()
|
||||
ordering = list(reverse_cuthill_mckee_ordering(G))
|
||||
# make a copy with integer labels according to rcm ordering
|
||||
# this could be done without a copy if we really wanted to
|
||||
H = nx.relabel_nodes(G, dict(zip(ordering, range(n))))
|
||||
L = nx.laplacian_matrix(H, nodelist=range(n), weight=weight).asformat("csc")
|
||||
L = L.astype(dtype)
|
||||
C = solvername[solver](L, dtype=dtype) # initialize solver
|
||||
betweenness = dict.fromkeys(H, 0.0)
|
||||
nb = (n - 1.0) * (n - 2.0) # normalization factor
|
||||
cstar = n * (n - 1) / nb
|
||||
l = 1 # parameter in approximation, adjustable
|
||||
k = l * int(np.ceil((cstar / epsilon) ** 2 * np.log(n)))
|
||||
if k > kmax:
|
||||
msg = f"Number random pairs k>kmax ({k}>{kmax}) "
|
||||
raise nx.NetworkXError(msg, "Increase kmax or epsilon")
|
||||
cstar2k = cstar / (2 * k)
|
||||
for _ in range(k):
|
||||
s, t = pair = seed.sample(range(n), 2)
|
||||
b = np.zeros(n, dtype=dtype)
|
||||
b[s] = 1
|
||||
b[t] = -1
|
||||
p = C.solve(b)
|
||||
for v in H:
|
||||
if v in pair:
|
||||
continue
|
||||
for nbr in H[v]:
|
||||
w = H[v][nbr].get(weight, 1.0)
|
||||
betweenness[v] += float(w * np.abs(p[v] - p[nbr]) * cstar2k)
|
||||
if normalized:
|
||||
factor = 1.0
|
||||
else:
|
||||
factor = nb / 2.0
|
||||
# remap to original node names and "unnormalize" if required
|
||||
return {ordering[k]: v * factor for k, v in betweenness.items()}
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def current_flow_betweenness_centrality(
|
||||
G, normalized=True, weight=None, dtype=float, solver="full"
|
||||
):
|
||||
r"""Compute current-flow betweenness centrality for nodes.
|
||||
|
||||
Current-flow betweenness centrality uses an electrical current
|
||||
model for information spreading in contrast to betweenness
|
||||
centrality which uses shortest paths.
|
||||
|
||||
Current-flow betweenness centrality is also known as
|
||||
random-walk betweenness centrality [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
normalized : bool, optional (default=True)
|
||||
If True the betweenness values are normalized by 2/[(n-1)(n-2)] where
|
||||
n is the number of nodes in G.
|
||||
|
||||
weight : string or None, optional (default=None)
|
||||
Key for edge data used as the edge weight.
|
||||
If None, then use 1 as each edge weight.
|
||||
The weight reflects the capacity or the strength of the
|
||||
edge.
|
||||
|
||||
dtype : data type (float)
|
||||
Default data type for internal matrices.
|
||||
Set to np.float32 for lower memory consumption.
|
||||
|
||||
solver : string (default='full')
|
||||
Type of linear solver to use for computing the flow matrix.
|
||||
Options are "full" (uses most memory), "lu" (recommended), and
|
||||
"cg" (uses least memory).
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with betweenness centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
approximate_current_flow_betweenness_centrality
|
||||
betweenness_centrality
|
||||
edge_betweenness_centrality
|
||||
edge_current_flow_betweenness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
Current-flow betweenness can be computed in $O(I(n-1)+mn \log n)$
|
||||
time [1]_, where $I(n-1)$ is the time needed to compute the
|
||||
inverse Laplacian. For a full matrix this is $O(n^3)$ but using
|
||||
sparse methods you can achieve $O(nm{\sqrt k})$ where $k$ is the
|
||||
Laplacian matrix condition number.
|
||||
|
||||
The space required is $O(nw)$ where $w$ is the width of the sparse
|
||||
Laplacian matrix. Worse case is $w=n$ for $O(n^2)$.
|
||||
|
||||
If the edges have a 'weight' attribute they will be used as
|
||||
weights in this algorithm. Unspecified weights are set to 1.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Centrality Measures Based on Current Flow.
|
||||
Ulrik Brandes and Daniel Fleischer,
|
||||
Proc. 22nd Symp. Theoretical Aspects of Computer Science (STACS '05).
|
||||
LNCS 3404, pp. 533-544. Springer-Verlag, 2005.
|
||||
https://doi.org/10.1007/978-3-540-31856-9_44
|
||||
|
||||
.. [2] A measure of betweenness centrality based on random walks,
|
||||
M. E. J. Newman, Social Networks 27, 39-54 (2005).
|
||||
"""
|
||||
if not nx.is_connected(G):
|
||||
raise nx.NetworkXError("Graph not connected.")
|
||||
N = G.number_of_nodes()
|
||||
ordering = list(reverse_cuthill_mckee_ordering(G))
|
||||
# make a copy with integer labels according to rcm ordering
|
||||
# this could be done without a copy if we really wanted to
|
||||
H = nx.relabel_nodes(G, dict(zip(ordering, range(N))))
|
||||
betweenness = dict.fromkeys(H, 0.0) # b[n]=0 for n in H
|
||||
for row, (s, t) in flow_matrix_row(H, weight=weight, dtype=dtype, solver=solver):
|
||||
pos = dict(zip(row.argsort()[::-1], range(N)))
|
||||
for i in range(N):
|
||||
betweenness[s] += (i - pos[i]) * row.item(i)
|
||||
betweenness[t] += (N - i - 1 - pos[i]) * row.item(i)
|
||||
if normalized:
|
||||
nb = (N - 1.0) * (N - 2.0) # normalization factor
|
||||
else:
|
||||
nb = 2.0
|
||||
return {ordering[n]: (b - n) * 2.0 / nb for n, b in betweenness.items()}
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def edge_current_flow_betweenness_centrality(
|
||||
G, normalized=True, weight=None, dtype=float, solver="full"
|
||||
):
|
||||
r"""Compute current-flow betweenness centrality for edges.
|
||||
|
||||
Current-flow betweenness centrality uses an electrical current
|
||||
model for information spreading in contrast to betweenness
|
||||
centrality which uses shortest paths.
|
||||
|
||||
Current-flow betweenness centrality is also known as
|
||||
random-walk betweenness centrality [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
normalized : bool, optional (default=True)
|
||||
If True the betweenness values are normalized by 2/[(n-1)(n-2)] where
|
||||
n is the number of nodes in G.
|
||||
|
||||
weight : string or None, optional (default=None)
|
||||
Key for edge data used as the edge weight.
|
||||
If None, then use 1 as each edge weight.
|
||||
The weight reflects the capacity or the strength of the
|
||||
edge.
|
||||
|
||||
dtype : data type (default=float)
|
||||
Default data type for internal matrices.
|
||||
Set to np.float32 for lower memory consumption.
|
||||
|
||||
solver : string (default='full')
|
||||
Type of linear solver to use for computing the flow matrix.
|
||||
Options are "full" (uses most memory), "lu" (recommended), and
|
||||
"cg" (uses least memory).
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of edge tuples with betweenness centrality as the value.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
The algorithm does not support DiGraphs.
|
||||
If the input graph is an instance of DiGraph class, NetworkXError
|
||||
is raised.
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality
|
||||
edge_betweenness_centrality
|
||||
current_flow_betweenness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
Current-flow betweenness can be computed in $O(I(n-1)+mn \log n)$
|
||||
time [1]_, where $I(n-1)$ is the time needed to compute the
|
||||
inverse Laplacian. For a full matrix this is $O(n^3)$ but using
|
||||
sparse methods you can achieve $O(nm{\sqrt k})$ where $k$ is the
|
||||
Laplacian matrix condition number.
|
||||
|
||||
The space required is $O(nw)$ where $w$ is the width of the sparse
|
||||
Laplacian matrix. Worse case is $w=n$ for $O(n^2)$.
|
||||
|
||||
If the edges have a 'weight' attribute they will be used as
|
||||
weights in this algorithm. Unspecified weights are set to 1.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Centrality Measures Based on Current Flow.
|
||||
Ulrik Brandes and Daniel Fleischer,
|
||||
Proc. 22nd Symp. Theoretical Aspects of Computer Science (STACS '05).
|
||||
LNCS 3404, pp. 533-544. Springer-Verlag, 2005.
|
||||
https://doi.org/10.1007/978-3-540-31856-9_44
|
||||
|
||||
.. [2] A measure of betweenness centrality based on random walks,
|
||||
M. E. J. Newman, Social Networks 27, 39-54 (2005).
|
||||
"""
|
||||
if not nx.is_connected(G):
|
||||
raise nx.NetworkXError("Graph not connected.")
|
||||
N = G.number_of_nodes()
|
||||
ordering = list(reverse_cuthill_mckee_ordering(G))
|
||||
# make a copy with integer labels according to rcm ordering
|
||||
# this could be done without a copy if we really wanted to
|
||||
H = nx.relabel_nodes(G, dict(zip(ordering, range(N))))
|
||||
edges = (tuple(sorted((u, v))) for u, v in H.edges())
|
||||
betweenness = dict.fromkeys(edges, 0.0)
|
||||
if normalized:
|
||||
nb = (N - 1.0) * (N - 2.0) # normalization factor
|
||||
else:
|
||||
nb = 2.0
|
||||
for row, (e) in flow_matrix_row(H, weight=weight, dtype=dtype, solver=solver):
|
||||
pos = dict(zip(row.argsort()[::-1], range(1, N + 1)))
|
||||
for i in range(N):
|
||||
betweenness[e] += (i + 1 - pos[i]) * row.item(i)
|
||||
betweenness[e] += (N - i - pos[i]) * row.item(i)
|
||||
betweenness[e] /= nb
|
||||
return {(ordering[s], ordering[t]): b for (s, t), b in betweenness.items()}
|
||||
226
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/current_flow_betweenness_subset.py
vendored
Normal file
226
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/current_flow_betweenness_subset.py
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
"""Current-flow betweenness centrality measures for subsets of nodes."""
|
||||
import networkx as nx
|
||||
from networkx.algorithms.centrality.flow_matrix import flow_matrix_row
|
||||
from networkx.utils import not_implemented_for, reverse_cuthill_mckee_ordering
|
||||
|
||||
__all__ = [
|
||||
"current_flow_betweenness_centrality_subset",
|
||||
"edge_current_flow_betweenness_centrality_subset",
|
||||
]
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def current_flow_betweenness_centrality_subset(
|
||||
G, sources, targets, normalized=True, weight=None, dtype=float, solver="lu"
|
||||
):
|
||||
r"""Compute current-flow betweenness centrality for subsets of nodes.
|
||||
|
||||
Current-flow betweenness centrality uses an electrical current
|
||||
model for information spreading in contrast to betweenness
|
||||
centrality which uses shortest paths.
|
||||
|
||||
Current-flow betweenness centrality is also known as
|
||||
random-walk betweenness centrality [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
sources: list of nodes
|
||||
Nodes to use as sources for current
|
||||
|
||||
targets: list of nodes
|
||||
Nodes to use as sinks for current
|
||||
|
||||
normalized : bool, optional (default=True)
|
||||
If True the betweenness values are normalized by b=b/(n-1)(n-2) where
|
||||
n is the number of nodes in G.
|
||||
|
||||
weight : string or None, optional (default=None)
|
||||
Key for edge data used as the edge weight.
|
||||
If None, then use 1 as each edge weight.
|
||||
The weight reflects the capacity or the strength of the
|
||||
edge.
|
||||
|
||||
dtype: data type (float)
|
||||
Default data type for internal matrices.
|
||||
Set to np.float32 for lower memory consumption.
|
||||
|
||||
solver: string (default='lu')
|
||||
Type of linear solver to use for computing the flow matrix.
|
||||
Options are "full" (uses most memory), "lu" (recommended), and
|
||||
"cg" (uses least memory).
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with betweenness centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
approximate_current_flow_betweenness_centrality
|
||||
betweenness_centrality
|
||||
edge_betweenness_centrality
|
||||
edge_current_flow_betweenness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
Current-flow betweenness can be computed in $O(I(n-1)+mn \log n)$
|
||||
time [1]_, where $I(n-1)$ is the time needed to compute the
|
||||
inverse Laplacian. For a full matrix this is $O(n^3)$ but using
|
||||
sparse methods you can achieve $O(nm{\sqrt k})$ where $k$ is the
|
||||
Laplacian matrix condition number.
|
||||
|
||||
The space required is $O(nw)$ where $w$ is the width of the sparse
|
||||
Laplacian matrix. Worse case is $w=n$ for $O(n^2)$.
|
||||
|
||||
If the edges have a 'weight' attribute they will be used as
|
||||
weights in this algorithm. Unspecified weights are set to 1.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Centrality Measures Based on Current Flow.
|
||||
Ulrik Brandes and Daniel Fleischer,
|
||||
Proc. 22nd Symp. Theoretical Aspects of Computer Science (STACS '05).
|
||||
LNCS 3404, pp. 533-544. Springer-Verlag, 2005.
|
||||
https://doi.org/10.1007/978-3-540-31856-9_44
|
||||
|
||||
.. [2] A measure of betweenness centrality based on random walks,
|
||||
M. E. J. Newman, Social Networks 27, 39-54 (2005).
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
from networkx.utils import reverse_cuthill_mckee_ordering
|
||||
|
||||
if not nx.is_connected(G):
|
||||
raise nx.NetworkXError("Graph not connected.")
|
||||
N = G.number_of_nodes()
|
||||
ordering = list(reverse_cuthill_mckee_ordering(G))
|
||||
# make a copy with integer labels according to rcm ordering
|
||||
# this could be done without a copy if we really wanted to
|
||||
mapping = dict(zip(ordering, range(N)))
|
||||
H = nx.relabel_nodes(G, mapping)
|
||||
betweenness = dict.fromkeys(H, 0.0) # b[n]=0 for n in H
|
||||
for row, (s, t) in flow_matrix_row(H, weight=weight, dtype=dtype, solver=solver):
|
||||
for ss in sources:
|
||||
i = mapping[ss]
|
||||
for tt in targets:
|
||||
j = mapping[tt]
|
||||
betweenness[s] += 0.5 * abs(row.item(i) - row.item(j))
|
||||
betweenness[t] += 0.5 * abs(row.item(i) - row.item(j))
|
||||
if normalized:
|
||||
nb = (N - 1.0) * (N - 2.0) # normalization factor
|
||||
else:
|
||||
nb = 2.0
|
||||
for node in H:
|
||||
betweenness[node] = betweenness[node] / nb + 1.0 / (2 - N)
|
||||
return {ordering[node]: value for node, value in betweenness.items()}
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def edge_current_flow_betweenness_centrality_subset(
|
||||
G, sources, targets, normalized=True, weight=None, dtype=float, solver="lu"
|
||||
):
|
||||
r"""Compute current-flow betweenness centrality for edges using subsets
|
||||
of nodes.
|
||||
|
||||
Current-flow betweenness centrality uses an electrical current
|
||||
model for information spreading in contrast to betweenness
|
||||
centrality which uses shortest paths.
|
||||
|
||||
Current-flow betweenness centrality is also known as
|
||||
random-walk betweenness centrality [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
sources: list of nodes
|
||||
Nodes to use as sources for current
|
||||
|
||||
targets: list of nodes
|
||||
Nodes to use as sinks for current
|
||||
|
||||
normalized : bool, optional (default=True)
|
||||
If True the betweenness values are normalized by b=b/(n-1)(n-2) where
|
||||
n is the number of nodes in G.
|
||||
|
||||
weight : string or None, optional (default=None)
|
||||
Key for edge data used as the edge weight.
|
||||
If None, then use 1 as each edge weight.
|
||||
The weight reflects the capacity or the strength of the
|
||||
edge.
|
||||
|
||||
dtype: data type (float)
|
||||
Default data type for internal matrices.
|
||||
Set to np.float32 for lower memory consumption.
|
||||
|
||||
solver: string (default='lu')
|
||||
Type of linear solver to use for computing the flow matrix.
|
||||
Options are "full" (uses most memory), "lu" (recommended), and
|
||||
"cg" (uses least memory).
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dict
|
||||
Dictionary of edge tuples with betweenness centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality
|
||||
edge_betweenness_centrality
|
||||
current_flow_betweenness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
Current-flow betweenness can be computed in $O(I(n-1)+mn \log n)$
|
||||
time [1]_, where $I(n-1)$ is the time needed to compute the
|
||||
inverse Laplacian. For a full matrix this is $O(n^3)$ but using
|
||||
sparse methods you can achieve $O(nm{\sqrt k})$ where $k$ is the
|
||||
Laplacian matrix condition number.
|
||||
|
||||
The space required is $O(nw)$ where $w$ is the width of the sparse
|
||||
Laplacian matrix. Worse case is $w=n$ for $O(n^2)$.
|
||||
|
||||
If the edges have a 'weight' attribute they will be used as
|
||||
weights in this algorithm. Unspecified weights are set to 1.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Centrality Measures Based on Current Flow.
|
||||
Ulrik Brandes and Daniel Fleischer,
|
||||
Proc. 22nd Symp. Theoretical Aspects of Computer Science (STACS '05).
|
||||
LNCS 3404, pp. 533-544. Springer-Verlag, 2005.
|
||||
https://doi.org/10.1007/978-3-540-31856-9_44
|
||||
|
||||
.. [2] A measure of betweenness centrality based on random walks,
|
||||
M. E. J. Newman, Social Networks 27, 39-54 (2005).
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
if not nx.is_connected(G):
|
||||
raise nx.NetworkXError("Graph not connected.")
|
||||
N = G.number_of_nodes()
|
||||
ordering = list(reverse_cuthill_mckee_ordering(G))
|
||||
# make a copy with integer labels according to rcm ordering
|
||||
# this could be done without a copy if we really wanted to
|
||||
mapping = dict(zip(ordering, range(N)))
|
||||
H = nx.relabel_nodes(G, mapping)
|
||||
edges = (tuple(sorted((u, v))) for u, v in H.edges())
|
||||
betweenness = dict.fromkeys(edges, 0.0)
|
||||
if normalized:
|
||||
nb = (N - 1.0) * (N - 2.0) # normalization factor
|
||||
else:
|
||||
nb = 2.0
|
||||
for row, (e) in flow_matrix_row(H, weight=weight, dtype=dtype, solver=solver):
|
||||
for ss in sources:
|
||||
i = mapping[ss]
|
||||
for tt in targets:
|
||||
j = mapping[tt]
|
||||
betweenness[e] += 0.5 * abs(row.item(i) - row.item(j))
|
||||
betweenness[e] /= nb
|
||||
return {(ordering[s], ordering[t]): value for (s, t), value in betweenness.items()}
|
||||
95
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/current_flow_closeness.py
vendored
Normal file
95
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/current_flow_closeness.py
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
"""Current-flow closeness centrality measures."""
|
||||
import networkx as nx
|
||||
from networkx.algorithms.centrality.flow_matrix import (
|
||||
CGInverseLaplacian,
|
||||
FullInverseLaplacian,
|
||||
SuperLUInverseLaplacian,
|
||||
)
|
||||
from networkx.utils import not_implemented_for, reverse_cuthill_mckee_ordering
|
||||
|
||||
__all__ = ["current_flow_closeness_centrality", "information_centrality"]
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def current_flow_closeness_centrality(G, weight=None, dtype=float, solver="lu"):
|
||||
"""Compute current-flow closeness centrality for nodes.
|
||||
|
||||
Current-flow closeness centrality is variant of closeness
|
||||
centrality based on effective resistance between nodes in
|
||||
a network. This metric is also known as information centrality.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
The weight reflects the capacity or the strength of the
|
||||
edge.
|
||||
|
||||
dtype: data type (default=float)
|
||||
Default data type for internal matrices.
|
||||
Set to np.float32 for lower memory consumption.
|
||||
|
||||
solver: string (default='lu')
|
||||
Type of linear solver to use for computing the flow matrix.
|
||||
Options are "full" (uses most memory), "lu" (recommended), and
|
||||
"cg" (uses least memory).
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with current flow closeness centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
closeness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The algorithm is from Brandes [1]_.
|
||||
|
||||
See also [2]_ for the original definition of information centrality.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Ulrik Brandes and Daniel Fleischer,
|
||||
Centrality Measures Based on Current Flow.
|
||||
Proc. 22nd Symp. Theoretical Aspects of Computer Science (STACS '05).
|
||||
LNCS 3404, pp. 533-544. Springer-Verlag, 2005.
|
||||
https://doi.org/10.1007/978-3-540-31856-9_44
|
||||
|
||||
.. [2] Karen Stephenson and Marvin Zelen:
|
||||
Rethinking centrality: Methods and examples.
|
||||
Social Networks 11(1):1-37, 1989.
|
||||
https://doi.org/10.1016/0378-8733(89)90016-6
|
||||
"""
|
||||
if not nx.is_connected(G):
|
||||
raise nx.NetworkXError("Graph not connected.")
|
||||
solvername = {
|
||||
"full": FullInverseLaplacian,
|
||||
"lu": SuperLUInverseLaplacian,
|
||||
"cg": CGInverseLaplacian,
|
||||
}
|
||||
N = G.number_of_nodes()
|
||||
ordering = list(reverse_cuthill_mckee_ordering(G))
|
||||
# make a copy with integer labels according to rcm ordering
|
||||
# this could be done without a copy if we really wanted to
|
||||
H = nx.relabel_nodes(G, dict(zip(ordering, range(N))))
|
||||
betweenness = dict.fromkeys(H, 0.0) # b[n]=0 for n in H
|
||||
N = H.number_of_nodes()
|
||||
L = nx.laplacian_matrix(H, nodelist=range(N), weight=weight).asformat("csc")
|
||||
L = L.astype(dtype)
|
||||
C2 = solvername[solver](L, width=1, dtype=dtype) # initialize solver
|
||||
for v in H:
|
||||
col = C2.get_row(v)
|
||||
for w in H:
|
||||
betweenness[v] += col.item(v) - 2 * col.item(w)
|
||||
betweenness[w] += col.item(v)
|
||||
return {ordering[node]: 1 / value for node, value in betweenness.items()}
|
||||
|
||||
|
||||
information_centrality = current_flow_closeness_centrality
|
||||
149
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/degree_alg.py
vendored
Normal file
149
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/degree_alg.py
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
"""Degree centrality measures."""
|
||||
import networkx as nx
|
||||
from networkx.utils.decorators import not_implemented_for
|
||||
|
||||
__all__ = ["degree_centrality", "in_degree_centrality", "out_degree_centrality"]
|
||||
|
||||
|
||||
@nx._dispatchable
|
||||
def degree_centrality(G):
|
||||
"""Compute the degree centrality for nodes.
|
||||
|
||||
The degree centrality for a node v is the fraction of nodes it
|
||||
is connected to.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with degree centrality as the value.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)])
|
||||
>>> nx.degree_centrality(G)
|
||||
{0: 1.0, 1: 1.0, 2: 0.6666666666666666, 3: 0.6666666666666666}
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality, load_centrality, eigenvector_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The degree centrality values are normalized by dividing by the maximum
|
||||
possible degree in a simple graph n-1 where n is the number of nodes in G.
|
||||
|
||||
For multigraphs or graphs with self loops the maximum degree might
|
||||
be higher than n-1 and values of degree centrality greater than 1
|
||||
are possible.
|
||||
"""
|
||||
if len(G) <= 1:
|
||||
return {n: 1 for n in G}
|
||||
|
||||
s = 1.0 / (len(G) - 1.0)
|
||||
centrality = {n: d * s for n, d in G.degree()}
|
||||
return centrality
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
@nx._dispatchable
|
||||
def in_degree_centrality(G):
|
||||
"""Compute the in-degree centrality for nodes.
|
||||
|
||||
The in-degree centrality for a node v is the fraction of nodes its
|
||||
incoming edges are connected to.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with in-degree centrality as values.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If G is undirected.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.DiGraph([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)])
|
||||
>>> nx.in_degree_centrality(G)
|
||||
{0: 0.0, 1: 0.3333333333333333, 2: 0.6666666666666666, 3: 0.6666666666666666}
|
||||
|
||||
See Also
|
||||
--------
|
||||
degree_centrality, out_degree_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The degree centrality values are normalized by dividing by the maximum
|
||||
possible degree in a simple graph n-1 where n is the number of nodes in G.
|
||||
|
||||
For multigraphs or graphs with self loops the maximum degree might
|
||||
be higher than n-1 and values of degree centrality greater than 1
|
||||
are possible.
|
||||
"""
|
||||
if len(G) <= 1:
|
||||
return {n: 1 for n in G}
|
||||
|
||||
s = 1.0 / (len(G) - 1.0)
|
||||
centrality = {n: d * s for n, d in G.in_degree()}
|
||||
return centrality
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
@nx._dispatchable
|
||||
def out_degree_centrality(G):
|
||||
"""Compute the out-degree centrality for nodes.
|
||||
|
||||
The out-degree centrality for a node v is the fraction of nodes its
|
||||
outgoing edges are connected to.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with out-degree centrality as values.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If G is undirected.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.DiGraph([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)])
|
||||
>>> nx.out_degree_centrality(G)
|
||||
{0: 1.0, 1: 0.6666666666666666, 2: 0.0, 3: 0.0}
|
||||
|
||||
See Also
|
||||
--------
|
||||
degree_centrality, in_degree_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The degree centrality values are normalized by dividing by the maximum
|
||||
possible degree in a simple graph n-1 where n is the number of nodes in G.
|
||||
|
||||
For multigraphs or graphs with self loops the maximum degree might
|
||||
be higher than n-1 and values of degree centrality greater than 1
|
||||
are possible.
|
||||
"""
|
||||
if len(G) <= 1:
|
||||
return {n: 1 for n in G}
|
||||
|
||||
s = 1.0 / (len(G) - 1.0)
|
||||
centrality = {n: d * s for n, d in G.out_degree()}
|
||||
return centrality
|
||||
107
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/dispersion.py
vendored
Normal file
107
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/dispersion.py
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
from itertools import combinations
|
||||
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["dispersion"]
|
||||
|
||||
|
||||
@nx._dispatchable
|
||||
def dispersion(G, u=None, v=None, normalized=True, alpha=1.0, b=0.0, c=0.0):
|
||||
r"""Calculate dispersion between `u` and `v` in `G`.
|
||||
|
||||
A link between two actors (`u` and `v`) has a high dispersion when their
|
||||
mutual ties (`s` and `t`) are not well connected with each other.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
u : node, optional
|
||||
The source for the dispersion score (e.g. ego node of the network).
|
||||
v : node, optional
|
||||
The target of the dispersion score if specified.
|
||||
normalized : bool
|
||||
If True (default) normalize by the embeddedness of the nodes (u and v).
|
||||
alpha, b, c : float
|
||||
Parameters for the normalization procedure. When `normalized` is True,
|
||||
the dispersion value is normalized by::
|
||||
|
||||
result = ((dispersion + b) ** alpha) / (embeddedness + c)
|
||||
|
||||
as long as the denominator is nonzero.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
If u (v) is specified, returns a dictionary of nodes with dispersion
|
||||
score for all "target" ("source") nodes. If neither u nor v is
|
||||
specified, returns a dictionary of dictionaries for all nodes 'u' in the
|
||||
graph with a dispersion score for each node 'v'.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This implementation follows Lars Backstrom and Jon Kleinberg [1]_. Typical
|
||||
usage would be to run dispersion on the ego network $G_u$ if $u$ were
|
||||
specified. Running :func:`dispersion` with neither $u$ nor $v$ specified
|
||||
can take some time to complete.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Romantic Partnerships and the Dispersion of Social Ties:
|
||||
A Network Analysis of Relationship Status on Facebook.
|
||||
Lars Backstrom, Jon Kleinberg.
|
||||
https://arxiv.org/pdf/1310.6753v1.pdf
|
||||
|
||||
"""
|
||||
|
||||
def _dispersion(G_u, u, v):
|
||||
"""dispersion for all nodes 'v' in a ego network G_u of node 'u'"""
|
||||
u_nbrs = set(G_u[u])
|
||||
ST = {n for n in G_u[v] if n in u_nbrs}
|
||||
set_uv = {u, v}
|
||||
# all possible ties of connections that u and b share
|
||||
possib = combinations(ST, 2)
|
||||
total = 0
|
||||
for s, t in possib:
|
||||
# neighbors of s that are in G_u, not including u and v
|
||||
nbrs_s = u_nbrs.intersection(G_u[s]) - set_uv
|
||||
# s and t are not directly connected
|
||||
if t not in nbrs_s:
|
||||
# s and t do not share a connection
|
||||
if nbrs_s.isdisjoint(G_u[t]):
|
||||
# tick for disp(u, v)
|
||||
total += 1
|
||||
# neighbors that u and v share
|
||||
embeddedness = len(ST)
|
||||
|
||||
dispersion_val = total
|
||||
if normalized:
|
||||
dispersion_val = (total + b) ** alpha
|
||||
if embeddedness + c != 0:
|
||||
dispersion_val /= embeddedness + c
|
||||
|
||||
return dispersion_val
|
||||
|
||||
if u is None:
|
||||
# v and u are not specified
|
||||
if v is None:
|
||||
results = {n: {} for n in G}
|
||||
for u in G:
|
||||
for v in G[u]:
|
||||
results[u][v] = _dispersion(G, u, v)
|
||||
# u is not specified, but v is
|
||||
else:
|
||||
results = dict.fromkeys(G[v], {})
|
||||
for u in G[v]:
|
||||
results[u] = _dispersion(G, v, u)
|
||||
else:
|
||||
# u is specified with no target v
|
||||
if v is None:
|
||||
results = dict.fromkeys(G[u], {})
|
||||
for v in G[u]:
|
||||
results[v] = _dispersion(G, u, v)
|
||||
# both u and v are specified
|
||||
else:
|
||||
results = _dispersion(G, u, v)
|
||||
|
||||
return results
|
||||
341
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/eigenvector.py
vendored
Normal file
341
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/eigenvector.py
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
"""Functions for computing eigenvector centrality."""
|
||||
import math
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import not_implemented_for
|
||||
|
||||
__all__ = ["eigenvector_centrality", "eigenvector_centrality_numpy"]
|
||||
|
||||
|
||||
@not_implemented_for("multigraph")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def eigenvector_centrality(G, max_iter=100, tol=1.0e-6, nstart=None, weight=None):
|
||||
r"""Compute the eigenvector centrality for the graph G.
|
||||
|
||||
Eigenvector centrality computes the centrality for a node by adding
|
||||
the centrality of its predecessors. The centrality for node $i$ is the
|
||||
$i$-th element of a left eigenvector associated with the eigenvalue $\lambda$
|
||||
of maximum modulus that is positive. Such an eigenvector $x$ is
|
||||
defined up to a multiplicative constant by the equation
|
||||
|
||||
.. math::
|
||||
|
||||
\lambda x^T = x^T A,
|
||||
|
||||
where $A$ is the adjacency matrix of the graph G. By definition of
|
||||
row-column product, the equation above is equivalent to
|
||||
|
||||
.. math::
|
||||
|
||||
\lambda x_i = \sum_{j\to i}x_j.
|
||||
|
||||
That is, adding the eigenvector centralities of the predecessors of
|
||||
$i$ one obtains the eigenvector centrality of $i$ multiplied by
|
||||
$\lambda$. In the case of undirected graphs, $x$ also solves the familiar
|
||||
right-eigenvector equation $Ax = \lambda x$.
|
||||
|
||||
By virtue of the Perron–Frobenius theorem [1]_, if G is strongly
|
||||
connected there is a unique eigenvector $x$, and all its entries
|
||||
are strictly positive.
|
||||
|
||||
If G is not strongly connected there might be several left
|
||||
eigenvectors associated with $\lambda$, and some of their elements
|
||||
might be zero.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph.
|
||||
|
||||
max_iter : integer, optional (default=100)
|
||||
Maximum number of power iterations.
|
||||
|
||||
tol : float, optional (default=1.0e-6)
|
||||
Error tolerance (in Euclidean norm) used to check convergence in
|
||||
power iteration.
|
||||
|
||||
nstart : dictionary, optional (default=None)
|
||||
Starting value of power iteration for each node. Must have a nonzero
|
||||
projection on the desired eigenvector for the power method to converge.
|
||||
If None, this implementation uses an all-ones vector, which is a safe
|
||||
choice.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal. Otherwise holds the
|
||||
name of the edge attribute used as weight. In this measure the
|
||||
weight is interpreted as the connection strength.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with eigenvector centrality as the value. The
|
||||
associated vector has unit Euclidean norm and the values are
|
||||
nonegative.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> centrality = nx.eigenvector_centrality(G)
|
||||
>>> sorted((v, f"{c:0.2f}") for v, c in centrality.items())
|
||||
[(0, '0.37'), (1, '0.60'), (2, '0.60'), (3, '0.37')]
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXPointlessConcept
|
||||
If the graph G is the null graph.
|
||||
|
||||
NetworkXError
|
||||
If each value in `nstart` is zero.
|
||||
|
||||
PowerIterationFailedConvergence
|
||||
If the algorithm fails to converge to the specified tolerance
|
||||
within the specified number of iterations of the power iteration
|
||||
method.
|
||||
|
||||
See Also
|
||||
--------
|
||||
eigenvector_centrality_numpy
|
||||
:func:`~networkx.algorithms.link_analysis.pagerank_alg.pagerank`
|
||||
:func:`~networkx.algorithms.link_analysis.hits_alg.hits`
|
||||
|
||||
Notes
|
||||
-----
|
||||
Eigenvector centrality was introduced by Landau [2]_ for chess
|
||||
tournaments. It was later rediscovered by Wei [3]_ and then
|
||||
popularized by Kendall [4]_ in the context of sport ranking. Berge
|
||||
introduced a general definition for graphs based on social connections
|
||||
[5]_. Bonacich [6]_ reintroduced again eigenvector centrality and made
|
||||
it popular in link analysis.
|
||||
|
||||
This function computes the left dominant eigenvector, which corresponds
|
||||
to adding the centrality of predecessors: this is the usual approach.
|
||||
To add the centrality of successors first reverse the graph with
|
||||
``G.reverse()``.
|
||||
|
||||
The implementation uses power iteration [7]_ to compute a dominant
|
||||
eigenvector starting from the provided vector `nstart`. Convergence is
|
||||
guaranteed as long as `nstart` has a nonzero projection on a dominant
|
||||
eigenvector, which certainly happens using the default value.
|
||||
|
||||
The method stops when the change in the computed vector between two
|
||||
iterations is smaller than an error tolerance of ``G.number_of_nodes()
|
||||
* tol`` or after ``max_iter`` iterations, but in the second case it
|
||||
raises an exception.
|
||||
|
||||
This implementation uses $(A + I)$ rather than the adjacency matrix
|
||||
$A$ because the change preserves eigenvectors, but it shifts the
|
||||
spectrum, thus guaranteeing convergence even for networks with
|
||||
negative eigenvalues of maximum modulus.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Abraham Berman and Robert J. Plemmons.
|
||||
"Nonnegative Matrices in the Mathematical Sciences."
|
||||
Classics in Applied Mathematics. SIAM, 1994.
|
||||
|
||||
.. [2] Edmund Landau.
|
||||
"Zur relativen Wertbemessung der Turnierresultate."
|
||||
Deutsches Wochenschach, 11:366–369, 1895.
|
||||
|
||||
.. [3] Teh-Hsing Wei.
|
||||
"The Algebraic Foundations of Ranking Theory."
|
||||
PhD thesis, University of Cambridge, 1952.
|
||||
|
||||
.. [4] Maurice G. Kendall.
|
||||
"Further contributions to the theory of paired comparisons."
|
||||
Biometrics, 11(1):43–62, 1955.
|
||||
https://www.jstor.org/stable/3001479
|
||||
|
||||
.. [5] Claude Berge
|
||||
"Théorie des graphes et ses applications."
|
||||
Dunod, Paris, France, 1958.
|
||||
|
||||
.. [6] Phillip Bonacich.
|
||||
"Technique for analyzing overlapping memberships."
|
||||
Sociological Methodology, 4:176–185, 1972.
|
||||
https://www.jstor.org/stable/270732
|
||||
|
||||
.. [7] Power iteration:: https://en.wikipedia.org/wiki/Power_iteration
|
||||
|
||||
"""
|
||||
if len(G) == 0:
|
||||
raise nx.NetworkXPointlessConcept(
|
||||
"cannot compute centrality for the null graph"
|
||||
)
|
||||
# If no initial vector is provided, start with the all-ones vector.
|
||||
if nstart is None:
|
||||
nstart = {v: 1 for v in G}
|
||||
if all(v == 0 for v in nstart.values()):
|
||||
raise nx.NetworkXError("initial vector cannot have all zero values")
|
||||
# Normalize the initial vector so that each entry is in [0, 1]. This is
|
||||
# guaranteed to never have a divide-by-zero error by the previous line.
|
||||
nstart_sum = sum(nstart.values())
|
||||
x = {k: v / nstart_sum for k, v in nstart.items()}
|
||||
nnodes = G.number_of_nodes()
|
||||
# make up to max_iter iterations
|
||||
for _ in range(max_iter):
|
||||
xlast = x
|
||||
x = xlast.copy() # Start with xlast times I to iterate with (A+I)
|
||||
# do the multiplication y^T = x^T A (left eigenvector)
|
||||
for n in x:
|
||||
for nbr in G[n]:
|
||||
w = G[n][nbr].get(weight, 1) if weight else 1
|
||||
x[nbr] += xlast[n] * w
|
||||
# Normalize the vector. The normalization denominator `norm`
|
||||
# should never be zero by the Perron--Frobenius
|
||||
# theorem. However, in case it is due to numerical error, we
|
||||
# assume the norm to be one instead.
|
||||
norm = math.hypot(*x.values()) or 1
|
||||
x = {k: v / norm for k, v in x.items()}
|
||||
# Check for convergence (in the L_1 norm).
|
||||
if sum(abs(x[n] - xlast[n]) for n in x) < nnodes * tol:
|
||||
return x
|
||||
raise nx.PowerIterationFailedConvergence(max_iter)
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def eigenvector_centrality_numpy(G, weight=None, max_iter=50, tol=0):
|
||||
r"""Compute the eigenvector centrality for the graph G.
|
||||
|
||||
Eigenvector centrality computes the centrality for a node by adding
|
||||
the centrality of its predecessors. The centrality for node $i$ is the
|
||||
$i$-th element of a left eigenvector associated with the eigenvalue $\lambda$
|
||||
of maximum modulus that is positive. Such an eigenvector $x$ is
|
||||
defined up to a multiplicative constant by the equation
|
||||
|
||||
.. math::
|
||||
|
||||
\lambda x^T = x^T A,
|
||||
|
||||
where $A$ is the adjacency matrix of the graph G. By definition of
|
||||
row-column product, the equation above is equivalent to
|
||||
|
||||
.. math::
|
||||
|
||||
\lambda x_i = \sum_{j\to i}x_j.
|
||||
|
||||
That is, adding the eigenvector centralities of the predecessors of
|
||||
$i$ one obtains the eigenvector centrality of $i$ multiplied by
|
||||
$\lambda$. In the case of undirected graphs, $x$ also solves the familiar
|
||||
right-eigenvector equation $Ax = \lambda x$.
|
||||
|
||||
By virtue of the Perron–Frobenius theorem [1]_, if G is strongly
|
||||
connected there is a unique eigenvector $x$, and all its entries
|
||||
are strictly positive.
|
||||
|
||||
If G is not strongly connected there might be several left
|
||||
eigenvectors associated with $\lambda$, and some of their elements
|
||||
might be zero.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph.
|
||||
|
||||
max_iter : integer, optional (default=50)
|
||||
Maximum number of Arnoldi update iterations allowed.
|
||||
|
||||
tol : float, optional (default=0)
|
||||
Relative accuracy for eigenvalues (stopping criterion).
|
||||
The default value of 0 implies machine precision.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal. Otherwise holds the
|
||||
name of the edge attribute used as weight. In this measure the
|
||||
weight is interpreted as the connection strength.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with eigenvector centrality as the value. The
|
||||
associated vector has unit Euclidean norm and the values are
|
||||
nonegative.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> centrality = nx.eigenvector_centrality_numpy(G)
|
||||
>>> print([f"{node} {centrality[node]:0.2f}" for node in centrality])
|
||||
['0 0.37', '1 0.60', '2 0.60', '3 0.37']
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXPointlessConcept
|
||||
If the graph G is the null graph.
|
||||
|
||||
ArpackNoConvergence
|
||||
When the requested convergence is not obtained. The currently
|
||||
converged eigenvalues and eigenvectors can be found as
|
||||
eigenvalues and eigenvectors attributes of the exception object.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:func:`scipy.sparse.linalg.eigs`
|
||||
eigenvector_centrality
|
||||
:func:`~networkx.algorithms.link_analysis.pagerank_alg.pagerank`
|
||||
:func:`~networkx.algorithms.link_analysis.hits_alg.hits`
|
||||
|
||||
Notes
|
||||
-----
|
||||
Eigenvector centrality was introduced by Landau [2]_ for chess
|
||||
tournaments. It was later rediscovered by Wei [3]_ and then
|
||||
popularized by Kendall [4]_ in the context of sport ranking. Berge
|
||||
introduced a general definition for graphs based on social connections
|
||||
[5]_. Bonacich [6]_ reintroduced again eigenvector centrality and made
|
||||
it popular in link analysis.
|
||||
|
||||
This function computes the left dominant eigenvector, which corresponds
|
||||
to adding the centrality of predecessors: this is the usual approach.
|
||||
To add the centrality of successors first reverse the graph with
|
||||
``G.reverse()``.
|
||||
|
||||
This implementation uses the
|
||||
:func:`SciPy sparse eigenvalue solver<scipy.sparse.linalg.eigs>` (ARPACK)
|
||||
to find the largest eigenvalue/eigenvector pair using Arnoldi iterations
|
||||
[7]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Abraham Berman and Robert J. Plemmons.
|
||||
"Nonnegative Matrices in the Mathematical Sciences."
|
||||
Classics in Applied Mathematics. SIAM, 1994.
|
||||
|
||||
.. [2] Edmund Landau.
|
||||
"Zur relativen Wertbemessung der Turnierresultate."
|
||||
Deutsches Wochenschach, 11:366–369, 1895.
|
||||
|
||||
.. [3] Teh-Hsing Wei.
|
||||
"The Algebraic Foundations of Ranking Theory."
|
||||
PhD thesis, University of Cambridge, 1952.
|
||||
|
||||
.. [4] Maurice G. Kendall.
|
||||
"Further contributions to the theory of paired comparisons."
|
||||
Biometrics, 11(1):43–62, 1955.
|
||||
https://www.jstor.org/stable/3001479
|
||||
|
||||
.. [5] Claude Berge
|
||||
"Théorie des graphes et ses applications."
|
||||
Dunod, Paris, France, 1958.
|
||||
|
||||
.. [6] Phillip Bonacich.
|
||||
"Technique for analyzing overlapping memberships."
|
||||
Sociological Methodology, 4:176–185, 1972.
|
||||
https://www.jstor.org/stable/270732
|
||||
|
||||
.. [7] Arnoldi iteration:: https://en.wikipedia.org/wiki/Arnoldi_iteration
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import scipy as sp
|
||||
|
||||
if len(G) == 0:
|
||||
raise nx.NetworkXPointlessConcept(
|
||||
"cannot compute centrality for the null graph"
|
||||
)
|
||||
M = nx.to_scipy_sparse_array(G, nodelist=list(G), weight=weight, dtype=float)
|
||||
_, eigenvector = sp.sparse.linalg.eigs(
|
||||
M.T, k=1, which="LR", maxiter=max_iter, tol=tol
|
||||
)
|
||||
largest = eigenvector.flatten().real
|
||||
norm = np.sign(largest.sum()) * sp.linalg.norm(largest)
|
||||
return dict(zip(G, (largest / norm).tolist()))
|
||||
130
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/flow_matrix.py
vendored
Normal file
130
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/flow_matrix.py
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
# Helpers for current-flow betweenness and current-flow closeness
|
||||
# Lazy computations for inverse Laplacian and flow-matrix rows.
|
||||
import networkx as nx
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def flow_matrix_row(G, weight=None, dtype=float, solver="lu"):
|
||||
# Generate a row of the current-flow matrix
|
||||
import numpy as np
|
||||
|
||||
solvername = {
|
||||
"full": FullInverseLaplacian,
|
||||
"lu": SuperLUInverseLaplacian,
|
||||
"cg": CGInverseLaplacian,
|
||||
}
|
||||
n = G.number_of_nodes()
|
||||
L = nx.laplacian_matrix(G, nodelist=range(n), weight=weight).asformat("csc")
|
||||
L = L.astype(dtype)
|
||||
C = solvername[solver](L, dtype=dtype) # initialize solver
|
||||
w = C.w # w is the Laplacian matrix width
|
||||
# row-by-row flow matrix
|
||||
for u, v in sorted(sorted((u, v)) for u, v in G.edges()):
|
||||
B = np.zeros(w, dtype=dtype)
|
||||
c = G[u][v].get(weight, 1.0)
|
||||
B[u % w] = c
|
||||
B[v % w] = -c
|
||||
# get only the rows needed in the inverse laplacian
|
||||
# and multiply to get the flow matrix row
|
||||
row = B @ C.get_rows(u, v)
|
||||
yield row, (u, v)
|
||||
|
||||
|
||||
# Class to compute the inverse laplacian only for specified rows
|
||||
# Allows computation of the current-flow matrix without storing entire
|
||||
# inverse laplacian matrix
|
||||
class InverseLaplacian:
|
||||
def __init__(self, L, width=None, dtype=None):
|
||||
global np
|
||||
import numpy as np
|
||||
|
||||
(n, n) = L.shape
|
||||
self.dtype = dtype
|
||||
self.n = n
|
||||
if width is None:
|
||||
self.w = self.width(L)
|
||||
else:
|
||||
self.w = width
|
||||
self.C = np.zeros((self.w, n), dtype=dtype)
|
||||
self.L1 = L[1:, 1:]
|
||||
self.init_solver(L)
|
||||
|
||||
def init_solver(self, L):
|
||||
pass
|
||||
|
||||
def solve(self, r):
|
||||
raise nx.NetworkXError("Implement solver")
|
||||
|
||||
def solve_inverse(self, r):
|
||||
raise nx.NetworkXError("Implement solver")
|
||||
|
||||
def get_rows(self, r1, r2):
|
||||
for r in range(r1, r2 + 1):
|
||||
self.C[r % self.w, 1:] = self.solve_inverse(r)
|
||||
return self.C
|
||||
|
||||
def get_row(self, r):
|
||||
self.C[r % self.w, 1:] = self.solve_inverse(r)
|
||||
return self.C[r % self.w]
|
||||
|
||||
def width(self, L):
|
||||
m = 0
|
||||
for i, row in enumerate(L):
|
||||
w = 0
|
||||
x, y = np.nonzero(row)
|
||||
if len(y) > 0:
|
||||
v = y - i
|
||||
w = v.max() - v.min() + 1
|
||||
m = max(w, m)
|
||||
return m
|
||||
|
||||
|
||||
class FullInverseLaplacian(InverseLaplacian):
|
||||
def init_solver(self, L):
|
||||
self.IL = np.zeros(L.shape, dtype=self.dtype)
|
||||
self.IL[1:, 1:] = np.linalg.inv(self.L1.todense())
|
||||
|
||||
def solve(self, rhs):
|
||||
s = np.zeros(rhs.shape, dtype=self.dtype)
|
||||
s = self.IL @ rhs
|
||||
return s
|
||||
|
||||
def solve_inverse(self, r):
|
||||
return self.IL[r, 1:]
|
||||
|
||||
|
||||
class SuperLUInverseLaplacian(InverseLaplacian):
|
||||
def init_solver(self, L):
|
||||
import scipy as sp
|
||||
|
||||
self.lusolve = sp.sparse.linalg.factorized(self.L1.tocsc())
|
||||
|
||||
def solve_inverse(self, r):
|
||||
rhs = np.zeros(self.n, dtype=self.dtype)
|
||||
rhs[r] = 1
|
||||
return self.lusolve(rhs[1:])
|
||||
|
||||
def solve(self, rhs):
|
||||
s = np.zeros(rhs.shape, dtype=self.dtype)
|
||||
s[1:] = self.lusolve(rhs[1:])
|
||||
return s
|
||||
|
||||
|
||||
class CGInverseLaplacian(InverseLaplacian):
|
||||
def init_solver(self, L):
|
||||
global sp
|
||||
import scipy as sp
|
||||
|
||||
ilu = sp.sparse.linalg.spilu(self.L1.tocsc())
|
||||
n = self.n - 1
|
||||
self.M = sp.sparse.linalg.LinearOperator(shape=(n, n), matvec=ilu.solve)
|
||||
|
||||
def solve(self, rhs):
|
||||
s = np.zeros(rhs.shape, dtype=self.dtype)
|
||||
s[1:] = sp.sparse.linalg.cg(self.L1, rhs[1:], M=self.M, atol=0)[0]
|
||||
return s
|
||||
|
||||
def solve_inverse(self, r):
|
||||
rhs = np.zeros(self.n, self.dtype)
|
||||
rhs[r] = 1
|
||||
return sp.sparse.linalg.cg(self.L1, rhs[1:], M=self.M, atol=0)[0]
|
||||
786
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/group.py
vendored
Normal file
786
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/group.py
vendored
Normal file
@@ -0,0 +1,786 @@
|
||||
"""Group centrality measures."""
|
||||
from copy import deepcopy
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms.centrality.betweenness import (
|
||||
_accumulate_endpoints,
|
||||
_single_source_dijkstra_path_basic,
|
||||
_single_source_shortest_path_basic,
|
||||
)
|
||||
from networkx.utils.decorators import not_implemented_for
|
||||
|
||||
__all__ = [
|
||||
"group_betweenness_centrality",
|
||||
"group_closeness_centrality",
|
||||
"group_degree_centrality",
|
||||
"group_in_degree_centrality",
|
||||
"group_out_degree_centrality",
|
||||
"prominent_group",
|
||||
]
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def group_betweenness_centrality(G, C, normalized=True, weight=None, endpoints=False):
|
||||
r"""Compute the group betweenness centrality for a group of nodes.
|
||||
|
||||
Group betweenness centrality of a group of nodes $C$ is the sum of the
|
||||
fraction of all-pairs shortest paths that pass through any vertex in $C$
|
||||
|
||||
.. math::
|
||||
|
||||
c_B(v) =\sum_{s,t \in V} \frac{\sigma(s, t|v)}{\sigma(s, t)}
|
||||
|
||||
where $V$ is the set of nodes, $\sigma(s, t)$ is the number of
|
||||
shortest $(s, t)$-paths, and $\sigma(s, t|C)$ is the number of
|
||||
those paths passing through some node in group $C$. Note that
|
||||
$(s, t)$ are not members of the group ($V-C$ is the set of nodes
|
||||
in $V$ that are not in $C$).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
C : list or set or list of lists or list of sets
|
||||
A group or a list of groups containing nodes which belong to G, for which group betweenness
|
||||
centrality is to be calculated.
|
||||
|
||||
normalized : bool, optional (default=True)
|
||||
If True, group betweenness is normalized by `1/((|V|-|C|)(|V|-|C|-1))`
|
||||
where `|V|` is the number of nodes in G and `|C|` is the number of nodes in C.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
The weight of an edge is treated as the length or distance between the two sides.
|
||||
|
||||
endpoints : bool, optional (default=False)
|
||||
If True include the endpoints in the shortest path counts.
|
||||
|
||||
Raises
|
||||
------
|
||||
NodeNotFound
|
||||
If node(s) in C are not present in G.
|
||||
|
||||
Returns
|
||||
-------
|
||||
betweenness : list of floats or float
|
||||
If C is a single group then return a float. If C is a list with
|
||||
several groups then return a list of group betweenness centralities.
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
Group betweenness centrality is described in [1]_ and its importance discussed in [3]_.
|
||||
The initial implementation of the algorithm is mentioned in [2]_. This function uses
|
||||
an improved algorithm presented in [4]_.
|
||||
|
||||
The number of nodes in the group must be a maximum of n - 2 where `n`
|
||||
is the total number of nodes in the graph.
|
||||
|
||||
For weighted graphs the edge weights must be greater than zero.
|
||||
Zero edge weights can produce an infinite number of equal length
|
||||
paths between pairs of nodes.
|
||||
|
||||
The total number of paths between source and target is counted
|
||||
differently for directed and undirected graphs. Directed paths
|
||||
between "u" and "v" are counted as two possible paths (one each
|
||||
direction) while undirected paths between "u" and "v" are counted
|
||||
as one path. Said another way, the sum in the expression above is
|
||||
over all ``s != t`` for directed graphs and for ``s < t`` for undirected graphs.
|
||||
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] M G Everett and S P Borgatti:
|
||||
The Centrality of Groups and Classes.
|
||||
Journal of Mathematical Sociology. 23(3): 181-201. 1999.
|
||||
http://www.analytictech.com/borgatti/group_centrality.htm
|
||||
.. [2] Ulrik Brandes:
|
||||
On Variants of Shortest-Path Betweenness
|
||||
Centrality and their Generic Computation.
|
||||
Social Networks 30(2):136-145, 2008.
|
||||
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.9610&rep=rep1&type=pdf
|
||||
.. [3] Sourav Medya et. al.:
|
||||
Group Centrality Maximization via Network Design.
|
||||
SIAM International Conference on Data Mining, SDM 2018, 126–134.
|
||||
https://sites.cs.ucsb.edu/~arlei/pubs/sdm18.pdf
|
||||
.. [4] Rami Puzis, Yuval Elovici, and Shlomi Dolev.
|
||||
"Fast algorithm for successive computation of group betweenness centrality."
|
||||
https://journals.aps.org/pre/pdf/10.1103/PhysRevE.76.056709
|
||||
|
||||
"""
|
||||
GBC = [] # initialize betweenness
|
||||
list_of_groups = True
|
||||
# check weather C contains one or many groups
|
||||
if any(el in G for el in C):
|
||||
C = [C]
|
||||
list_of_groups = False
|
||||
set_v = {node for group in C for node in group}
|
||||
if set_v - G.nodes: # element(s) of C not in G
|
||||
raise nx.NodeNotFound(f"The node(s) {set_v - G.nodes} are in C but not in G.")
|
||||
|
||||
# pre-processing
|
||||
PB, sigma, D = _group_preprocessing(G, set_v, weight)
|
||||
|
||||
# the algorithm for each group
|
||||
for group in C:
|
||||
group = set(group) # set of nodes in group
|
||||
# initialize the matrices of the sigma and the PB
|
||||
GBC_group = 0
|
||||
sigma_m = deepcopy(sigma)
|
||||
PB_m = deepcopy(PB)
|
||||
sigma_m_v = deepcopy(sigma_m)
|
||||
PB_m_v = deepcopy(PB_m)
|
||||
for v in group:
|
||||
GBC_group += PB_m[v][v]
|
||||
for x in group:
|
||||
for y in group:
|
||||
dxvy = 0
|
||||
dxyv = 0
|
||||
dvxy = 0
|
||||
if not (
|
||||
sigma_m[x][y] == 0 or sigma_m[x][v] == 0 or sigma_m[v][y] == 0
|
||||
):
|
||||
if D[x][v] == D[x][y] + D[y][v]:
|
||||
dxyv = sigma_m[x][y] * sigma_m[y][v] / sigma_m[x][v]
|
||||
if D[x][y] == D[x][v] + D[v][y]:
|
||||
dxvy = sigma_m[x][v] * sigma_m[v][y] / sigma_m[x][y]
|
||||
if D[v][y] == D[v][x] + D[x][y]:
|
||||
dvxy = sigma_m[v][x] * sigma[x][y] / sigma[v][y]
|
||||
sigma_m_v[x][y] = sigma_m[x][y] * (1 - dxvy)
|
||||
PB_m_v[x][y] = PB_m[x][y] - PB_m[x][y] * dxvy
|
||||
if y != v:
|
||||
PB_m_v[x][y] -= PB_m[x][v] * dxyv
|
||||
if x != v:
|
||||
PB_m_v[x][y] -= PB_m[v][y] * dvxy
|
||||
sigma_m, sigma_m_v = sigma_m_v, sigma_m
|
||||
PB_m, PB_m_v = PB_m_v, PB_m
|
||||
|
||||
# endpoints
|
||||
v, c = len(G), len(group)
|
||||
if not endpoints:
|
||||
scale = 0
|
||||
# if the graph is connected then subtract the endpoints from
|
||||
# the count for all the nodes in the graph. else count how many
|
||||
# nodes are connected to the group's nodes and subtract that.
|
||||
if nx.is_directed(G):
|
||||
if nx.is_strongly_connected(G):
|
||||
scale = c * (2 * v - c - 1)
|
||||
elif nx.is_connected(G):
|
||||
scale = c * (2 * v - c - 1)
|
||||
if scale == 0:
|
||||
for group_node1 in group:
|
||||
for node in D[group_node1]:
|
||||
if node != group_node1:
|
||||
if node in group:
|
||||
scale += 1
|
||||
else:
|
||||
scale += 2
|
||||
GBC_group -= scale
|
||||
|
||||
# normalized
|
||||
if normalized:
|
||||
scale = 1 / ((v - c) * (v - c - 1))
|
||||
GBC_group *= scale
|
||||
|
||||
# If undirected than count only the undirected edges
|
||||
elif not G.is_directed():
|
||||
GBC_group /= 2
|
||||
|
||||
GBC.append(GBC_group)
|
||||
if list_of_groups:
|
||||
return GBC
|
||||
return GBC[0]
|
||||
|
||||
|
||||
def _group_preprocessing(G, set_v, weight):
|
||||
sigma = {}
|
||||
delta = {}
|
||||
D = {}
|
||||
betweenness = dict.fromkeys(G, 0)
|
||||
for s in G:
|
||||
if weight is None: # use BFS
|
||||
S, P, sigma[s], D[s] = _single_source_shortest_path_basic(G, s)
|
||||
else: # use Dijkstra's algorithm
|
||||
S, P, sigma[s], D[s] = _single_source_dijkstra_path_basic(G, s, weight)
|
||||
betweenness, delta[s] = _accumulate_endpoints(betweenness, S, P, sigma[s], s)
|
||||
for i in delta[s]: # add the paths from s to i and rescale sigma
|
||||
if s != i:
|
||||
delta[s][i] += 1
|
||||
if weight is not None:
|
||||
sigma[s][i] = sigma[s][i] / 2
|
||||
# building the path betweenness matrix only for nodes that appear in the group
|
||||
PB = dict.fromkeys(G)
|
||||
for group_node1 in set_v:
|
||||
PB[group_node1] = dict.fromkeys(G, 0.0)
|
||||
for group_node2 in set_v:
|
||||
if group_node2 not in D[group_node1]:
|
||||
continue
|
||||
for node in G:
|
||||
# if node is connected to the two group nodes than continue
|
||||
if group_node2 in D[node] and group_node1 in D[node]:
|
||||
if (
|
||||
D[node][group_node2]
|
||||
== D[node][group_node1] + D[group_node1][group_node2]
|
||||
):
|
||||
PB[group_node1][group_node2] += (
|
||||
delta[node][group_node2]
|
||||
* sigma[node][group_node1]
|
||||
* sigma[group_node1][group_node2]
|
||||
/ sigma[node][group_node2]
|
||||
)
|
||||
return PB, sigma, D
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def prominent_group(
|
||||
G, k, weight=None, C=None, endpoints=False, normalized=True, greedy=False
|
||||
):
|
||||
r"""Find the prominent group of size $k$ in graph $G$. The prominence of the
|
||||
group is evaluated by the group betweenness centrality.
|
||||
|
||||
Group betweenness centrality of a group of nodes $C$ is the sum of the
|
||||
fraction of all-pairs shortest paths that pass through any vertex in $C$
|
||||
|
||||
.. math::
|
||||
|
||||
c_B(v) =\sum_{s,t \in V} \frac{\sigma(s, t|v)}{\sigma(s, t)}
|
||||
|
||||
where $V$ is the set of nodes, $\sigma(s, t)$ is the number of
|
||||
shortest $(s, t)$-paths, and $\sigma(s, t|C)$ is the number of
|
||||
those paths passing through some node in group $C$. Note that
|
||||
$(s, t)$ are not members of the group ($V-C$ is the set of nodes
|
||||
in $V$ that are not in $C$).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
k : int
|
||||
The number of nodes in the group.
|
||||
|
||||
normalized : bool, optional (default=True)
|
||||
If True, group betweenness is normalized by ``1/((|V|-|C|)(|V|-|C|-1))``
|
||||
where ``|V|`` is the number of nodes in G and ``|C|`` is the number of
|
||||
nodes in C.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
The weight of an edge is treated as the length or distance between the two sides.
|
||||
|
||||
endpoints : bool, optional (default=False)
|
||||
If True include the endpoints in the shortest path counts.
|
||||
|
||||
C : list or set, optional (default=None)
|
||||
list of nodes which won't be candidates of the prominent group.
|
||||
|
||||
greedy : bool, optional (default=False)
|
||||
Using a naive greedy algorithm in order to find non-optimal prominent
|
||||
group. For scale free networks the results are negligibly below the optimal
|
||||
results.
|
||||
|
||||
Raises
|
||||
------
|
||||
NodeNotFound
|
||||
If node(s) in C are not present in G.
|
||||
|
||||
Returns
|
||||
-------
|
||||
max_GBC : float
|
||||
The group betweenness centrality of the prominent group.
|
||||
|
||||
max_group : list
|
||||
The list of nodes in the prominent group.
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality, group_betweenness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
Group betweenness centrality is described in [1]_ and its importance discussed in [3]_.
|
||||
The algorithm is described in [2]_ and is based on techniques mentioned in [4]_.
|
||||
|
||||
The number of nodes in the group must be a maximum of ``n - 2`` where ``n``
|
||||
is the total number of nodes in the graph.
|
||||
|
||||
For weighted graphs the edge weights must be greater than zero.
|
||||
Zero edge weights can produce an infinite number of equal length
|
||||
paths between pairs of nodes.
|
||||
|
||||
The total number of paths between source and target is counted
|
||||
differently for directed and undirected graphs. Directed paths
|
||||
between "u" and "v" are counted as two possible paths (one each
|
||||
direction) while undirected paths between "u" and "v" are counted
|
||||
as one path. Said another way, the sum in the expression above is
|
||||
over all ``s != t`` for directed graphs and for ``s < t`` for undirected graphs.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] M G Everett and S P Borgatti:
|
||||
The Centrality of Groups and Classes.
|
||||
Journal of Mathematical Sociology. 23(3): 181-201. 1999.
|
||||
http://www.analytictech.com/borgatti/group_centrality.htm
|
||||
.. [2] Rami Puzis, Yuval Elovici, and Shlomi Dolev:
|
||||
"Finding the Most Prominent Group in Complex Networks"
|
||||
AI communications 20(4): 287-296, 2007.
|
||||
https://www.researchgate.net/profile/Rami_Puzis2/publication/220308855
|
||||
.. [3] Sourav Medya et. al.:
|
||||
Group Centrality Maximization via Network Design.
|
||||
SIAM International Conference on Data Mining, SDM 2018, 126–134.
|
||||
https://sites.cs.ucsb.edu/~arlei/pubs/sdm18.pdf
|
||||
.. [4] Rami Puzis, Yuval Elovici, and Shlomi Dolev.
|
||||
"Fast algorithm for successive computation of group betweenness centrality."
|
||||
https://journals.aps.org/pre/pdf/10.1103/PhysRevE.76.056709
|
||||
"""
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
if C is not None:
|
||||
C = set(C)
|
||||
if C - G.nodes: # element(s) of C not in G
|
||||
raise nx.NodeNotFound(f"The node(s) {C - G.nodes} are in C but not in G.")
|
||||
nodes = list(G.nodes - C)
|
||||
else:
|
||||
nodes = list(G.nodes)
|
||||
DF_tree = nx.Graph()
|
||||
DF_tree.__networkx_cache__ = None # Disable caching
|
||||
PB, sigma, D = _group_preprocessing(G, nodes, weight)
|
||||
betweenness = pd.DataFrame.from_dict(PB)
|
||||
if C is not None:
|
||||
for node in C:
|
||||
# remove from the betweenness all the nodes not part of the group
|
||||
betweenness.drop(index=node, inplace=True)
|
||||
betweenness.drop(columns=node, inplace=True)
|
||||
CL = [node for _, node in sorted(zip(np.diag(betweenness), nodes), reverse=True)]
|
||||
max_GBC = 0
|
||||
max_group = []
|
||||
DF_tree.add_node(
|
||||
1,
|
||||
CL=CL,
|
||||
betweenness=betweenness,
|
||||
GBC=0,
|
||||
GM=[],
|
||||
sigma=sigma,
|
||||
cont=dict(zip(nodes, np.diag(betweenness))),
|
||||
)
|
||||
|
||||
# the algorithm
|
||||
DF_tree.nodes[1]["heu"] = 0
|
||||
for i in range(k):
|
||||
DF_tree.nodes[1]["heu"] += DF_tree.nodes[1]["cont"][DF_tree.nodes[1]["CL"][i]]
|
||||
max_GBC, DF_tree, max_group = _dfbnb(
|
||||
G, k, DF_tree, max_GBC, 1, D, max_group, nodes, greedy
|
||||
)
|
||||
|
||||
v = len(G)
|
||||
if not endpoints:
|
||||
scale = 0
|
||||
# if the graph is connected then subtract the endpoints from
|
||||
# the count for all the nodes in the graph. else count how many
|
||||
# nodes are connected to the group's nodes and subtract that.
|
||||
if nx.is_directed(G):
|
||||
if nx.is_strongly_connected(G):
|
||||
scale = k * (2 * v - k - 1)
|
||||
elif nx.is_connected(G):
|
||||
scale = k * (2 * v - k - 1)
|
||||
if scale == 0:
|
||||
for group_node1 in max_group:
|
||||
for node in D[group_node1]:
|
||||
if node != group_node1:
|
||||
if node in max_group:
|
||||
scale += 1
|
||||
else:
|
||||
scale += 2
|
||||
max_GBC -= scale
|
||||
|
||||
# normalized
|
||||
if normalized:
|
||||
scale = 1 / ((v - k) * (v - k - 1))
|
||||
max_GBC *= scale
|
||||
|
||||
# If undirected then count only the undirected edges
|
||||
elif not G.is_directed():
|
||||
max_GBC /= 2
|
||||
max_GBC = float("%.2f" % max_GBC)
|
||||
return max_GBC, max_group
|
||||
|
||||
|
||||
def _dfbnb(G, k, DF_tree, max_GBC, root, D, max_group, nodes, greedy):
|
||||
# stopping condition - if we found a group of size k and with higher GBC then prune
|
||||
if len(DF_tree.nodes[root]["GM"]) == k and DF_tree.nodes[root]["GBC"] > max_GBC:
|
||||
return DF_tree.nodes[root]["GBC"], DF_tree, DF_tree.nodes[root]["GM"]
|
||||
# stopping condition - if the size of group members equal to k or there are less than
|
||||
# k - |GM| in the candidate list or the heuristic function plus the GBC is below the
|
||||
# maximal GBC found then prune
|
||||
if (
|
||||
len(DF_tree.nodes[root]["GM"]) == k
|
||||
or len(DF_tree.nodes[root]["CL"]) <= k - len(DF_tree.nodes[root]["GM"])
|
||||
or DF_tree.nodes[root]["GBC"] + DF_tree.nodes[root]["heu"] <= max_GBC
|
||||
):
|
||||
return max_GBC, DF_tree, max_group
|
||||
|
||||
# finding the heuristic of both children
|
||||
node_p, node_m, DF_tree = _heuristic(k, root, DF_tree, D, nodes, greedy)
|
||||
|
||||
# finding the child with the bigger heuristic + GBC and expand
|
||||
# that node first if greedy then only expand the plus node
|
||||
if greedy:
|
||||
max_GBC, DF_tree, max_group = _dfbnb(
|
||||
G, k, DF_tree, max_GBC, node_p, D, max_group, nodes, greedy
|
||||
)
|
||||
|
||||
elif (
|
||||
DF_tree.nodes[node_p]["GBC"] + DF_tree.nodes[node_p]["heu"]
|
||||
> DF_tree.nodes[node_m]["GBC"] + DF_tree.nodes[node_m]["heu"]
|
||||
):
|
||||
max_GBC, DF_tree, max_group = _dfbnb(
|
||||
G, k, DF_tree, max_GBC, node_p, D, max_group, nodes, greedy
|
||||
)
|
||||
max_GBC, DF_tree, max_group = _dfbnb(
|
||||
G, k, DF_tree, max_GBC, node_m, D, max_group, nodes, greedy
|
||||
)
|
||||
else:
|
||||
max_GBC, DF_tree, max_group = _dfbnb(
|
||||
G, k, DF_tree, max_GBC, node_m, D, max_group, nodes, greedy
|
||||
)
|
||||
max_GBC, DF_tree, max_group = _dfbnb(
|
||||
G, k, DF_tree, max_GBC, node_p, D, max_group, nodes, greedy
|
||||
)
|
||||
return max_GBC, DF_tree, max_group
|
||||
|
||||
|
||||
def _heuristic(k, root, DF_tree, D, nodes, greedy):
|
||||
import numpy as np
|
||||
|
||||
# This helper function add two nodes to DF_tree - one left son and the
|
||||
# other right son, finds their heuristic, CL, GBC, and GM
|
||||
node_p = DF_tree.number_of_nodes() + 1
|
||||
node_m = DF_tree.number_of_nodes() + 2
|
||||
added_node = DF_tree.nodes[root]["CL"][0]
|
||||
|
||||
# adding the plus node
|
||||
DF_tree.add_nodes_from([(node_p, deepcopy(DF_tree.nodes[root]))])
|
||||
DF_tree.nodes[node_p]["GM"].append(added_node)
|
||||
DF_tree.nodes[node_p]["GBC"] += DF_tree.nodes[node_p]["cont"][added_node]
|
||||
root_node = DF_tree.nodes[root]
|
||||
for x in nodes:
|
||||
for y in nodes:
|
||||
dxvy = 0
|
||||
dxyv = 0
|
||||
dvxy = 0
|
||||
if not (
|
||||
root_node["sigma"][x][y] == 0
|
||||
or root_node["sigma"][x][added_node] == 0
|
||||
or root_node["sigma"][added_node][y] == 0
|
||||
):
|
||||
if D[x][added_node] == D[x][y] + D[y][added_node]:
|
||||
dxyv = (
|
||||
root_node["sigma"][x][y]
|
||||
* root_node["sigma"][y][added_node]
|
||||
/ root_node["sigma"][x][added_node]
|
||||
)
|
||||
if D[x][y] == D[x][added_node] + D[added_node][y]:
|
||||
dxvy = (
|
||||
root_node["sigma"][x][added_node]
|
||||
* root_node["sigma"][added_node][y]
|
||||
/ root_node["sigma"][x][y]
|
||||
)
|
||||
if D[added_node][y] == D[added_node][x] + D[x][y]:
|
||||
dvxy = (
|
||||
root_node["sigma"][added_node][x]
|
||||
* root_node["sigma"][x][y]
|
||||
/ root_node["sigma"][added_node][y]
|
||||
)
|
||||
DF_tree.nodes[node_p]["sigma"][x][y] = root_node["sigma"][x][y] * (1 - dxvy)
|
||||
DF_tree.nodes[node_p]["betweenness"].loc[y, x] = (
|
||||
root_node["betweenness"][x][y] - root_node["betweenness"][x][y] * dxvy
|
||||
)
|
||||
if y != added_node:
|
||||
DF_tree.nodes[node_p]["betweenness"].loc[y, x] -= (
|
||||
root_node["betweenness"][x][added_node] * dxyv
|
||||
)
|
||||
if x != added_node:
|
||||
DF_tree.nodes[node_p]["betweenness"].loc[y, x] -= (
|
||||
root_node["betweenness"][added_node][y] * dvxy
|
||||
)
|
||||
|
||||
DF_tree.nodes[node_p]["CL"] = [
|
||||
node
|
||||
for _, node in sorted(
|
||||
zip(np.diag(DF_tree.nodes[node_p]["betweenness"]), nodes), reverse=True
|
||||
)
|
||||
if node not in DF_tree.nodes[node_p]["GM"]
|
||||
]
|
||||
DF_tree.nodes[node_p]["cont"] = dict(
|
||||
zip(nodes, np.diag(DF_tree.nodes[node_p]["betweenness"]))
|
||||
)
|
||||
DF_tree.nodes[node_p]["heu"] = 0
|
||||
for i in range(k - len(DF_tree.nodes[node_p]["GM"])):
|
||||
DF_tree.nodes[node_p]["heu"] += DF_tree.nodes[node_p]["cont"][
|
||||
DF_tree.nodes[node_p]["CL"][i]
|
||||
]
|
||||
|
||||
# adding the minus node - don't insert the first node in the CL to GM
|
||||
# Insert minus node only if isn't greedy type algorithm
|
||||
if not greedy:
|
||||
DF_tree.add_nodes_from([(node_m, deepcopy(DF_tree.nodes[root]))])
|
||||
DF_tree.nodes[node_m]["CL"].pop(0)
|
||||
DF_tree.nodes[node_m]["cont"].pop(added_node)
|
||||
DF_tree.nodes[node_m]["heu"] = 0
|
||||
for i in range(k - len(DF_tree.nodes[node_m]["GM"])):
|
||||
DF_tree.nodes[node_m]["heu"] += DF_tree.nodes[node_m]["cont"][
|
||||
DF_tree.nodes[node_m]["CL"][i]
|
||||
]
|
||||
else:
|
||||
node_m = None
|
||||
|
||||
return node_p, node_m, DF_tree
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def group_closeness_centrality(G, S, weight=None):
|
||||
r"""Compute the group closeness centrality for a group of nodes.
|
||||
|
||||
Group closeness centrality of a group of nodes $S$ is a measure
|
||||
of how close the group is to the other nodes in the graph.
|
||||
|
||||
.. math::
|
||||
|
||||
c_{close}(S) = \frac{|V-S|}{\sum_{v \in V-S} d_{S, v}}
|
||||
|
||||
d_{S, v} = min_{u \in S} (d_{u, v})
|
||||
|
||||
where $V$ is the set of nodes, $d_{S, v}$ is the distance of
|
||||
the group $S$ from $v$ defined as above. ($V-S$ is the set of nodes
|
||||
in $V$ that are not in $S$).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
S : list or set
|
||||
S is a group of nodes which belong to G, for which group closeness
|
||||
centrality is to be calculated.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
The weight of an edge is treated as the length or distance between the two sides.
|
||||
|
||||
Raises
|
||||
------
|
||||
NodeNotFound
|
||||
If node(s) in S are not present in G.
|
||||
|
||||
Returns
|
||||
-------
|
||||
closeness : float
|
||||
Group closeness centrality of the group S.
|
||||
|
||||
See Also
|
||||
--------
|
||||
closeness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The measure was introduced in [1]_.
|
||||
The formula implemented here is described in [2]_.
|
||||
|
||||
Higher values of closeness indicate greater centrality.
|
||||
|
||||
It is assumed that 1 / 0 is 0 (required in the case of directed graphs,
|
||||
or when a shortest path length is 0).
|
||||
|
||||
The number of nodes in the group must be a maximum of n - 1 where `n`
|
||||
is the total number of nodes in the graph.
|
||||
|
||||
For directed graphs, the incoming distance is utilized here. To use the
|
||||
outward distance, act on `G.reverse()`.
|
||||
|
||||
For weighted graphs the edge weights must be greater than zero.
|
||||
Zero edge weights can produce an infinite number of equal length
|
||||
paths between pairs of nodes.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] M G Everett and S P Borgatti:
|
||||
The Centrality of Groups and Classes.
|
||||
Journal of Mathematical Sociology. 23(3): 181-201. 1999.
|
||||
http://www.analytictech.com/borgatti/group_centrality.htm
|
||||
.. [2] J. Zhao et. al.:
|
||||
Measuring and Maximizing Group Closeness Centrality over
|
||||
Disk Resident Graphs.
|
||||
WWWConference Proceedings, 2014. 689-694.
|
||||
https://doi.org/10.1145/2567948.2579356
|
||||
"""
|
||||
if G.is_directed():
|
||||
G = G.reverse() # reverse view
|
||||
closeness = 0 # initialize to 0
|
||||
V = set(G) # set of nodes in G
|
||||
S = set(S) # set of nodes in group S
|
||||
V_S = V - S # set of nodes in V but not S
|
||||
shortest_path_lengths = nx.multi_source_dijkstra_path_length(G, S, weight=weight)
|
||||
# accumulation
|
||||
for v in V_S:
|
||||
try:
|
||||
closeness += shortest_path_lengths[v]
|
||||
except KeyError: # no path exists
|
||||
closeness += 0
|
||||
try:
|
||||
closeness = len(V_S) / closeness
|
||||
except ZeroDivisionError: # 1 / 0 assumed as 0
|
||||
closeness = 0
|
||||
return closeness
|
||||
|
||||
|
||||
@nx._dispatchable
|
||||
def group_degree_centrality(G, S):
|
||||
"""Compute the group degree centrality for a group of nodes.
|
||||
|
||||
Group degree centrality of a group of nodes $S$ is the fraction
|
||||
of non-group members connected to group members.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
S : list or set
|
||||
S is a group of nodes which belong to G, for which group degree
|
||||
centrality is to be calculated.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If node(s) in S are not in G.
|
||||
|
||||
Returns
|
||||
-------
|
||||
centrality : float
|
||||
Group degree centrality of the group S.
|
||||
|
||||
See Also
|
||||
--------
|
||||
degree_centrality
|
||||
group_in_degree_centrality
|
||||
group_out_degree_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The measure was introduced in [1]_.
|
||||
|
||||
The number of nodes in the group must be a maximum of n - 1 where `n`
|
||||
is the total number of nodes in the graph.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] M G Everett and S P Borgatti:
|
||||
The Centrality of Groups and Classes.
|
||||
Journal of Mathematical Sociology. 23(3): 181-201. 1999.
|
||||
http://www.analytictech.com/borgatti/group_centrality.htm
|
||||
"""
|
||||
centrality = len(set().union(*[set(G.neighbors(i)) for i in S]) - set(S))
|
||||
centrality /= len(G.nodes()) - len(S)
|
||||
return centrality
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
@nx._dispatchable
|
||||
def group_in_degree_centrality(G, S):
|
||||
"""Compute the group in-degree centrality for a group of nodes.
|
||||
|
||||
Group in-degree centrality of a group of nodes $S$ is the fraction
|
||||
of non-group members connected to group members by incoming edges.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
S : list or set
|
||||
S is a group of nodes which belong to G, for which group in-degree
|
||||
centrality is to be calculated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
centrality : float
|
||||
Group in-degree centrality of the group S.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If G is undirected.
|
||||
|
||||
NodeNotFound
|
||||
If node(s) in S are not in G.
|
||||
|
||||
See Also
|
||||
--------
|
||||
degree_centrality
|
||||
group_degree_centrality
|
||||
group_out_degree_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The number of nodes in the group must be a maximum of n - 1 where `n`
|
||||
is the total number of nodes in the graph.
|
||||
|
||||
`G.neighbors(i)` gives nodes with an outward edge from i, in a DiGraph,
|
||||
so for group in-degree centrality, the reverse graph is used.
|
||||
"""
|
||||
return group_degree_centrality(G.reverse(), S)
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
@nx._dispatchable
|
||||
def group_out_degree_centrality(G, S):
|
||||
"""Compute the group out-degree centrality for a group of nodes.
|
||||
|
||||
Group out-degree centrality of a group of nodes $S$ is the fraction
|
||||
of non-group members connected to group members by outgoing edges.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
S : list or set
|
||||
S is a group of nodes which belong to G, for which group in-degree
|
||||
centrality is to be calculated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
centrality : float
|
||||
Group out-degree centrality of the group S.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If G is undirected.
|
||||
|
||||
NodeNotFound
|
||||
If node(s) in S are not in G.
|
||||
|
||||
See Also
|
||||
--------
|
||||
degree_centrality
|
||||
group_degree_centrality
|
||||
group_in_degree_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The number of nodes in the group must be a maximum of n - 1 where `n`
|
||||
is the total number of nodes in the graph.
|
||||
|
||||
`G.neighbors(i)` gives nodes with an outward edge from i, in a DiGraph,
|
||||
so for group out-degree centrality, the graph itself is used.
|
||||
"""
|
||||
return group_degree_centrality(G, S)
|
||||
80
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/harmonic.py
vendored
Normal file
80
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/harmonic.py
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
"""Functions for computing the harmonic centrality of a graph."""
|
||||
from functools import partial
|
||||
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["harmonic_centrality"]
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="distance")
|
||||
def harmonic_centrality(G, nbunch=None, distance=None, sources=None):
|
||||
r"""Compute harmonic centrality for nodes.
|
||||
|
||||
Harmonic centrality [1]_ of a node `u` is the sum of the reciprocal
|
||||
of the shortest path distances from all other nodes to `u`
|
||||
|
||||
.. math::
|
||||
|
||||
C(u) = \sum_{v \neq u} \frac{1}{d(v, u)}
|
||||
|
||||
where `d(v, u)` is the shortest-path distance between `v` and `u`.
|
||||
|
||||
If `sources` is given as an argument, the returned harmonic centrality
|
||||
values are calculated as the sum of the reciprocals of the shortest
|
||||
path distances from the nodes specified in `sources` to `u` instead
|
||||
of from all nodes to `u`.
|
||||
|
||||
Notice that higher values indicate higher centrality.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
nbunch : container (default: all nodes in G)
|
||||
Container of nodes for which harmonic centrality values are calculated.
|
||||
|
||||
sources : container (default: all nodes in G)
|
||||
Container of nodes `v` over which reciprocal distances are computed.
|
||||
Nodes not in `G` are silently ignored.
|
||||
|
||||
distance : edge attribute key, optional (default=None)
|
||||
Use the specified edge attribute as the edge distance in shortest
|
||||
path calculations. If `None`, then each edge will have distance equal to 1.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with harmonic centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality, load_centrality, eigenvector_centrality,
|
||||
degree_centrality, closeness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
If the 'distance' keyword is set to an edge attribute key then the
|
||||
shortest-path length will be computed using Dijkstra's algorithm with
|
||||
that edge attribute as the edge weight.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Boldi, Paolo, and Sebastiano Vigna. "Axioms for centrality."
|
||||
Internet Mathematics 10.3-4 (2014): 222-262.
|
||||
"""
|
||||
|
||||
nbunch = set(G.nbunch_iter(nbunch)) if nbunch is not None else set(G.nodes)
|
||||
sources = set(G.nbunch_iter(sources)) if sources is not None else G.nodes
|
||||
|
||||
spl = partial(nx.shortest_path_length, G, weight=distance)
|
||||
centrality = {u: 0 for u in nbunch}
|
||||
for v in sources:
|
||||
dist = spl(v)
|
||||
for u in nbunch.intersection(dist):
|
||||
d = dist[u]
|
||||
if d == 0: # handle u == v and edges with 0 weight
|
||||
continue
|
||||
centrality[u] += 1 / d
|
||||
|
||||
return centrality
|
||||
330
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/katz.py
vendored
Normal file
330
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/katz.py
vendored
Normal file
@@ -0,0 +1,330 @@
|
||||
"""Katz centrality."""
|
||||
import math
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import not_implemented_for
|
||||
|
||||
__all__ = ["katz_centrality", "katz_centrality_numpy"]
|
||||
|
||||
|
||||
@not_implemented_for("multigraph")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def katz_centrality(
|
||||
G,
|
||||
alpha=0.1,
|
||||
beta=1.0,
|
||||
max_iter=1000,
|
||||
tol=1.0e-6,
|
||||
nstart=None,
|
||||
normalized=True,
|
||||
weight=None,
|
||||
):
|
||||
r"""Compute the Katz centrality for the nodes of the graph G.
|
||||
|
||||
Katz centrality computes the centrality for a node based on the centrality
|
||||
of its neighbors. It is a generalization of the eigenvector centrality. The
|
||||
Katz centrality for node $i$ is
|
||||
|
||||
.. math::
|
||||
|
||||
x_i = \alpha \sum_{j} A_{ij} x_j + \beta,
|
||||
|
||||
where $A$ is the adjacency matrix of graph G with eigenvalues $\lambda$.
|
||||
|
||||
The parameter $\beta$ controls the initial centrality and
|
||||
|
||||
.. math::
|
||||
|
||||
\alpha < \frac{1}{\lambda_{\max}}.
|
||||
|
||||
Katz centrality computes the relative influence of a node within a
|
||||
network by measuring the number of the immediate neighbors (first
|
||||
degree nodes) and also all other nodes in the network that connect
|
||||
to the node under consideration through these immediate neighbors.
|
||||
|
||||
Extra weight can be provided to immediate neighbors through the
|
||||
parameter $\beta$. Connections made with distant neighbors
|
||||
are, however, penalized by an attenuation factor $\alpha$ which
|
||||
should be strictly less than the inverse largest eigenvalue of the
|
||||
adjacency matrix in order for the Katz centrality to be computed
|
||||
correctly. More information is provided in [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
alpha : float, optional (default=0.1)
|
||||
Attenuation factor
|
||||
|
||||
beta : scalar or dictionary, optional (default=1.0)
|
||||
Weight attributed to the immediate neighborhood. If not a scalar, the
|
||||
dictionary must have a value for every node.
|
||||
|
||||
max_iter : integer, optional (default=1000)
|
||||
Maximum number of iterations in power method.
|
||||
|
||||
tol : float, optional (default=1.0e-6)
|
||||
Error tolerance used to check convergence in power method iteration.
|
||||
|
||||
nstart : dictionary, optional
|
||||
Starting value of Katz iteration for each node.
|
||||
|
||||
normalized : bool, optional (default=True)
|
||||
If True normalize the resulting values.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
In this measure the weight is interpreted as the connection strength.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with Katz centrality as the value.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the parameter `beta` is not a scalar but lacks a value for at least
|
||||
one node
|
||||
|
||||
PowerIterationFailedConvergence
|
||||
If the algorithm fails to converge to the specified tolerance
|
||||
within the specified number of iterations of the power iteration
|
||||
method.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import math
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> phi = (1 + math.sqrt(5)) / 2.0 # largest eigenvalue of adj matrix
|
||||
>>> centrality = nx.katz_centrality(G, 1 / phi - 0.01)
|
||||
>>> for n, c in sorted(centrality.items()):
|
||||
... print(f"{n} {c:.2f}")
|
||||
0 0.37
|
||||
1 0.60
|
||||
2 0.60
|
||||
3 0.37
|
||||
|
||||
See Also
|
||||
--------
|
||||
katz_centrality_numpy
|
||||
eigenvector_centrality
|
||||
eigenvector_centrality_numpy
|
||||
:func:`~networkx.algorithms.link_analysis.pagerank_alg.pagerank`
|
||||
:func:`~networkx.algorithms.link_analysis.hits_alg.hits`
|
||||
|
||||
Notes
|
||||
-----
|
||||
Katz centrality was introduced by [2]_.
|
||||
|
||||
This algorithm it uses the power method to find the eigenvector
|
||||
corresponding to the largest eigenvalue of the adjacency matrix of ``G``.
|
||||
The parameter ``alpha`` should be strictly less than the inverse of largest
|
||||
eigenvalue of the adjacency matrix for the algorithm to converge.
|
||||
You can use ``max(nx.adjacency_spectrum(G))`` to get $\lambda_{\max}$ the largest
|
||||
eigenvalue of the adjacency matrix.
|
||||
The iteration will stop after ``max_iter`` iterations or an error tolerance of
|
||||
``number_of_nodes(G) * tol`` has been reached.
|
||||
|
||||
For strongly connected graphs, as $\alpha \to 1/\lambda_{\max}$, and $\beta > 0$,
|
||||
Katz centrality approaches the results for eigenvector centrality.
|
||||
|
||||
For directed graphs this finds "left" eigenvectors which corresponds
|
||||
to the in-edges in the graph. For out-edges Katz centrality,
|
||||
first reverse the graph with ``G.reverse()``.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Mark E. J. Newman:
|
||||
Networks: An Introduction.
|
||||
Oxford University Press, USA, 2010, p. 720.
|
||||
.. [2] Leo Katz:
|
||||
A New Status Index Derived from Sociometric Index.
|
||||
Psychometrika 18(1):39–43, 1953
|
||||
https://link.springer.com/content/pdf/10.1007/BF02289026.pdf
|
||||
"""
|
||||
if len(G) == 0:
|
||||
return {}
|
||||
|
||||
nnodes = G.number_of_nodes()
|
||||
|
||||
if nstart is None:
|
||||
# choose starting vector with entries of 0
|
||||
x = {n: 0 for n in G}
|
||||
else:
|
||||
x = nstart
|
||||
|
||||
try:
|
||||
b = dict.fromkeys(G, float(beta))
|
||||
except (TypeError, ValueError, AttributeError) as err:
|
||||
b = beta
|
||||
if set(beta) != set(G):
|
||||
raise nx.NetworkXError(
|
||||
"beta dictionary must have a value for every node"
|
||||
) from err
|
||||
|
||||
# make up to max_iter iterations
|
||||
for _ in range(max_iter):
|
||||
xlast = x
|
||||
x = dict.fromkeys(xlast, 0)
|
||||
# do the multiplication y^T = Alpha * x^T A + Beta
|
||||
for n in x:
|
||||
for nbr in G[n]:
|
||||
x[nbr] += xlast[n] * G[n][nbr].get(weight, 1)
|
||||
for n in x:
|
||||
x[n] = alpha * x[n] + b[n]
|
||||
|
||||
# check convergence
|
||||
error = sum(abs(x[n] - xlast[n]) for n in x)
|
||||
if error < nnodes * tol:
|
||||
if normalized:
|
||||
# normalize vector
|
||||
try:
|
||||
s = 1.0 / math.hypot(*x.values())
|
||||
except ZeroDivisionError:
|
||||
s = 1.0
|
||||
else:
|
||||
s = 1
|
||||
for n in x:
|
||||
x[n] *= s
|
||||
return x
|
||||
raise nx.PowerIterationFailedConvergence(max_iter)
|
||||
|
||||
|
||||
@not_implemented_for("multigraph")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def katz_centrality_numpy(G, alpha=0.1, beta=1.0, normalized=True, weight=None):
|
||||
r"""Compute the Katz centrality for the graph G.
|
||||
|
||||
Katz centrality computes the centrality for a node based on the centrality
|
||||
of its neighbors. It is a generalization of the eigenvector centrality. The
|
||||
Katz centrality for node $i$ is
|
||||
|
||||
.. math::
|
||||
|
||||
x_i = \alpha \sum_{j} A_{ij} x_j + \beta,
|
||||
|
||||
where $A$ is the adjacency matrix of graph G with eigenvalues $\lambda$.
|
||||
|
||||
The parameter $\beta$ controls the initial centrality and
|
||||
|
||||
.. math::
|
||||
|
||||
\alpha < \frac{1}{\lambda_{\max}}.
|
||||
|
||||
Katz centrality computes the relative influence of a node within a
|
||||
network by measuring the number of the immediate neighbors (first
|
||||
degree nodes) and also all other nodes in the network that connect
|
||||
to the node under consideration through these immediate neighbors.
|
||||
|
||||
Extra weight can be provided to immediate neighbors through the
|
||||
parameter $\beta$. Connections made with distant neighbors
|
||||
are, however, penalized by an attenuation factor $\alpha$ which
|
||||
should be strictly less than the inverse largest eigenvalue of the
|
||||
adjacency matrix in order for the Katz centrality to be computed
|
||||
correctly. More information is provided in [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
alpha : float
|
||||
Attenuation factor
|
||||
|
||||
beta : scalar or dictionary, optional (default=1.0)
|
||||
Weight attributed to the immediate neighborhood. If not a scalar the
|
||||
dictionary must have an value for every node.
|
||||
|
||||
normalized : bool
|
||||
If True normalize the resulting values.
|
||||
|
||||
weight : None or string, optional
|
||||
If None, all edge weights are considered equal.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
In this measure the weight is interpreted as the connection strength.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with Katz centrality as the value.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the parameter `beta` is not a scalar but lacks a value for at least
|
||||
one node
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import math
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> phi = (1 + math.sqrt(5)) / 2.0 # largest eigenvalue of adj matrix
|
||||
>>> centrality = nx.katz_centrality_numpy(G, 1 / phi)
|
||||
>>> for n, c in sorted(centrality.items()):
|
||||
... print(f"{n} {c:.2f}")
|
||||
0 0.37
|
||||
1 0.60
|
||||
2 0.60
|
||||
3 0.37
|
||||
|
||||
See Also
|
||||
--------
|
||||
katz_centrality
|
||||
eigenvector_centrality_numpy
|
||||
eigenvector_centrality
|
||||
:func:`~networkx.algorithms.link_analysis.pagerank_alg.pagerank`
|
||||
:func:`~networkx.algorithms.link_analysis.hits_alg.hits`
|
||||
|
||||
Notes
|
||||
-----
|
||||
Katz centrality was introduced by [2]_.
|
||||
|
||||
This algorithm uses a direct linear solver to solve the above equation.
|
||||
The parameter ``alpha`` should be strictly less than the inverse of largest
|
||||
eigenvalue of the adjacency matrix for there to be a solution.
|
||||
You can use ``max(nx.adjacency_spectrum(G))`` to get $\lambda_{\max}$ the largest
|
||||
eigenvalue of the adjacency matrix.
|
||||
|
||||
For strongly connected graphs, as $\alpha \to 1/\lambda_{\max}$, and $\beta > 0$,
|
||||
Katz centrality approaches the results for eigenvector centrality.
|
||||
|
||||
For directed graphs this finds "left" eigenvectors which corresponds
|
||||
to the in-edges in the graph. For out-edges Katz centrality,
|
||||
first reverse the graph with ``G.reverse()``.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Mark E. J. Newman:
|
||||
Networks: An Introduction.
|
||||
Oxford University Press, USA, 2010, p. 173.
|
||||
.. [2] Leo Katz:
|
||||
A New Status Index Derived from Sociometric Index.
|
||||
Psychometrika 18(1):39–43, 1953
|
||||
https://link.springer.com/content/pdf/10.1007/BF02289026.pdf
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
if len(G) == 0:
|
||||
return {}
|
||||
try:
|
||||
nodelist = beta.keys()
|
||||
if set(nodelist) != set(G):
|
||||
raise nx.NetworkXError("beta dictionary must have a value for every node")
|
||||
b = np.array(list(beta.values()), dtype=float)
|
||||
except AttributeError:
|
||||
nodelist = list(G)
|
||||
try:
|
||||
b = np.ones((len(nodelist), 1)) * beta
|
||||
except (TypeError, ValueError, AttributeError) as err:
|
||||
raise nx.NetworkXError("beta must be a number") from err
|
||||
|
||||
A = nx.adjacency_matrix(G, nodelist=nodelist, weight=weight).todense().T
|
||||
n = A.shape[0]
|
||||
centrality = np.linalg.solve(np.eye(n, n) - (alpha * A), b).squeeze()
|
||||
|
||||
# Normalize: rely on truediv to cast to float, then tolist to make Python numbers
|
||||
norm = np.sign(sum(centrality)) * np.linalg.norm(centrality) if normalized else 1
|
||||
return dict(zip(nodelist, (centrality / norm).tolist()))
|
||||
149
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/laplacian.py
vendored
Normal file
149
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/laplacian.py
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
"""
|
||||
Laplacian centrality measures.
|
||||
"""
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["laplacian_centrality"]
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def laplacian_centrality(
|
||||
G, normalized=True, nodelist=None, weight="weight", walk_type=None, alpha=0.95
|
||||
):
|
||||
r"""Compute the Laplacian centrality for nodes in the graph `G`.
|
||||
|
||||
The Laplacian Centrality of a node ``i`` is measured by the drop in the
|
||||
Laplacian Energy after deleting node ``i`` from the graph. The Laplacian Energy
|
||||
is the sum of the squared eigenvalues of a graph's Laplacian matrix.
|
||||
|
||||
.. math::
|
||||
|
||||
C_L(u_i,G) = \frac{(\Delta E)_i}{E_L (G)} = \frac{E_L (G)-E_L (G_i)}{E_L (G)}
|
||||
|
||||
E_L (G) = \sum_{i=0}^n \lambda_i^2
|
||||
|
||||
Where $E_L (G)$ is the Laplacian energy of graph `G`,
|
||||
E_L (G_i) is the Laplacian energy of graph `G` after deleting node ``i``
|
||||
and $\lambda_i$ are the eigenvalues of `G`'s Laplacian matrix.
|
||||
This formula shows the normalized value. Without normalization,
|
||||
the numerator on the right side is returned.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph
|
||||
|
||||
normalized : bool (default = True)
|
||||
If True the centrality score is scaled so the sum over all nodes is 1.
|
||||
If False the centrality score for each node is the drop in Laplacian
|
||||
energy when that node is removed.
|
||||
|
||||
nodelist : list, optional (default = None)
|
||||
The rows and columns are ordered according to the nodes in nodelist.
|
||||
If nodelist is None, then the ordering is produced by G.nodes().
|
||||
|
||||
weight: string or None, optional (default=`weight`)
|
||||
Optional parameter `weight` to compute the Laplacian matrix.
|
||||
The edge data key used to compute each value in the matrix.
|
||||
If None, then each edge has weight 1.
|
||||
|
||||
walk_type : string or None, optional (default=None)
|
||||
Optional parameter `walk_type` used when calling
|
||||
:func:`directed_laplacian_matrix <networkx.directed_laplacian_matrix>`.
|
||||
One of ``"random"``, ``"lazy"``, or ``"pagerank"``. If ``walk_type=None``
|
||||
(the default), then a value is selected according to the properties of `G`:
|
||||
- ``walk_type="random"`` if `G` is strongly connected and aperiodic
|
||||
- ``walk_type="lazy"`` if `G` is strongly connected but not aperiodic
|
||||
- ``walk_type="pagerank"`` for all other cases.
|
||||
|
||||
alpha : real (default = 0.95)
|
||||
Optional parameter `alpha` used when calling
|
||||
:func:`directed_laplacian_matrix <networkx.directed_laplacian_matrix>`.
|
||||
(1 - alpha) is the teleportation probability used with pagerank.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with Laplacian centrality as the value.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph()
|
||||
>>> edges = [(0, 1, 4), (0, 2, 2), (2, 1, 1), (1, 3, 2), (1, 4, 2), (4, 5, 1)]
|
||||
>>> G.add_weighted_edges_from(edges)
|
||||
>>> sorted((v, f"{c:0.2f}") for v, c in laplacian_centrality(G).items())
|
||||
[(0, '0.70'), (1, '0.90'), (2, '0.28'), (3, '0.22'), (4, '0.26'), (5, '0.04')]
|
||||
|
||||
Notes
|
||||
-----
|
||||
The algorithm is implemented based on [1]_ with an extension to directed graphs
|
||||
using the ``directed_laplacian_matrix`` function.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXPointlessConcept
|
||||
If the graph `G` is the null graph.
|
||||
ZeroDivisionError
|
||||
If the graph `G` has no edges (is empty) and normalization is requested.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Qi, X., Fuller, E., Wu, Q., Wu, Y., and Zhang, C.-Q. (2012).
|
||||
Laplacian centrality: A new centrality measure for weighted networks.
|
||||
Information Sciences, 194:240-253.
|
||||
https://math.wvu.edu/~cqzhang/Publication-files/my-paper/INS-2012-Laplacian-W.pdf
|
||||
|
||||
See Also
|
||||
--------
|
||||
:func:`~networkx.linalg.laplacianmatrix.directed_laplacian_matrix`
|
||||
:func:`~networkx.linalg.laplacianmatrix.laplacian_matrix`
|
||||
"""
|
||||
import numpy as np
|
||||
import scipy as sp
|
||||
|
||||
if len(G) == 0:
|
||||
raise nx.NetworkXPointlessConcept("null graph has no centrality defined")
|
||||
if G.size(weight=weight) == 0:
|
||||
if normalized:
|
||||
raise ZeroDivisionError("graph with no edges has zero full energy")
|
||||
return {n: 0 for n in G}
|
||||
|
||||
if nodelist is not None:
|
||||
nodeset = set(G.nbunch_iter(nodelist))
|
||||
if len(nodeset) != len(nodelist):
|
||||
raise nx.NetworkXError("nodelist has duplicate nodes or nodes not in G")
|
||||
nodes = nodelist + [n for n in G if n not in nodeset]
|
||||
else:
|
||||
nodelist = nodes = list(G)
|
||||
|
||||
if G.is_directed():
|
||||
lap_matrix = nx.directed_laplacian_matrix(G, nodes, weight, walk_type, alpha)
|
||||
else:
|
||||
lap_matrix = nx.laplacian_matrix(G, nodes, weight).toarray()
|
||||
|
||||
full_energy = np.power(sp.linalg.eigh(lap_matrix, eigvals_only=True), 2).sum()
|
||||
|
||||
# calculate laplacian centrality
|
||||
laplace_centralities_dict = {}
|
||||
for i, node in enumerate(nodelist):
|
||||
# remove row and col i from lap_matrix
|
||||
all_but_i = list(np.arange(lap_matrix.shape[0]))
|
||||
all_but_i.remove(i)
|
||||
A_2 = lap_matrix[all_but_i, :][:, all_but_i]
|
||||
|
||||
# Adjust diagonal for removed row
|
||||
new_diag = lap_matrix.diagonal() - abs(lap_matrix[:, i])
|
||||
np.fill_diagonal(A_2, new_diag[all_but_i])
|
||||
|
||||
if len(all_but_i) > 0: # catches degenerate case of single node
|
||||
new_energy = np.power(sp.linalg.eigh(A_2, eigvals_only=True), 2).sum()
|
||||
else:
|
||||
new_energy = 0.0
|
||||
|
||||
lapl_cent = full_energy - new_energy
|
||||
if normalized:
|
||||
lapl_cent = lapl_cent / full_energy
|
||||
|
||||
laplace_centralities_dict[node] = float(lapl_cent)
|
||||
|
||||
return laplace_centralities_dict
|
||||
199
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/load.py
vendored
Normal file
199
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/load.py
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
"""Load centrality."""
|
||||
from operator import itemgetter
|
||||
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["load_centrality", "edge_load_centrality"]
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def newman_betweenness_centrality(G, v=None, cutoff=None, normalized=True, weight=None):
|
||||
"""Compute load centrality for nodes.
|
||||
|
||||
The load centrality of a node is the fraction of all shortest
|
||||
paths that pass through that node.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph.
|
||||
|
||||
normalized : bool, optional (default=True)
|
||||
If True the betweenness values are normalized by b=b/(n-1)(n-2) where
|
||||
n is the number of nodes in G.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, edge weights are ignored.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
The weight of an edge is treated as the length or distance between the two sides.
|
||||
|
||||
cutoff : bool, optional (default=None)
|
||||
If specified, only consider paths of length <= cutoff.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
Load centrality is slightly different than betweenness. It was originally
|
||||
introduced by [2]_. For this load algorithm see [1]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Mark E. J. Newman:
|
||||
Scientific collaboration networks. II.
|
||||
Shortest paths, weighted networks, and centrality.
|
||||
Physical Review E 64, 016132, 2001.
|
||||
http://journals.aps.org/pre/abstract/10.1103/PhysRevE.64.016132
|
||||
.. [2] Kwang-Il Goh, Byungnam Kahng and Doochul Kim
|
||||
Universal behavior of Load Distribution in Scale-Free Networks.
|
||||
Physical Review Letters 87(27):1–4, 2001.
|
||||
https://doi.org/10.1103/PhysRevLett.87.278701
|
||||
"""
|
||||
if v is not None: # only one node
|
||||
betweenness = 0.0
|
||||
for source in G:
|
||||
ubetween = _node_betweenness(G, source, cutoff, False, weight)
|
||||
betweenness += ubetween[v] if v in ubetween else 0
|
||||
if normalized:
|
||||
order = G.order()
|
||||
if order <= 2:
|
||||
return betweenness # no normalization b=0 for all nodes
|
||||
betweenness *= 1.0 / ((order - 1) * (order - 2))
|
||||
else:
|
||||
betweenness = {}.fromkeys(G, 0.0)
|
||||
for source in betweenness:
|
||||
ubetween = _node_betweenness(G, source, cutoff, False, weight)
|
||||
for vk in ubetween:
|
||||
betweenness[vk] += ubetween[vk]
|
||||
if normalized:
|
||||
order = G.order()
|
||||
if order <= 2:
|
||||
return betweenness # no normalization b=0 for all nodes
|
||||
scale = 1.0 / ((order - 1) * (order - 2))
|
||||
for v in betweenness:
|
||||
betweenness[v] *= scale
|
||||
return betweenness # all nodes
|
||||
|
||||
|
||||
def _node_betweenness(G, source, cutoff=False, normalized=True, weight=None):
|
||||
"""Node betweenness_centrality helper:
|
||||
|
||||
See betweenness_centrality for what you probably want.
|
||||
This actually computes "load" and not betweenness.
|
||||
See https://networkx.lanl.gov/ticket/103
|
||||
|
||||
This calculates the load of each node for paths from a single source.
|
||||
(The fraction of number of shortests paths from source that go
|
||||
through each node.)
|
||||
|
||||
To get the load for a node you need to do all-pairs shortest paths.
|
||||
|
||||
If weight is not None then use Dijkstra for finding shortest paths.
|
||||
"""
|
||||
# get the predecessor and path length data
|
||||
if weight is None:
|
||||
(pred, length) = nx.predecessor(G, source, cutoff=cutoff, return_seen=True)
|
||||
else:
|
||||
(pred, length) = nx.dijkstra_predecessor_and_distance(G, source, cutoff, weight)
|
||||
|
||||
# order the nodes by path length
|
||||
onodes = [(l, vert) for (vert, l) in length.items()]
|
||||
onodes.sort()
|
||||
onodes[:] = [vert for (l, vert) in onodes if l > 0]
|
||||
|
||||
# initialize betweenness
|
||||
between = {}.fromkeys(length, 1.0)
|
||||
|
||||
while onodes:
|
||||
v = onodes.pop()
|
||||
if v in pred:
|
||||
num_paths = len(pred[v]) # Discount betweenness if more than
|
||||
for x in pred[v]: # one shortest path.
|
||||
if x == source: # stop if hit source because all remaining v
|
||||
break # also have pred[v]==[source]
|
||||
between[x] += between[v] / num_paths
|
||||
# remove source
|
||||
for v in between:
|
||||
between[v] -= 1
|
||||
# rescale to be between 0 and 1
|
||||
if normalized:
|
||||
l = len(between)
|
||||
if l > 2:
|
||||
# scale by 1/the number of possible paths
|
||||
scale = 1 / ((l - 1) * (l - 2))
|
||||
for v in between:
|
||||
between[v] *= scale
|
||||
return between
|
||||
|
||||
|
||||
load_centrality = newman_betweenness_centrality
|
||||
|
||||
|
||||
@nx._dispatchable
|
||||
def edge_load_centrality(G, cutoff=False):
|
||||
"""Compute edge load.
|
||||
|
||||
WARNING: This concept of edge load has not been analysed
|
||||
or discussed outside of NetworkX that we know of.
|
||||
It is based loosely on load_centrality in the sense that
|
||||
it counts the number of shortest paths which cross each edge.
|
||||
This function is for demonstration and testing purposes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph
|
||||
|
||||
cutoff : bool, optional (default=False)
|
||||
If specified, only consider paths of length <= cutoff.
|
||||
|
||||
Returns
|
||||
-------
|
||||
A dict keyed by edge 2-tuple to the number of shortest paths
|
||||
which use that edge. Where more than one path is shortest
|
||||
the count is divided equally among paths.
|
||||
"""
|
||||
betweenness = {}
|
||||
for u, v in G.edges():
|
||||
betweenness[(u, v)] = 0.0
|
||||
betweenness[(v, u)] = 0.0
|
||||
|
||||
for source in G:
|
||||
ubetween = _edge_betweenness(G, source, cutoff=cutoff)
|
||||
for e, ubetweenv in ubetween.items():
|
||||
betweenness[e] += ubetweenv # cumulative total
|
||||
return betweenness
|
||||
|
||||
|
||||
def _edge_betweenness(G, source, nodes=None, cutoff=False):
|
||||
"""Edge betweenness helper."""
|
||||
# get the predecessor data
|
||||
(pred, length) = nx.predecessor(G, source, cutoff=cutoff, return_seen=True)
|
||||
# order the nodes by path length
|
||||
onodes = [n for n, d in sorted(length.items(), key=itemgetter(1))]
|
||||
# initialize betweenness, doesn't account for any edge weights
|
||||
between = {}
|
||||
for u, v in G.edges(nodes):
|
||||
between[(u, v)] = 1.0
|
||||
between[(v, u)] = 1.0
|
||||
|
||||
while onodes: # work through all paths
|
||||
v = onodes.pop()
|
||||
if v in pred:
|
||||
# Discount betweenness if more than one shortest path.
|
||||
num_paths = len(pred[v])
|
||||
for w in pred[v]:
|
||||
if w in pred:
|
||||
# Discount betweenness, mult path
|
||||
num_paths = len(pred[w])
|
||||
for x in pred[w]:
|
||||
between[(w, x)] += between[(v, w)] / num_paths
|
||||
between[(x, w)] += between[(w, v)] / num_paths
|
||||
return between
|
||||
128
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/percolation.py
vendored
Normal file
128
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/percolation.py
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
"""Percolation centrality measures."""
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms.centrality.betweenness import (
|
||||
_single_source_dijkstra_path_basic as dijkstra,
|
||||
)
|
||||
from networkx.algorithms.centrality.betweenness import (
|
||||
_single_source_shortest_path_basic as shortest_path,
|
||||
)
|
||||
|
||||
__all__ = ["percolation_centrality"]
|
||||
|
||||
|
||||
@nx._dispatchable(node_attrs="attribute", edge_attrs="weight")
|
||||
def percolation_centrality(G, attribute="percolation", states=None, weight=None):
|
||||
r"""Compute the percolation centrality for nodes.
|
||||
|
||||
Percolation centrality of a node $v$, at a given time, is defined
|
||||
as the proportion of ‘percolated paths’ that go through that node.
|
||||
|
||||
This measure quantifies relative impact of nodes based on their
|
||||
topological connectivity, as well as their percolation states.
|
||||
|
||||
Percolation states of nodes are used to depict network percolation
|
||||
scenarios (such as during infection transmission in a social network
|
||||
of individuals, spreading of computer viruses on computer networks, or
|
||||
transmission of disease over a network of towns) over time. In this
|
||||
measure usually the percolation state is expressed as a decimal
|
||||
between 0.0 and 1.0.
|
||||
|
||||
When all nodes are in the same percolated state this measure is
|
||||
equivalent to betweenness centrality.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
attribute : None or string, optional (default='percolation')
|
||||
Name of the node attribute to use for percolation state, used
|
||||
if `states` is None. If a node does not set the attribute the
|
||||
state of that node will be set to the default value of 1.
|
||||
If all nodes do not have the attribute all nodes will be set to
|
||||
1 and the centrality measure will be equivalent to betweenness centrality.
|
||||
|
||||
states : None or dict, optional (default=None)
|
||||
Specify percolation states for the nodes, nodes as keys states
|
||||
as values.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, all edge weights are considered equal.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
The weight of an edge is treated as the length or distance between the two sides.
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with percolation centrality as the value.
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
The algorithm is from Mahendra Piraveenan, Mikhail Prokopenko, and
|
||||
Liaquat Hossain [1]_
|
||||
Pair dependencies are calculated and accumulated using [2]_
|
||||
|
||||
For weighted graphs the edge weights must be greater than zero.
|
||||
Zero edge weights can produce an infinite number of equal length
|
||||
paths between pairs of nodes.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Mahendra Piraveenan, Mikhail Prokopenko, Liaquat Hossain
|
||||
Percolation Centrality: Quantifying Graph-Theoretic Impact of Nodes
|
||||
during Percolation in Networks
|
||||
http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0053095
|
||||
.. [2] Ulrik Brandes:
|
||||
A Faster Algorithm for Betweenness Centrality.
|
||||
Journal of Mathematical Sociology 25(2):163-177, 2001.
|
||||
https://doi.org/10.1080/0022250X.2001.9990249
|
||||
"""
|
||||
percolation = dict.fromkeys(G, 0.0) # b[v]=0 for v in G
|
||||
|
||||
nodes = G
|
||||
|
||||
if states is None:
|
||||
states = nx.get_node_attributes(nodes, attribute, default=1)
|
||||
|
||||
# sum of all percolation states
|
||||
p_sigma_x_t = 0.0
|
||||
for v in states.values():
|
||||
p_sigma_x_t += v
|
||||
|
||||
for s in nodes:
|
||||
# single source shortest paths
|
||||
if weight is None: # use BFS
|
||||
S, P, sigma, _ = shortest_path(G, s)
|
||||
else: # use Dijkstra's algorithm
|
||||
S, P, sigma, _ = dijkstra(G, s, weight)
|
||||
# accumulation
|
||||
percolation = _accumulate_percolation(
|
||||
percolation, S, P, sigma, s, states, p_sigma_x_t
|
||||
)
|
||||
|
||||
n = len(G)
|
||||
|
||||
for v in percolation:
|
||||
percolation[v] *= 1 / (n - 2)
|
||||
|
||||
return percolation
|
||||
|
||||
|
||||
def _accumulate_percolation(percolation, S, P, sigma, s, states, p_sigma_x_t):
|
||||
delta = dict.fromkeys(S, 0)
|
||||
while S:
|
||||
w = S.pop()
|
||||
coeff = (1 + delta[w]) / sigma[w]
|
||||
for v in P[w]:
|
||||
delta[v] += sigma[v] * coeff
|
||||
if w != s:
|
||||
# percolation weight
|
||||
pw_s_w = states[s] / (p_sigma_x_t - states[w])
|
||||
percolation[w] += delta[w] * pw_s_w
|
||||
return percolation
|
||||
206
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/reaching.py
vendored
Normal file
206
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/reaching.py
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
"""Functions for computing reaching centrality of a node or a graph."""
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import pairwise
|
||||
|
||||
__all__ = ["global_reaching_centrality", "local_reaching_centrality"]
|
||||
|
||||
|
||||
def _average_weight(G, path, weight=None):
|
||||
"""Returns the average weight of an edge in a weighted path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph.
|
||||
|
||||
path: list
|
||||
A list of vertices that define the path.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
If None, edge weights are ignored. Then the average weight of an edge
|
||||
is assumed to be the multiplicative inverse of the length of the path.
|
||||
Otherwise holds the name of the edge attribute used as weight.
|
||||
"""
|
||||
path_length = len(path) - 1
|
||||
if path_length <= 0:
|
||||
return 0
|
||||
if weight is None:
|
||||
return 1 / path_length
|
||||
total_weight = sum(G.edges[i, j][weight] for i, j in pairwise(path))
|
||||
return total_weight / path_length
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def global_reaching_centrality(G, weight=None, normalized=True):
|
||||
"""Returns the global reaching centrality of a directed graph.
|
||||
|
||||
The *global reaching centrality* of a weighted directed graph is the
|
||||
average over all nodes of the difference between the local reaching
|
||||
centrality of the node and the greatest local reaching centrality of
|
||||
any node in the graph [1]_. For more information on the local
|
||||
reaching centrality, see :func:`local_reaching_centrality`.
|
||||
Informally, the local reaching centrality is the proportion of the
|
||||
graph that is reachable from the neighbors of the node.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : DiGraph
|
||||
A networkx DiGraph.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
Attribute to use for edge weights. If ``None``, each edge weight
|
||||
is assumed to be one. A higher weight implies a stronger
|
||||
connection between nodes and a *shorter* path length.
|
||||
|
||||
normalized : bool, optional (default=True)
|
||||
Whether to normalize the edge weights by the total sum of edge
|
||||
weights.
|
||||
|
||||
Returns
|
||||
-------
|
||||
h : float
|
||||
The global reaching centrality of the graph.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.DiGraph()
|
||||
>>> G.add_edge(1, 2)
|
||||
>>> G.add_edge(1, 3)
|
||||
>>> nx.global_reaching_centrality(G)
|
||||
1.0
|
||||
>>> G.add_edge(3, 2)
|
||||
>>> nx.global_reaching_centrality(G)
|
||||
0.75
|
||||
|
||||
See also
|
||||
--------
|
||||
local_reaching_centrality
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Mones, Enys, Lilla Vicsek, and Tamás Vicsek.
|
||||
"Hierarchy Measure for Complex Networks."
|
||||
*PLoS ONE* 7.3 (2012): e33799.
|
||||
https://doi.org/10.1371/journal.pone.0033799
|
||||
"""
|
||||
if nx.is_negatively_weighted(G, weight=weight):
|
||||
raise nx.NetworkXError("edge weights must be positive")
|
||||
total_weight = G.size(weight=weight)
|
||||
if total_weight <= 0:
|
||||
raise nx.NetworkXError("Size of G must be positive")
|
||||
|
||||
# If provided, weights must be interpreted as connection strength
|
||||
# (so higher weights are more likely to be chosen). However, the
|
||||
# shortest path algorithms in NetworkX assume the provided "weight"
|
||||
# is actually a distance (so edges with higher weight are less
|
||||
# likely to be chosen). Therefore we need to invert the weights when
|
||||
# computing shortest paths.
|
||||
#
|
||||
# If weight is None, we leave it as-is so that the shortest path
|
||||
# algorithm can use a faster, unweighted algorithm.
|
||||
if weight is not None:
|
||||
|
||||
def as_distance(u, v, d):
|
||||
return total_weight / d.get(weight, 1)
|
||||
|
||||
shortest_paths = nx.shortest_path(G, weight=as_distance)
|
||||
else:
|
||||
shortest_paths = nx.shortest_path(G)
|
||||
|
||||
centrality = local_reaching_centrality
|
||||
# TODO This can be trivially parallelized.
|
||||
lrc = [
|
||||
centrality(G, node, paths=paths, weight=weight, normalized=normalized)
|
||||
for node, paths in shortest_paths.items()
|
||||
]
|
||||
|
||||
max_lrc = max(lrc)
|
||||
return sum(max_lrc - c for c in lrc) / (len(G) - 1)
|
||||
|
||||
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def local_reaching_centrality(G, v, paths=None, weight=None, normalized=True):
|
||||
"""Returns the local reaching centrality of a node in a directed
|
||||
graph.
|
||||
|
||||
The *local reaching centrality* of a node in a directed graph is the
|
||||
proportion of other nodes reachable from that node [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : DiGraph
|
||||
A NetworkX DiGraph.
|
||||
|
||||
v : node
|
||||
A node in the directed graph `G`.
|
||||
|
||||
paths : dictionary (default=None)
|
||||
If this is not `None` it must be a dictionary representation
|
||||
of single-source shortest paths, as computed by, for example,
|
||||
:func:`networkx.shortest_path` with source node `v`. Use this
|
||||
keyword argument if you intend to invoke this function many
|
||||
times but don't want the paths to be recomputed each time.
|
||||
|
||||
weight : None or string, optional (default=None)
|
||||
Attribute to use for edge weights. If `None`, each edge weight
|
||||
is assumed to be one. A higher weight implies a stronger
|
||||
connection between nodes and a *shorter* path length.
|
||||
|
||||
normalized : bool, optional (default=True)
|
||||
Whether to normalize the edge weights by the total sum of edge
|
||||
weights.
|
||||
|
||||
Returns
|
||||
-------
|
||||
h : float
|
||||
The local reaching centrality of the node ``v`` in the graph
|
||||
``G``.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.DiGraph()
|
||||
>>> G.add_edges_from([(1, 2), (1, 3)])
|
||||
>>> nx.local_reaching_centrality(G, 3)
|
||||
0.0
|
||||
>>> G.add_edge(3, 2)
|
||||
>>> nx.local_reaching_centrality(G, 3)
|
||||
0.5
|
||||
|
||||
See also
|
||||
--------
|
||||
global_reaching_centrality
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Mones, Enys, Lilla Vicsek, and Tamás Vicsek.
|
||||
"Hierarchy Measure for Complex Networks."
|
||||
*PLoS ONE* 7.3 (2012): e33799.
|
||||
https://doi.org/10.1371/journal.pone.0033799
|
||||
"""
|
||||
if paths is None:
|
||||
if nx.is_negatively_weighted(G, weight=weight):
|
||||
raise nx.NetworkXError("edge weights must be positive")
|
||||
total_weight = G.size(weight=weight)
|
||||
if total_weight <= 0:
|
||||
raise nx.NetworkXError("Size of G must be positive")
|
||||
if weight is not None:
|
||||
# Interpret weights as lengths.
|
||||
def as_distance(u, v, d):
|
||||
return total_weight / d.get(weight, 1)
|
||||
|
||||
paths = nx.shortest_path(G, source=v, weight=as_distance)
|
||||
else:
|
||||
paths = nx.shortest_path(G, source=v)
|
||||
# If the graph is unweighted, simply return the proportion of nodes
|
||||
# reachable from the source node ``v``.
|
||||
if weight is None and G.is_directed():
|
||||
return (len(paths) - 1) / (len(G) - 1)
|
||||
if normalized and weight is not None:
|
||||
norm = G.size(weight=weight) / G.size()
|
||||
else:
|
||||
norm = 1
|
||||
# TODO This can be trivially parallelized.
|
||||
avgw = (_average_weight(G, path, weight=weight) for path in paths.values())
|
||||
sum_avg_weight = sum(avgw) / norm
|
||||
return sum_avg_weight / (len(G) - 1)
|
||||
141
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/second_order.py
vendored
Normal file
141
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/second_order.py
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
"""Copyright (c) 2015 – Thomson Licensing, SAS
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted (subject to the limitations in the
|
||||
disclaimer below) provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Thomson Licensing, or Technicolor, nor the names
|
||||
of its contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
|
||||
GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
|
||||
HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import not_implemented_for
|
||||
|
||||
# Authors: Erwan Le Merrer (erwan.lemerrer@technicolor.com)
|
||||
|
||||
__all__ = ["second_order_centrality"]
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def second_order_centrality(G, weight="weight"):
|
||||
"""Compute the second order centrality for nodes of G.
|
||||
|
||||
The second order centrality of a given node is the standard deviation of
|
||||
the return times to that node of a perpetual random walk on G:
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX connected and undirected graph.
|
||||
|
||||
weight : string or None, optional (default="weight")
|
||||
The name of an edge attribute that holds the numerical value
|
||||
used as a weight. If None then each edge has weight 1.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary keyed by node with second order centrality as the value.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.star_graph(10)
|
||||
>>> soc = nx.second_order_centrality(G)
|
||||
>>> print(sorted(soc.items(), key=lambda x: x[1])[0][0]) # pick first id
|
||||
0
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXException
|
||||
If the graph G is empty, non connected or has negative weights.
|
||||
|
||||
See Also
|
||||
--------
|
||||
betweenness_centrality
|
||||
|
||||
Notes
|
||||
-----
|
||||
Lower values of second order centrality indicate higher centrality.
|
||||
|
||||
The algorithm is from Kermarrec, Le Merrer, Sericola and Trédan [1]_.
|
||||
|
||||
This code implements the analytical version of the algorithm, i.e.,
|
||||
there is no simulation of a random walk process involved. The random walk
|
||||
is here unbiased (corresponding to eq 6 of the paper [1]_), thus the
|
||||
centrality values are the standard deviations for random walk return times
|
||||
on the transformed input graph G (equal in-degree at each nodes by adding
|
||||
self-loops).
|
||||
|
||||
Complexity of this implementation, made to run locally on a single machine,
|
||||
is O(n^3), with n the size of G, which makes it viable only for small
|
||||
graphs.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Anne-Marie Kermarrec, Erwan Le Merrer, Bruno Sericola, Gilles Trédan
|
||||
"Second order centrality: Distributed assessment of nodes criticity in
|
||||
complex networks", Elsevier Computer Communications 34(5):619-628, 2011.
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
n = len(G)
|
||||
|
||||
if n == 0:
|
||||
raise nx.NetworkXException("Empty graph.")
|
||||
if not nx.is_connected(G):
|
||||
raise nx.NetworkXException("Non connected graph.")
|
||||
if any(d.get(weight, 0) < 0 for u, v, d in G.edges(data=True)):
|
||||
raise nx.NetworkXException("Graph has negative edge weights.")
|
||||
|
||||
# balancing G for Metropolis-Hastings random walks
|
||||
G = nx.DiGraph(G)
|
||||
in_deg = dict(G.in_degree(weight=weight))
|
||||
d_max = max(in_deg.values())
|
||||
for i, deg in in_deg.items():
|
||||
if deg < d_max:
|
||||
G.add_edge(i, i, weight=d_max - deg)
|
||||
|
||||
P = nx.to_numpy_array(G)
|
||||
P /= P.sum(axis=1)[:, np.newaxis] # to transition probability matrix
|
||||
|
||||
def _Qj(P, j):
|
||||
P = P.copy()
|
||||
P[:, j] = 0
|
||||
return P
|
||||
|
||||
M = np.empty([n, n])
|
||||
|
||||
for i in range(n):
|
||||
M[:, i] = np.linalg.solve(
|
||||
np.identity(n) - _Qj(P, i), np.ones([n, 1])[:, 0]
|
||||
) # eq 3
|
||||
|
||||
return dict(
|
||||
zip(
|
||||
G.nodes,
|
||||
(float(np.sqrt(2 * np.sum(M[:, i]) - n * (n + 1))) for i in range(n)),
|
||||
)
|
||||
) # eq 6
|
||||
339
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/subgraph_alg.py
vendored
Normal file
339
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/subgraph_alg.py
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
"""
|
||||
Subraph centrality and communicability betweenness.
|
||||
"""
|
||||
import networkx as nx
|
||||
from networkx.utils import not_implemented_for
|
||||
|
||||
__all__ = [
|
||||
"subgraph_centrality_exp",
|
||||
"subgraph_centrality",
|
||||
"communicability_betweenness_centrality",
|
||||
"estrada_index",
|
||||
]
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@not_implemented_for("multigraph")
|
||||
@nx._dispatchable
|
||||
def subgraph_centrality_exp(G):
|
||||
r"""Returns the subgraph centrality for each node of G.
|
||||
|
||||
Subgraph centrality of a node `n` is the sum of weighted closed
|
||||
walks of all lengths starting and ending at node `n`. The weights
|
||||
decrease with path length. Each closed walk is associated with a
|
||||
connected subgraph ([1]_).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G: graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes:dictionary
|
||||
Dictionary of nodes with subgraph centrality as the value.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the graph is not undirected and simple.
|
||||
|
||||
See Also
|
||||
--------
|
||||
subgraph_centrality:
|
||||
Alternative algorithm of the subgraph centrality for each node of G.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This version of the algorithm exponentiates the adjacency matrix.
|
||||
|
||||
The subgraph centrality of a node `u` in G can be found using
|
||||
the matrix exponential of the adjacency matrix of G [1]_,
|
||||
|
||||
.. math::
|
||||
|
||||
SC(u)=(e^A)_{uu} .
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Ernesto Estrada, Juan A. Rodriguez-Velazquez,
|
||||
"Subgraph centrality in complex networks",
|
||||
Physical Review E 71, 056103 (2005).
|
||||
https://arxiv.org/abs/cond-mat/0504730
|
||||
|
||||
Examples
|
||||
--------
|
||||
(Example from [1]_)
|
||||
>>> G = nx.Graph(
|
||||
... [
|
||||
... (1, 2),
|
||||
... (1, 5),
|
||||
... (1, 8),
|
||||
... (2, 3),
|
||||
... (2, 8),
|
||||
... (3, 4),
|
||||
... (3, 6),
|
||||
... (4, 5),
|
||||
... (4, 7),
|
||||
... (5, 6),
|
||||
... (6, 7),
|
||||
... (7, 8),
|
||||
... ]
|
||||
... )
|
||||
>>> sc = nx.subgraph_centrality_exp(G)
|
||||
>>> print([f"{node} {sc[node]:0.2f}" for node in sorted(sc)])
|
||||
['1 3.90', '2 3.90', '3 3.64', '4 3.71', '5 3.64', '6 3.71', '7 3.64', '8 3.90']
|
||||
"""
|
||||
# alternative implementation that calculates the matrix exponential
|
||||
import scipy as sp
|
||||
|
||||
nodelist = list(G) # ordering of nodes in matrix
|
||||
A = nx.to_numpy_array(G, nodelist)
|
||||
# convert to 0-1 matrix
|
||||
A[A != 0.0] = 1
|
||||
expA = sp.linalg.expm(A)
|
||||
# convert diagonal to dictionary keyed by node
|
||||
sc = dict(zip(nodelist, map(float, expA.diagonal())))
|
||||
return sc
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@not_implemented_for("multigraph")
|
||||
@nx._dispatchable
|
||||
def subgraph_centrality(G):
|
||||
r"""Returns subgraph centrality for each node in G.
|
||||
|
||||
Subgraph centrality of a node `n` is the sum of weighted closed
|
||||
walks of all lengths starting and ending at node `n`. The weights
|
||||
decrease with path length. Each closed walk is associated with a
|
||||
connected subgraph ([1]_).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G: graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with subgraph centrality as the value.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the graph is not undirected and simple.
|
||||
|
||||
See Also
|
||||
--------
|
||||
subgraph_centrality_exp:
|
||||
Alternative algorithm of the subgraph centrality for each node of G.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This version of the algorithm computes eigenvalues and eigenvectors
|
||||
of the adjacency matrix.
|
||||
|
||||
Subgraph centrality of a node `u` in G can be found using
|
||||
a spectral decomposition of the adjacency matrix [1]_,
|
||||
|
||||
.. math::
|
||||
|
||||
SC(u)=\sum_{j=1}^{N}(v_{j}^{u})^2 e^{\lambda_{j}},
|
||||
|
||||
where `v_j` is an eigenvector of the adjacency matrix `A` of G
|
||||
corresponding to the eigenvalue `\lambda_j`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
(Example from [1]_)
|
||||
>>> G = nx.Graph(
|
||||
... [
|
||||
... (1, 2),
|
||||
... (1, 5),
|
||||
... (1, 8),
|
||||
... (2, 3),
|
||||
... (2, 8),
|
||||
... (3, 4),
|
||||
... (3, 6),
|
||||
... (4, 5),
|
||||
... (4, 7),
|
||||
... (5, 6),
|
||||
... (6, 7),
|
||||
... (7, 8),
|
||||
... ]
|
||||
... )
|
||||
>>> sc = nx.subgraph_centrality(G)
|
||||
>>> print([f"{node} {sc[node]:0.2f}" for node in sorted(sc)])
|
||||
['1 3.90', '2 3.90', '3 3.64', '4 3.71', '5 3.64', '6 3.71', '7 3.64', '8 3.90']
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Ernesto Estrada, Juan A. Rodriguez-Velazquez,
|
||||
"Subgraph centrality in complex networks",
|
||||
Physical Review E 71, 056103 (2005).
|
||||
https://arxiv.org/abs/cond-mat/0504730
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
nodelist = list(G) # ordering of nodes in matrix
|
||||
A = nx.to_numpy_array(G, nodelist)
|
||||
# convert to 0-1 matrix
|
||||
A[np.nonzero(A)] = 1
|
||||
w, v = np.linalg.eigh(A)
|
||||
vsquare = np.array(v) ** 2
|
||||
expw = np.exp(w)
|
||||
xg = vsquare @ expw
|
||||
# convert vector dictionary keyed by node
|
||||
sc = dict(zip(nodelist, map(float, xg)))
|
||||
return sc
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@not_implemented_for("multigraph")
|
||||
@nx._dispatchable
|
||||
def communicability_betweenness_centrality(G):
|
||||
r"""Returns subgraph communicability for all pairs of nodes in G.
|
||||
|
||||
Communicability betweenness measure makes use of the number of walks
|
||||
connecting every pair of nodes as the basis of a betweenness centrality
|
||||
measure.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G: graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dictionary
|
||||
Dictionary of nodes with communicability betweenness as the value.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the graph is not undirected and simple.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Let `G=(V,E)` be a simple undirected graph with `n` nodes and `m` edges,
|
||||
and `A` denote the adjacency matrix of `G`.
|
||||
|
||||
Let `G(r)=(V,E(r))` be the graph resulting from
|
||||
removing all edges connected to node `r` but not the node itself.
|
||||
|
||||
The adjacency matrix for `G(r)` is `A+E(r)`, where `E(r)` has nonzeros
|
||||
only in row and column `r`.
|
||||
|
||||
The subraph betweenness of a node `r` is [1]_
|
||||
|
||||
.. math::
|
||||
|
||||
\omega_{r} = \frac{1}{C}\sum_{p}\sum_{q}\frac{G_{prq}}{G_{pq}},
|
||||
p\neq q, q\neq r,
|
||||
|
||||
where
|
||||
`G_{prq}=(e^{A}_{pq} - (e^{A+E(r)})_{pq}` is the number of walks
|
||||
involving node r,
|
||||
`G_{pq}=(e^{A})_{pq}` is the number of closed walks starting
|
||||
at node `p` and ending at node `q`,
|
||||
and `C=(n-1)^{2}-(n-1)` is a normalization factor equal to the
|
||||
number of terms in the sum.
|
||||
|
||||
The resulting `\omega_{r}` takes values between zero and one.
|
||||
The lower bound cannot be attained for a connected
|
||||
graph, and the upper bound is attained in the star graph.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Ernesto Estrada, Desmond J. Higham, Naomichi Hatano,
|
||||
"Communicability Betweenness in Complex Networks"
|
||||
Physica A 388 (2009) 764-774.
|
||||
https://arxiv.org/abs/0905.4102
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph([(0, 1), (1, 2), (1, 5), (5, 4), (2, 4), (2, 3), (4, 3), (3, 6)])
|
||||
>>> cbc = nx.communicability_betweenness_centrality(G)
|
||||
>>> print([f"{node} {cbc[node]:0.2f}" for node in sorted(cbc)])
|
||||
['0 0.03', '1 0.45', '2 0.51', '3 0.45', '4 0.40', '5 0.19', '6 0.03']
|
||||
"""
|
||||
import numpy as np
|
||||
import scipy as sp
|
||||
|
||||
nodelist = list(G) # ordering of nodes in matrix
|
||||
n = len(nodelist)
|
||||
A = nx.to_numpy_array(G, nodelist)
|
||||
# convert to 0-1 matrix
|
||||
A[np.nonzero(A)] = 1
|
||||
expA = sp.linalg.expm(A)
|
||||
mapping = dict(zip(nodelist, range(n)))
|
||||
cbc = {}
|
||||
for v in G:
|
||||
# remove row and col of node v
|
||||
i = mapping[v]
|
||||
row = A[i, :].copy()
|
||||
col = A[:, i].copy()
|
||||
A[i, :] = 0
|
||||
A[:, i] = 0
|
||||
B = (expA - sp.linalg.expm(A)) / expA
|
||||
# sum with row/col of node v and diag set to zero
|
||||
B[i, :] = 0
|
||||
B[:, i] = 0
|
||||
B -= np.diag(np.diag(B))
|
||||
cbc[v] = float(B.sum())
|
||||
# put row and col back
|
||||
A[i, :] = row
|
||||
A[:, i] = col
|
||||
# rescale when more than two nodes
|
||||
order = len(cbc)
|
||||
if order > 2:
|
||||
scale = 1.0 / ((order - 1.0) ** 2 - (order - 1.0))
|
||||
cbc = {node: value * scale for node, value in cbc.items()}
|
||||
return cbc
|
||||
|
||||
|
||||
@nx._dispatchable
|
||||
def estrada_index(G):
|
||||
r"""Returns the Estrada index of a the graph G.
|
||||
|
||||
The Estrada Index is a topological index of folding or 3D "compactness" ([1]_).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G: graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
estrada index: float
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the graph is not undirected and simple.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Let `G=(V,E)` be a simple undirected graph with `n` nodes and let
|
||||
`\lambda_{1}\leq\lambda_{2}\leq\cdots\lambda_{n}`
|
||||
be a non-increasing ordering of the eigenvalues of its adjacency
|
||||
matrix `A`. The Estrada index is ([1]_, [2]_)
|
||||
|
||||
.. math::
|
||||
EE(G)=\sum_{j=1}^n e^{\lambda _j}.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] E. Estrada, "Characterization of 3D molecular structure",
|
||||
Chem. Phys. Lett. 319, 713 (2000).
|
||||
https://doi.org/10.1016/S0009-2614(00)00158-5
|
||||
.. [2] José Antonio de la Peñaa, Ivan Gutman, Juan Rada,
|
||||
"Estimating the Estrada index",
|
||||
Linear Algebra and its Applications. 427, 1 (2007).
|
||||
https://doi.org/10.1016/j.laa.2007.06.020
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph([(0, 1), (1, 2), (1, 5), (5, 4), (2, 4), (2, 3), (4, 3), (3, 6)])
|
||||
>>> ei = nx.estrada_index(G)
|
||||
>>> print(f"{ei:0.5}")
|
||||
20.55
|
||||
"""
|
||||
return sum(subgraph_centrality(G).values())
|
||||
0
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/__init__.py
vendored
Normal file
0
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/__init__.py
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
780
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_betweenness_centrality.py
vendored
Normal file
780
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_betweenness_centrality.py
vendored
Normal file
@@ -0,0 +1,780 @@
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def weighted_G():
|
||||
G = nx.Graph()
|
||||
G.add_edge(0, 1, weight=3)
|
||||
G.add_edge(0, 2, weight=2)
|
||||
G.add_edge(0, 3, weight=6)
|
||||
G.add_edge(0, 4, weight=4)
|
||||
G.add_edge(1, 3, weight=5)
|
||||
G.add_edge(1, 5, weight=5)
|
||||
G.add_edge(2, 4, weight=1)
|
||||
G.add_edge(3, 4, weight=2)
|
||||
G.add_edge(3, 5, weight=1)
|
||||
G.add_edge(4, 5, weight=4)
|
||||
return G
|
||||
|
||||
|
||||
class TestBetweennessCentrality:
|
||||
def test_K5(self):
|
||||
"""Betweenness centrality: K5"""
|
||||
G = nx.complete_graph(5)
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=False)
|
||||
b_answer = {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_K5_endpoints(self):
|
||||
"""Betweenness centrality: K5 endpoints"""
|
||||
G = nx.complete_graph(5)
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=False, endpoints=True)
|
||||
b_answer = {0: 4.0, 1: 4.0, 2: 4.0, 3: 4.0, 4: 4.0}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
# normalized = True case
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=True, endpoints=True)
|
||||
b_answer = {0: 0.4, 1: 0.4, 2: 0.4, 3: 0.4, 4: 0.4}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P3_normalized(self):
|
||||
"""Betweenness centrality: P3 normalized"""
|
||||
G = nx.path_graph(3)
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=True)
|
||||
b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P3(self):
|
||||
"""Betweenness centrality: P3"""
|
||||
G = nx.path_graph(3)
|
||||
b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=False)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_sample_from_P3(self):
|
||||
"""Betweenness centrality: P3 sample"""
|
||||
G = nx.path_graph(3)
|
||||
b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
|
||||
b = nx.betweenness_centrality(G, k=3, weight=None, normalized=False, seed=1)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
b = nx.betweenness_centrality(G, k=2, weight=None, normalized=False, seed=1)
|
||||
# python versions give different results with same seed
|
||||
b_approx1 = {0: 0.0, 1: 1.5, 2: 0.0}
|
||||
b_approx2 = {0: 0.0, 1: 0.75, 2: 0.0}
|
||||
for n in sorted(G):
|
||||
assert b[n] in (b_approx1[n], b_approx2[n])
|
||||
|
||||
def test_P3_endpoints(self):
|
||||
"""Betweenness centrality: P3 endpoints"""
|
||||
G = nx.path_graph(3)
|
||||
b_answer = {0: 2.0, 1: 3.0, 2: 2.0}
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=False, endpoints=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
# normalized = True case
|
||||
b_answer = {0: 2 / 3, 1: 1.0, 2: 2 / 3}
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=True, endpoints=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_krackhardt_kite_graph(self):
|
||||
"""Betweenness centrality: Krackhardt kite graph"""
|
||||
G = nx.krackhardt_kite_graph()
|
||||
b_answer = {
|
||||
0: 1.667,
|
||||
1: 1.667,
|
||||
2: 0.000,
|
||||
3: 7.333,
|
||||
4: 0.000,
|
||||
5: 16.667,
|
||||
6: 16.667,
|
||||
7: 28.000,
|
||||
8: 16.000,
|
||||
9: 0.000,
|
||||
}
|
||||
for b in b_answer:
|
||||
b_answer[b] /= 2
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=False)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_krackhardt_kite_graph_normalized(self):
|
||||
"""Betweenness centrality: Krackhardt kite graph normalized"""
|
||||
G = nx.krackhardt_kite_graph()
|
||||
b_answer = {
|
||||
0: 0.023,
|
||||
1: 0.023,
|
||||
2: 0.000,
|
||||
3: 0.102,
|
||||
4: 0.000,
|
||||
5: 0.231,
|
||||
6: 0.231,
|
||||
7: 0.389,
|
||||
8: 0.222,
|
||||
9: 0.000,
|
||||
}
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_florentine_families_graph(self):
|
||||
"""Betweenness centrality: Florentine families graph"""
|
||||
G = nx.florentine_families_graph()
|
||||
b_answer = {
|
||||
"Acciaiuoli": 0.000,
|
||||
"Albizzi": 0.212,
|
||||
"Barbadori": 0.093,
|
||||
"Bischeri": 0.104,
|
||||
"Castellani": 0.055,
|
||||
"Ginori": 0.000,
|
||||
"Guadagni": 0.255,
|
||||
"Lamberteschi": 0.000,
|
||||
"Medici": 0.522,
|
||||
"Pazzi": 0.000,
|
||||
"Peruzzi": 0.022,
|
||||
"Ridolfi": 0.114,
|
||||
"Salviati": 0.143,
|
||||
"Strozzi": 0.103,
|
||||
"Tornabuoni": 0.092,
|
||||
}
|
||||
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_les_miserables_graph(self):
|
||||
"""Betweenness centrality: Les Miserables graph"""
|
||||
G = nx.les_miserables_graph()
|
||||
b_answer = {
|
||||
"Napoleon": 0.000,
|
||||
"Myriel": 0.177,
|
||||
"MlleBaptistine": 0.000,
|
||||
"MmeMagloire": 0.000,
|
||||
"CountessDeLo": 0.000,
|
||||
"Geborand": 0.000,
|
||||
"Champtercier": 0.000,
|
||||
"Cravatte": 0.000,
|
||||
"Count": 0.000,
|
||||
"OldMan": 0.000,
|
||||
"Valjean": 0.570,
|
||||
"Labarre": 0.000,
|
||||
"Marguerite": 0.000,
|
||||
"MmeDeR": 0.000,
|
||||
"Isabeau": 0.000,
|
||||
"Gervais": 0.000,
|
||||
"Listolier": 0.000,
|
||||
"Tholomyes": 0.041,
|
||||
"Fameuil": 0.000,
|
||||
"Blacheville": 0.000,
|
||||
"Favourite": 0.000,
|
||||
"Dahlia": 0.000,
|
||||
"Zephine": 0.000,
|
||||
"Fantine": 0.130,
|
||||
"MmeThenardier": 0.029,
|
||||
"Thenardier": 0.075,
|
||||
"Cosette": 0.024,
|
||||
"Javert": 0.054,
|
||||
"Fauchelevent": 0.026,
|
||||
"Bamatabois": 0.008,
|
||||
"Perpetue": 0.000,
|
||||
"Simplice": 0.009,
|
||||
"Scaufflaire": 0.000,
|
||||
"Woman1": 0.000,
|
||||
"Judge": 0.000,
|
||||
"Champmathieu": 0.000,
|
||||
"Brevet": 0.000,
|
||||
"Chenildieu": 0.000,
|
||||
"Cochepaille": 0.000,
|
||||
"Pontmercy": 0.007,
|
||||
"Boulatruelle": 0.000,
|
||||
"Eponine": 0.011,
|
||||
"Anzelma": 0.000,
|
||||
"Woman2": 0.000,
|
||||
"MotherInnocent": 0.000,
|
||||
"Gribier": 0.000,
|
||||
"MmeBurgon": 0.026,
|
||||
"Jondrette": 0.000,
|
||||
"Gavroche": 0.165,
|
||||
"Gillenormand": 0.020,
|
||||
"Magnon": 0.000,
|
||||
"MlleGillenormand": 0.048,
|
||||
"MmePontmercy": 0.000,
|
||||
"MlleVaubois": 0.000,
|
||||
"LtGillenormand": 0.000,
|
||||
"Marius": 0.132,
|
||||
"BaronessT": 0.000,
|
||||
"Mabeuf": 0.028,
|
||||
"Enjolras": 0.043,
|
||||
"Combeferre": 0.001,
|
||||
"Prouvaire": 0.000,
|
||||
"Feuilly": 0.001,
|
||||
"Courfeyrac": 0.005,
|
||||
"Bahorel": 0.002,
|
||||
"Bossuet": 0.031,
|
||||
"Joly": 0.002,
|
||||
"Grantaire": 0.000,
|
||||
"MotherPlutarch": 0.000,
|
||||
"Gueulemer": 0.005,
|
||||
"Babet": 0.005,
|
||||
"Claquesous": 0.005,
|
||||
"Montparnasse": 0.004,
|
||||
"Toussaint": 0.000,
|
||||
"Child1": 0.000,
|
||||
"Child2": 0.000,
|
||||
"Brujon": 0.000,
|
||||
"MmeHucheloup": 0.000,
|
||||
}
|
||||
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_ladder_graph(self):
|
||||
"""Betweenness centrality: Ladder graph"""
|
||||
G = nx.Graph() # ladder_graph(3)
|
||||
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (4, 5), (3, 5)])
|
||||
b_answer = {0: 1.667, 1: 1.667, 2: 6.667, 3: 6.667, 4: 1.667, 5: 1.667}
|
||||
for b in b_answer:
|
||||
b_answer[b] /= 2
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=False)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_disconnected_path(self):
|
||||
"""Betweenness centrality: disconnected path"""
|
||||
G = nx.Graph()
|
||||
nx.add_path(G, [0, 1, 2])
|
||||
nx.add_path(G, [3, 4, 5, 6])
|
||||
b_answer = {0: 0, 1: 1, 2: 0, 3: 0, 4: 2, 5: 2, 6: 0}
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=False)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_disconnected_path_endpoints(self):
|
||||
"""Betweenness centrality: disconnected path endpoints"""
|
||||
G = nx.Graph()
|
||||
nx.add_path(G, [0, 1, 2])
|
||||
nx.add_path(G, [3, 4, 5, 6])
|
||||
b_answer = {0: 2, 1: 3, 2: 2, 3: 3, 4: 5, 5: 5, 6: 3}
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=False, endpoints=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
# normalized = True case
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=True, endpoints=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n] / 21, abs=1e-7)
|
||||
|
||||
def test_directed_path(self):
|
||||
"""Betweenness centrality: directed path"""
|
||||
G = nx.DiGraph()
|
||||
nx.add_path(G, [0, 1, 2])
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=False)
|
||||
b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_directed_path_normalized(self):
|
||||
"""Betweenness centrality: directed path normalized"""
|
||||
G = nx.DiGraph()
|
||||
nx.add_path(G, [0, 1, 2])
|
||||
b = nx.betweenness_centrality(G, weight=None, normalized=True)
|
||||
b_answer = {0: 0.0, 1: 0.5, 2: 0.0}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
|
||||
class TestWeightedBetweennessCentrality:
|
||||
def test_K5(self):
|
||||
"""Weighted betweenness centrality: K5"""
|
||||
G = nx.complete_graph(5)
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=False)
|
||||
b_answer = {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P3_normalized(self):
|
||||
"""Weighted betweenness centrality: P3 normalized"""
|
||||
G = nx.path_graph(3)
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=True)
|
||||
b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P3(self):
|
||||
"""Weighted betweenness centrality: P3"""
|
||||
G = nx.path_graph(3)
|
||||
b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=False)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_krackhardt_kite_graph(self):
|
||||
"""Weighted betweenness centrality: Krackhardt kite graph"""
|
||||
G = nx.krackhardt_kite_graph()
|
||||
b_answer = {
|
||||
0: 1.667,
|
||||
1: 1.667,
|
||||
2: 0.000,
|
||||
3: 7.333,
|
||||
4: 0.000,
|
||||
5: 16.667,
|
||||
6: 16.667,
|
||||
7: 28.000,
|
||||
8: 16.000,
|
||||
9: 0.000,
|
||||
}
|
||||
for b in b_answer:
|
||||
b_answer[b] /= 2
|
||||
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=False)
|
||||
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_krackhardt_kite_graph_normalized(self):
|
||||
"""Weighted betweenness centrality:
|
||||
Krackhardt kite graph normalized
|
||||
"""
|
||||
G = nx.krackhardt_kite_graph()
|
||||
b_answer = {
|
||||
0: 0.023,
|
||||
1: 0.023,
|
||||
2: 0.000,
|
||||
3: 0.102,
|
||||
4: 0.000,
|
||||
5: 0.231,
|
||||
6: 0.231,
|
||||
7: 0.389,
|
||||
8: 0.222,
|
||||
9: 0.000,
|
||||
}
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=True)
|
||||
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_florentine_families_graph(self):
|
||||
"""Weighted betweenness centrality:
|
||||
Florentine families graph"""
|
||||
G = nx.florentine_families_graph()
|
||||
b_answer = {
|
||||
"Acciaiuoli": 0.000,
|
||||
"Albizzi": 0.212,
|
||||
"Barbadori": 0.093,
|
||||
"Bischeri": 0.104,
|
||||
"Castellani": 0.055,
|
||||
"Ginori": 0.000,
|
||||
"Guadagni": 0.255,
|
||||
"Lamberteschi": 0.000,
|
||||
"Medici": 0.522,
|
||||
"Pazzi": 0.000,
|
||||
"Peruzzi": 0.022,
|
||||
"Ridolfi": 0.114,
|
||||
"Salviati": 0.143,
|
||||
"Strozzi": 0.103,
|
||||
"Tornabuoni": 0.092,
|
||||
}
|
||||
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_les_miserables_graph(self):
|
||||
"""Weighted betweenness centrality: Les Miserables graph"""
|
||||
G = nx.les_miserables_graph()
|
||||
b_answer = {
|
||||
"Napoleon": 0.000,
|
||||
"Myriel": 0.177,
|
||||
"MlleBaptistine": 0.000,
|
||||
"MmeMagloire": 0.000,
|
||||
"CountessDeLo": 0.000,
|
||||
"Geborand": 0.000,
|
||||
"Champtercier": 0.000,
|
||||
"Cravatte": 0.000,
|
||||
"Count": 0.000,
|
||||
"OldMan": 0.000,
|
||||
"Valjean": 0.454,
|
||||
"Labarre": 0.000,
|
||||
"Marguerite": 0.009,
|
||||
"MmeDeR": 0.000,
|
||||
"Isabeau": 0.000,
|
||||
"Gervais": 0.000,
|
||||
"Listolier": 0.000,
|
||||
"Tholomyes": 0.066,
|
||||
"Fameuil": 0.000,
|
||||
"Blacheville": 0.000,
|
||||
"Favourite": 0.000,
|
||||
"Dahlia": 0.000,
|
||||
"Zephine": 0.000,
|
||||
"Fantine": 0.114,
|
||||
"MmeThenardier": 0.046,
|
||||
"Thenardier": 0.129,
|
||||
"Cosette": 0.075,
|
||||
"Javert": 0.193,
|
||||
"Fauchelevent": 0.026,
|
||||
"Bamatabois": 0.080,
|
||||
"Perpetue": 0.000,
|
||||
"Simplice": 0.001,
|
||||
"Scaufflaire": 0.000,
|
||||
"Woman1": 0.000,
|
||||
"Judge": 0.000,
|
||||
"Champmathieu": 0.000,
|
||||
"Brevet": 0.000,
|
||||
"Chenildieu": 0.000,
|
||||
"Cochepaille": 0.000,
|
||||
"Pontmercy": 0.023,
|
||||
"Boulatruelle": 0.000,
|
||||
"Eponine": 0.023,
|
||||
"Anzelma": 0.000,
|
||||
"Woman2": 0.000,
|
||||
"MotherInnocent": 0.000,
|
||||
"Gribier": 0.000,
|
||||
"MmeBurgon": 0.026,
|
||||
"Jondrette": 0.000,
|
||||
"Gavroche": 0.285,
|
||||
"Gillenormand": 0.024,
|
||||
"Magnon": 0.005,
|
||||
"MlleGillenormand": 0.036,
|
||||
"MmePontmercy": 0.005,
|
||||
"MlleVaubois": 0.000,
|
||||
"LtGillenormand": 0.015,
|
||||
"Marius": 0.072,
|
||||
"BaronessT": 0.004,
|
||||
"Mabeuf": 0.089,
|
||||
"Enjolras": 0.003,
|
||||
"Combeferre": 0.000,
|
||||
"Prouvaire": 0.000,
|
||||
"Feuilly": 0.004,
|
||||
"Courfeyrac": 0.001,
|
||||
"Bahorel": 0.007,
|
||||
"Bossuet": 0.028,
|
||||
"Joly": 0.000,
|
||||
"Grantaire": 0.036,
|
||||
"MotherPlutarch": 0.000,
|
||||
"Gueulemer": 0.025,
|
||||
"Babet": 0.015,
|
||||
"Claquesous": 0.042,
|
||||
"Montparnasse": 0.050,
|
||||
"Toussaint": 0.011,
|
||||
"Child1": 0.000,
|
||||
"Child2": 0.000,
|
||||
"Brujon": 0.002,
|
||||
"MmeHucheloup": 0.034,
|
||||
}
|
||||
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_ladder_graph(self):
|
||||
"""Weighted betweenness centrality: Ladder graph"""
|
||||
G = nx.Graph() # ladder_graph(3)
|
||||
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (4, 5), (3, 5)])
|
||||
b_answer = {0: 1.667, 1: 1.667, 2: 6.667, 3: 6.667, 4: 1.667, 5: 1.667}
|
||||
for b in b_answer:
|
||||
b_answer[b] /= 2
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=False)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_G(self):
|
||||
"""Weighted betweenness centrality: G"""
|
||||
G = weighted_G()
|
||||
b_answer = {0: 2.0, 1: 0.0, 2: 4.0, 3: 3.0, 4: 4.0, 5: 0.0}
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=False)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_G2(self):
|
||||
"""Weighted betweenness centrality: G2"""
|
||||
G = nx.DiGraph()
|
||||
G.add_weighted_edges_from(
|
||||
[
|
||||
("s", "u", 10),
|
||||
("s", "x", 5),
|
||||
("u", "v", 1),
|
||||
("u", "x", 2),
|
||||
("v", "y", 1),
|
||||
("x", "u", 3),
|
||||
("x", "v", 5),
|
||||
("x", "y", 2),
|
||||
("y", "s", 7),
|
||||
("y", "v", 6),
|
||||
]
|
||||
)
|
||||
|
||||
b_answer = {"y": 5.0, "x": 5.0, "s": 4.0, "u": 2.0, "v": 2.0}
|
||||
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=False)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_G3(self):
|
||||
"""Weighted betweenness centrality: G3"""
|
||||
G = nx.MultiGraph(weighted_G())
|
||||
es = list(G.edges(data=True))[::2] # duplicate every other edge
|
||||
G.add_edges_from(es)
|
||||
b_answer = {0: 2.0, 1: 0.0, 2: 4.0, 3: 3.0, 4: 4.0, 5: 0.0}
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=False)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_G4(self):
|
||||
"""Weighted betweenness centrality: G4"""
|
||||
G = nx.MultiDiGraph()
|
||||
G.add_weighted_edges_from(
|
||||
[
|
||||
("s", "u", 10),
|
||||
("s", "x", 5),
|
||||
("s", "x", 6),
|
||||
("u", "v", 1),
|
||||
("u", "x", 2),
|
||||
("v", "y", 1),
|
||||
("v", "y", 1),
|
||||
("x", "u", 3),
|
||||
("x", "v", 5),
|
||||
("x", "y", 2),
|
||||
("x", "y", 3),
|
||||
("y", "s", 7),
|
||||
("y", "v", 6),
|
||||
("y", "v", 6),
|
||||
]
|
||||
)
|
||||
|
||||
b_answer = {"y": 5.0, "x": 5.0, "s": 4.0, "u": 2.0, "v": 2.0}
|
||||
|
||||
b = nx.betweenness_centrality(G, weight="weight", normalized=False)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
|
||||
class TestEdgeBetweennessCentrality:
|
||||
def test_K5(self):
|
||||
"""Edge betweenness centrality: K5"""
|
||||
G = nx.complete_graph(5)
|
||||
b = nx.edge_betweenness_centrality(G, weight=None, normalized=False)
|
||||
b_answer = dict.fromkeys(G.edges(), 1)
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_normalized_K5(self):
|
||||
"""Edge betweenness centrality: K5"""
|
||||
G = nx.complete_graph(5)
|
||||
b = nx.edge_betweenness_centrality(G, weight=None, normalized=True)
|
||||
b_answer = dict.fromkeys(G.edges(), 1 / 10)
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_C4(self):
|
||||
"""Edge betweenness centrality: C4"""
|
||||
G = nx.cycle_graph(4)
|
||||
b = nx.edge_betweenness_centrality(G, weight=None, normalized=True)
|
||||
b_answer = {(0, 1): 2, (0, 3): 2, (1, 2): 2, (2, 3): 2}
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n] / 6, abs=1e-7)
|
||||
|
||||
def test_P4(self):
|
||||
"""Edge betweenness centrality: P4"""
|
||||
G = nx.path_graph(4)
|
||||
b = nx.edge_betweenness_centrality(G, weight=None, normalized=False)
|
||||
b_answer = {(0, 1): 3, (1, 2): 4, (2, 3): 3}
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_normalized_P4(self):
|
||||
"""Edge betweenness centrality: P4"""
|
||||
G = nx.path_graph(4)
|
||||
b = nx.edge_betweenness_centrality(G, weight=None, normalized=True)
|
||||
b_answer = {(0, 1): 3, (1, 2): 4, (2, 3): 3}
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n] / 6, abs=1e-7)
|
||||
|
||||
def test_balanced_tree(self):
|
||||
"""Edge betweenness centrality: balanced tree"""
|
||||
G = nx.balanced_tree(r=2, h=2)
|
||||
b = nx.edge_betweenness_centrality(G, weight=None, normalized=False)
|
||||
b_answer = {(0, 1): 12, (0, 2): 12, (1, 3): 6, (1, 4): 6, (2, 5): 6, (2, 6): 6}
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
|
||||
class TestWeightedEdgeBetweennessCentrality:
|
||||
def test_K5(self):
|
||||
"""Edge betweenness centrality: K5"""
|
||||
G = nx.complete_graph(5)
|
||||
b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
|
||||
b_answer = dict.fromkeys(G.edges(), 1)
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_C4(self):
|
||||
"""Edge betweenness centrality: C4"""
|
||||
G = nx.cycle_graph(4)
|
||||
b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
|
||||
b_answer = {(0, 1): 2, (0, 3): 2, (1, 2): 2, (2, 3): 2}
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P4(self):
|
||||
"""Edge betweenness centrality: P4"""
|
||||
G = nx.path_graph(4)
|
||||
b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
|
||||
b_answer = {(0, 1): 3, (1, 2): 4, (2, 3): 3}
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_balanced_tree(self):
|
||||
"""Edge betweenness centrality: balanced tree"""
|
||||
G = nx.balanced_tree(r=2, h=2)
|
||||
b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
|
||||
b_answer = {(0, 1): 12, (0, 2): 12, (1, 3): 6, (1, 4): 6, (2, 5): 6, (2, 6): 6}
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_weighted_graph(self):
|
||||
"""Edge betweenness centrality: weighted"""
|
||||
eList = [
|
||||
(0, 1, 5),
|
||||
(0, 2, 4),
|
||||
(0, 3, 3),
|
||||
(0, 4, 2),
|
||||
(1, 2, 4),
|
||||
(1, 3, 1),
|
||||
(1, 4, 3),
|
||||
(2, 4, 5),
|
||||
(3, 4, 4),
|
||||
]
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from(eList)
|
||||
b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
|
||||
b_answer = {
|
||||
(0, 1): 0.0,
|
||||
(0, 2): 1.0,
|
||||
(0, 3): 2.0,
|
||||
(0, 4): 1.0,
|
||||
(1, 2): 2.0,
|
||||
(1, 3): 3.5,
|
||||
(1, 4): 1.5,
|
||||
(2, 4): 1.0,
|
||||
(3, 4): 0.5,
|
||||
}
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_normalized_weighted_graph(self):
|
||||
"""Edge betweenness centrality: normalized weighted"""
|
||||
eList = [
|
||||
(0, 1, 5),
|
||||
(0, 2, 4),
|
||||
(0, 3, 3),
|
||||
(0, 4, 2),
|
||||
(1, 2, 4),
|
||||
(1, 3, 1),
|
||||
(1, 4, 3),
|
||||
(2, 4, 5),
|
||||
(3, 4, 4),
|
||||
]
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from(eList)
|
||||
b = nx.edge_betweenness_centrality(G, weight="weight", normalized=True)
|
||||
b_answer = {
|
||||
(0, 1): 0.0,
|
||||
(0, 2): 1.0,
|
||||
(0, 3): 2.0,
|
||||
(0, 4): 1.0,
|
||||
(1, 2): 2.0,
|
||||
(1, 3): 3.5,
|
||||
(1, 4): 1.5,
|
||||
(2, 4): 1.0,
|
||||
(3, 4): 0.5,
|
||||
}
|
||||
norm = len(G) * (len(G) - 1) / 2
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n] / norm, abs=1e-7)
|
||||
|
||||
def test_weighted_multigraph(self):
|
||||
"""Edge betweenness centrality: weighted multigraph"""
|
||||
eList = [
|
||||
(0, 1, 5),
|
||||
(0, 1, 4),
|
||||
(0, 2, 4),
|
||||
(0, 3, 3),
|
||||
(0, 3, 3),
|
||||
(0, 4, 2),
|
||||
(1, 2, 4),
|
||||
(1, 3, 1),
|
||||
(1, 3, 2),
|
||||
(1, 4, 3),
|
||||
(1, 4, 4),
|
||||
(2, 4, 5),
|
||||
(3, 4, 4),
|
||||
(3, 4, 4),
|
||||
]
|
||||
G = nx.MultiGraph()
|
||||
G.add_weighted_edges_from(eList)
|
||||
b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
|
||||
b_answer = {
|
||||
(0, 1, 0): 0.0,
|
||||
(0, 1, 1): 0.5,
|
||||
(0, 2, 0): 1.0,
|
||||
(0, 3, 0): 0.75,
|
||||
(0, 3, 1): 0.75,
|
||||
(0, 4, 0): 1.0,
|
||||
(1, 2, 0): 2.0,
|
||||
(1, 3, 0): 3.0,
|
||||
(1, 3, 1): 0.0,
|
||||
(1, 4, 0): 1.5,
|
||||
(1, 4, 1): 0.0,
|
||||
(2, 4, 0): 1.0,
|
||||
(3, 4, 0): 0.25,
|
||||
(3, 4, 1): 0.25,
|
||||
}
|
||||
for n in sorted(G.edges(keys=True)):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_normalized_weighted_multigraph(self):
|
||||
"""Edge betweenness centrality: normalized weighted multigraph"""
|
||||
eList = [
|
||||
(0, 1, 5),
|
||||
(0, 1, 4),
|
||||
(0, 2, 4),
|
||||
(0, 3, 3),
|
||||
(0, 3, 3),
|
||||
(0, 4, 2),
|
||||
(1, 2, 4),
|
||||
(1, 3, 1),
|
||||
(1, 3, 2),
|
||||
(1, 4, 3),
|
||||
(1, 4, 4),
|
||||
(2, 4, 5),
|
||||
(3, 4, 4),
|
||||
(3, 4, 4),
|
||||
]
|
||||
G = nx.MultiGraph()
|
||||
G.add_weighted_edges_from(eList)
|
||||
b = nx.edge_betweenness_centrality(G, weight="weight", normalized=True)
|
||||
b_answer = {
|
||||
(0, 1, 0): 0.0,
|
||||
(0, 1, 1): 0.5,
|
||||
(0, 2, 0): 1.0,
|
||||
(0, 3, 0): 0.75,
|
||||
(0, 3, 1): 0.75,
|
||||
(0, 4, 0): 1.0,
|
||||
(1, 2, 0): 2.0,
|
||||
(1, 3, 0): 3.0,
|
||||
(1, 3, 1): 0.0,
|
||||
(1, 4, 0): 1.5,
|
||||
(1, 4, 1): 0.0,
|
||||
(2, 4, 0): 1.0,
|
||||
(3, 4, 0): 0.25,
|
||||
(3, 4, 1): 0.25,
|
||||
}
|
||||
norm = len(G) * (len(G) - 1) / 2
|
||||
for n in sorted(G.edges(keys=True)):
|
||||
assert b[n] == pytest.approx(b_answer[n] / norm, abs=1e-7)
|
||||
@@ -0,0 +1,340 @@
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestSubsetBetweennessCentrality:
|
||||
def test_K5(self):
|
||||
"""Betweenness Centrality Subset: K5"""
|
||||
G = nx.complete_graph(5)
|
||||
b = nx.betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[1, 3], weight=None
|
||||
)
|
||||
b_answer = {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P5_directed(self):
|
||||
"""Betweenness Centrality Subset: P5 directed"""
|
||||
G = nx.DiGraph()
|
||||
nx.add_path(G, range(5))
|
||||
b_answer = {0: 0, 1: 1, 2: 1, 3: 0, 4: 0, 5: 0}
|
||||
b = nx.betweenness_centrality_subset(G, sources=[0], targets=[3], weight=None)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P5(self):
|
||||
"""Betweenness Centrality Subset: P5"""
|
||||
G = nx.Graph()
|
||||
nx.add_path(G, range(5))
|
||||
b_answer = {0: 0, 1: 0.5, 2: 0.5, 3: 0, 4: 0, 5: 0}
|
||||
b = nx.betweenness_centrality_subset(G, sources=[0], targets=[3], weight=None)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P5_multiple_target(self):
|
||||
"""Betweenness Centrality Subset: P5 multiple target"""
|
||||
G = nx.Graph()
|
||||
nx.add_path(G, range(5))
|
||||
b_answer = {0: 0, 1: 1, 2: 1, 3: 0.5, 4: 0, 5: 0}
|
||||
b = nx.betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[3, 4], weight=None
|
||||
)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_box(self):
|
||||
"""Betweenness Centrality Subset: box"""
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
|
||||
b_answer = {0: 0, 1: 0.25, 2: 0.25, 3: 0}
|
||||
b = nx.betweenness_centrality_subset(G, sources=[0], targets=[3], weight=None)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_box_and_path(self):
|
||||
"""Betweenness Centrality Subset: box and path"""
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (4, 5)])
|
||||
b_answer = {0: 0, 1: 0.5, 2: 0.5, 3: 0.5, 4: 0, 5: 0}
|
||||
b = nx.betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[3, 4], weight=None
|
||||
)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_box_and_path2(self):
|
||||
"""Betweenness Centrality Subset: box and path multiple target"""
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(0, 1), (1, 2), (2, 3), (1, 20), (20, 3), (3, 4)])
|
||||
b_answer = {0: 0, 1: 1.0, 2: 0.5, 20: 0.5, 3: 0.5, 4: 0}
|
||||
b = nx.betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[3, 4], weight=None
|
||||
)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_diamond_multi_path(self):
|
||||
"""Betweenness Centrality Subset: Diamond Multi Path"""
|
||||
G = nx.Graph()
|
||||
G.add_edges_from(
|
||||
[
|
||||
(1, 2),
|
||||
(1, 3),
|
||||
(1, 4),
|
||||
(1, 5),
|
||||
(1, 10),
|
||||
(10, 11),
|
||||
(11, 12),
|
||||
(12, 9),
|
||||
(2, 6),
|
||||
(3, 6),
|
||||
(4, 6),
|
||||
(5, 7),
|
||||
(7, 8),
|
||||
(6, 8),
|
||||
(8, 9),
|
||||
]
|
||||
)
|
||||
b = nx.betweenness_centrality_subset(G, sources=[1], targets=[9], weight=None)
|
||||
|
||||
expected_b = {
|
||||
1: 0,
|
||||
2: 1.0 / 10,
|
||||
3: 1.0 / 10,
|
||||
4: 1.0 / 10,
|
||||
5: 1.0 / 10,
|
||||
6: 3.0 / 10,
|
||||
7: 1.0 / 10,
|
||||
8: 4.0 / 10,
|
||||
9: 0,
|
||||
10: 1.0 / 10,
|
||||
11: 1.0 / 10,
|
||||
12: 1.0 / 10,
|
||||
}
|
||||
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(expected_b[n], abs=1e-7)
|
||||
|
||||
def test_normalized_p2(self):
|
||||
"""
|
||||
Betweenness Centrality Subset: Normalized P2
|
||||
if n <= 2: no normalization, betweenness centrality should be 0 for all nodes.
|
||||
"""
|
||||
G = nx.Graph()
|
||||
nx.add_path(G, range(2))
|
||||
b_answer = {0: 0, 1: 0.0}
|
||||
b = nx.betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[1], normalized=True, weight=None
|
||||
)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_normalized_P5_directed(self):
|
||||
"""Betweenness Centrality Subset: Normalized Directed P5"""
|
||||
G = nx.DiGraph()
|
||||
nx.add_path(G, range(5))
|
||||
b_answer = {0: 0, 1: 1.0 / 12.0, 2: 1.0 / 12.0, 3: 0, 4: 0, 5: 0}
|
||||
b = nx.betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[3], normalized=True, weight=None
|
||||
)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_weighted_graph(self):
|
||||
"""Betweenness Centrality Subset: Weighted Graph"""
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(0, 1, weight=3)
|
||||
G.add_edge(0, 2, weight=2)
|
||||
G.add_edge(0, 3, weight=6)
|
||||
G.add_edge(0, 4, weight=4)
|
||||
G.add_edge(1, 3, weight=5)
|
||||
G.add_edge(1, 5, weight=5)
|
||||
G.add_edge(2, 4, weight=1)
|
||||
G.add_edge(3, 4, weight=2)
|
||||
G.add_edge(3, 5, weight=1)
|
||||
G.add_edge(4, 5, weight=4)
|
||||
b_answer = {0: 0.0, 1: 0.0, 2: 0.5, 3: 0.5, 4: 0.5, 5: 0.0}
|
||||
b = nx.betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[5], normalized=False, weight="weight"
|
||||
)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
|
||||
class TestEdgeSubsetBetweennessCentrality:
|
||||
def test_K5(self):
|
||||
"""Edge betweenness subset centrality: K5"""
|
||||
G = nx.complete_graph(5)
|
||||
b = nx.edge_betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[1, 3], weight=None
|
||||
)
|
||||
b_answer = dict.fromkeys(G.edges(), 0)
|
||||
b_answer[(0, 3)] = b_answer[(0, 1)] = 0.5
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P5_directed(self):
|
||||
"""Edge betweenness subset centrality: P5 directed"""
|
||||
G = nx.DiGraph()
|
||||
nx.add_path(G, range(5))
|
||||
b_answer = dict.fromkeys(G.edges(), 0)
|
||||
b_answer[(0, 1)] = b_answer[(1, 2)] = b_answer[(2, 3)] = 1
|
||||
b = nx.edge_betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[3], weight=None
|
||||
)
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P5(self):
|
||||
"""Edge betweenness subset centrality: P5"""
|
||||
G = nx.Graph()
|
||||
nx.add_path(G, range(5))
|
||||
b_answer = dict.fromkeys(G.edges(), 0)
|
||||
b_answer[(0, 1)] = b_answer[(1, 2)] = b_answer[(2, 3)] = 0.5
|
||||
b = nx.edge_betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[3], weight=None
|
||||
)
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P5_multiple_target(self):
|
||||
"""Edge betweenness subset centrality: P5 multiple target"""
|
||||
G = nx.Graph()
|
||||
nx.add_path(G, range(5))
|
||||
b_answer = dict.fromkeys(G.edges(), 0)
|
||||
b_answer[(0, 1)] = b_answer[(1, 2)] = b_answer[(2, 3)] = 1
|
||||
b_answer[(3, 4)] = 0.5
|
||||
b = nx.edge_betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[3, 4], weight=None
|
||||
)
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_box(self):
|
||||
"""Edge betweenness subset centrality: box"""
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
|
||||
b_answer = dict.fromkeys(G.edges(), 0)
|
||||
b_answer[(0, 1)] = b_answer[(0, 2)] = 0.25
|
||||
b_answer[(1, 3)] = b_answer[(2, 3)] = 0.25
|
||||
b = nx.edge_betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[3], weight=None
|
||||
)
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_box_and_path(self):
|
||||
"""Edge betweenness subset centrality: box and path"""
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (4, 5)])
|
||||
b_answer = dict.fromkeys(G.edges(), 0)
|
||||
b_answer[(0, 1)] = b_answer[(0, 2)] = 0.5
|
||||
b_answer[(1, 3)] = b_answer[(2, 3)] = 0.5
|
||||
b_answer[(3, 4)] = 0.5
|
||||
b = nx.edge_betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[3, 4], weight=None
|
||||
)
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_box_and_path2(self):
|
||||
"""Edge betweenness subset centrality: box and path multiple target"""
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(0, 1), (1, 2), (2, 3), (1, 20), (20, 3), (3, 4)])
|
||||
b_answer = dict.fromkeys(G.edges(), 0)
|
||||
b_answer[(0, 1)] = 1.0
|
||||
b_answer[(1, 20)] = b_answer[(3, 20)] = 0.5
|
||||
b_answer[(1, 2)] = b_answer[(2, 3)] = 0.5
|
||||
b_answer[(3, 4)] = 0.5
|
||||
b = nx.edge_betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[3, 4], weight=None
|
||||
)
|
||||
for n in sorted(G.edges()):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_diamond_multi_path(self):
|
||||
"""Edge betweenness subset centrality: Diamond Multi Path"""
|
||||
G = nx.Graph()
|
||||
G.add_edges_from(
|
||||
[
|
||||
(1, 2),
|
||||
(1, 3),
|
||||
(1, 4),
|
||||
(1, 5),
|
||||
(1, 10),
|
||||
(10, 11),
|
||||
(11, 12),
|
||||
(12, 9),
|
||||
(2, 6),
|
||||
(3, 6),
|
||||
(4, 6),
|
||||
(5, 7),
|
||||
(7, 8),
|
||||
(6, 8),
|
||||
(8, 9),
|
||||
]
|
||||
)
|
||||
b_answer = dict.fromkeys(G.edges(), 0)
|
||||
b_answer[(8, 9)] = 0.4
|
||||
b_answer[(6, 8)] = b_answer[(7, 8)] = 0.2
|
||||
b_answer[(2, 6)] = b_answer[(3, 6)] = b_answer[(4, 6)] = 0.2 / 3.0
|
||||
b_answer[(1, 2)] = b_answer[(1, 3)] = b_answer[(1, 4)] = 0.2 / 3.0
|
||||
b_answer[(5, 7)] = 0.2
|
||||
b_answer[(1, 5)] = 0.2
|
||||
b_answer[(9, 12)] = 0.1
|
||||
b_answer[(11, 12)] = b_answer[(10, 11)] = b_answer[(1, 10)] = 0.1
|
||||
b = nx.edge_betweenness_centrality_subset(
|
||||
G, sources=[1], targets=[9], weight=None
|
||||
)
|
||||
for n in G.edges():
|
||||
sort_n = tuple(sorted(n))
|
||||
assert b[n] == pytest.approx(b_answer[sort_n], abs=1e-7)
|
||||
|
||||
def test_normalized_p1(self):
|
||||
"""
|
||||
Edge betweenness subset centrality: P1
|
||||
if n <= 1: no normalization b=0 for all nodes
|
||||
"""
|
||||
G = nx.Graph()
|
||||
nx.add_path(G, range(1))
|
||||
b_answer = dict.fromkeys(G.edges(), 0)
|
||||
b = nx.edge_betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[0], normalized=True, weight=None
|
||||
)
|
||||
for n in G.edges():
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_normalized_P5_directed(self):
|
||||
"""Edge betweenness subset centrality: Normalized Directed P5"""
|
||||
G = nx.DiGraph()
|
||||
nx.add_path(G, range(5))
|
||||
b_answer = dict.fromkeys(G.edges(), 0)
|
||||
b_answer[(0, 1)] = b_answer[(1, 2)] = b_answer[(2, 3)] = 0.05
|
||||
b = nx.edge_betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[3], normalized=True, weight=None
|
||||
)
|
||||
for n in G.edges():
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_weighted_graph(self):
|
||||
"""Edge betweenness subset centrality: Weighted Graph"""
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(0, 1, weight=3)
|
||||
G.add_edge(0, 2, weight=2)
|
||||
G.add_edge(0, 3, weight=6)
|
||||
G.add_edge(0, 4, weight=4)
|
||||
G.add_edge(1, 3, weight=5)
|
||||
G.add_edge(1, 5, weight=5)
|
||||
G.add_edge(2, 4, weight=1)
|
||||
G.add_edge(3, 4, weight=2)
|
||||
G.add_edge(3, 5, weight=1)
|
||||
G.add_edge(4, 5, weight=4)
|
||||
b_answer = dict.fromkeys(G.edges(), 0)
|
||||
b_answer[(0, 2)] = b_answer[(2, 4)] = b_answer[(4, 5)] = 0.5
|
||||
b_answer[(0, 3)] = b_answer[(3, 5)] = 0.5
|
||||
b = nx.edge_betweenness_centrality_subset(
|
||||
G, sources=[0], targets=[5], normalized=False, weight="weight"
|
||||
)
|
||||
for n in G.edges():
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
306
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_closeness_centrality.py
vendored
Normal file
306
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_closeness_centrality.py
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
"""
|
||||
Tests for closeness centrality.
|
||||
"""
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestClosenessCentrality:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.K = nx.krackhardt_kite_graph()
|
||||
cls.P3 = nx.path_graph(3)
|
||||
cls.P4 = nx.path_graph(4)
|
||||
cls.K5 = nx.complete_graph(5)
|
||||
|
||||
cls.C4 = nx.cycle_graph(4)
|
||||
cls.T = nx.balanced_tree(r=2, h=2)
|
||||
cls.Gb = nx.Graph()
|
||||
cls.Gb.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (4, 5), (3, 5)])
|
||||
|
||||
F = nx.florentine_families_graph()
|
||||
cls.F = F
|
||||
|
||||
cls.LM = nx.les_miserables_graph()
|
||||
|
||||
# Create random undirected, unweighted graph for testing incremental version
|
||||
cls.undirected_G = nx.fast_gnp_random_graph(n=100, p=0.6, seed=123)
|
||||
cls.undirected_G_cc = nx.closeness_centrality(cls.undirected_G)
|
||||
|
||||
def test_wf_improved(self):
|
||||
G = nx.union(self.P4, nx.path_graph([4, 5, 6]))
|
||||
c = nx.closeness_centrality(G)
|
||||
cwf = nx.closeness_centrality(G, wf_improved=False)
|
||||
res = {0: 0.25, 1: 0.375, 2: 0.375, 3: 0.25, 4: 0.222, 5: 0.333, 6: 0.222}
|
||||
wf_res = {0: 0.5, 1: 0.75, 2: 0.75, 3: 0.5, 4: 0.667, 5: 1.0, 6: 0.667}
|
||||
for n in G:
|
||||
assert c[n] == pytest.approx(res[n], abs=1e-3)
|
||||
assert cwf[n] == pytest.approx(wf_res[n], abs=1e-3)
|
||||
|
||||
def test_digraph(self):
|
||||
G = nx.path_graph(3, create_using=nx.DiGraph())
|
||||
c = nx.closeness_centrality(G)
|
||||
cr = nx.closeness_centrality(G.reverse())
|
||||
d = {0: 0.0, 1: 0.500, 2: 0.667}
|
||||
dr = {0: 0.667, 1: 0.500, 2: 0.0}
|
||||
for n in sorted(self.P3):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
assert cr[n] == pytest.approx(dr[n], abs=1e-3)
|
||||
|
||||
def test_k5_closeness(self):
|
||||
c = nx.closeness_centrality(self.K5)
|
||||
d = {0: 1.000, 1: 1.000, 2: 1.000, 3: 1.000, 4: 1.000}
|
||||
for n in sorted(self.K5):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_p3_closeness(self):
|
||||
c = nx.closeness_centrality(self.P3)
|
||||
d = {0: 0.667, 1: 1.000, 2: 0.667}
|
||||
for n in sorted(self.P3):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_krackhardt_closeness(self):
|
||||
c = nx.closeness_centrality(self.K)
|
||||
d = {
|
||||
0: 0.529,
|
||||
1: 0.529,
|
||||
2: 0.500,
|
||||
3: 0.600,
|
||||
4: 0.500,
|
||||
5: 0.643,
|
||||
6: 0.643,
|
||||
7: 0.600,
|
||||
8: 0.429,
|
||||
9: 0.310,
|
||||
}
|
||||
for n in sorted(self.K):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_florentine_families_closeness(self):
|
||||
c = nx.closeness_centrality(self.F)
|
||||
d = {
|
||||
"Acciaiuoli": 0.368,
|
||||
"Albizzi": 0.483,
|
||||
"Barbadori": 0.4375,
|
||||
"Bischeri": 0.400,
|
||||
"Castellani": 0.389,
|
||||
"Ginori": 0.333,
|
||||
"Guadagni": 0.467,
|
||||
"Lamberteschi": 0.326,
|
||||
"Medici": 0.560,
|
||||
"Pazzi": 0.286,
|
||||
"Peruzzi": 0.368,
|
||||
"Ridolfi": 0.500,
|
||||
"Salviati": 0.389,
|
||||
"Strozzi": 0.4375,
|
||||
"Tornabuoni": 0.483,
|
||||
}
|
||||
for n in sorted(self.F):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_les_miserables_closeness(self):
|
||||
c = nx.closeness_centrality(self.LM)
|
||||
d = {
|
||||
"Napoleon": 0.302,
|
||||
"Myriel": 0.429,
|
||||
"MlleBaptistine": 0.413,
|
||||
"MmeMagloire": 0.413,
|
||||
"CountessDeLo": 0.302,
|
||||
"Geborand": 0.302,
|
||||
"Champtercier": 0.302,
|
||||
"Cravatte": 0.302,
|
||||
"Count": 0.302,
|
||||
"OldMan": 0.302,
|
||||
"Valjean": 0.644,
|
||||
"Labarre": 0.394,
|
||||
"Marguerite": 0.413,
|
||||
"MmeDeR": 0.394,
|
||||
"Isabeau": 0.394,
|
||||
"Gervais": 0.394,
|
||||
"Listolier": 0.341,
|
||||
"Tholomyes": 0.392,
|
||||
"Fameuil": 0.341,
|
||||
"Blacheville": 0.341,
|
||||
"Favourite": 0.341,
|
||||
"Dahlia": 0.341,
|
||||
"Zephine": 0.341,
|
||||
"Fantine": 0.461,
|
||||
"MmeThenardier": 0.461,
|
||||
"Thenardier": 0.517,
|
||||
"Cosette": 0.478,
|
||||
"Javert": 0.517,
|
||||
"Fauchelevent": 0.402,
|
||||
"Bamatabois": 0.427,
|
||||
"Perpetue": 0.318,
|
||||
"Simplice": 0.418,
|
||||
"Scaufflaire": 0.394,
|
||||
"Woman1": 0.396,
|
||||
"Judge": 0.404,
|
||||
"Champmathieu": 0.404,
|
||||
"Brevet": 0.404,
|
||||
"Chenildieu": 0.404,
|
||||
"Cochepaille": 0.404,
|
||||
"Pontmercy": 0.373,
|
||||
"Boulatruelle": 0.342,
|
||||
"Eponine": 0.396,
|
||||
"Anzelma": 0.352,
|
||||
"Woman2": 0.402,
|
||||
"MotherInnocent": 0.398,
|
||||
"Gribier": 0.288,
|
||||
"MmeBurgon": 0.344,
|
||||
"Jondrette": 0.257,
|
||||
"Gavroche": 0.514,
|
||||
"Gillenormand": 0.442,
|
||||
"Magnon": 0.335,
|
||||
"MlleGillenormand": 0.442,
|
||||
"MmePontmercy": 0.315,
|
||||
"MlleVaubois": 0.308,
|
||||
"LtGillenormand": 0.365,
|
||||
"Marius": 0.531,
|
||||
"BaronessT": 0.352,
|
||||
"Mabeuf": 0.396,
|
||||
"Enjolras": 0.481,
|
||||
"Combeferre": 0.392,
|
||||
"Prouvaire": 0.357,
|
||||
"Feuilly": 0.392,
|
||||
"Courfeyrac": 0.400,
|
||||
"Bahorel": 0.394,
|
||||
"Bossuet": 0.475,
|
||||
"Joly": 0.394,
|
||||
"Grantaire": 0.358,
|
||||
"MotherPlutarch": 0.285,
|
||||
"Gueulemer": 0.463,
|
||||
"Babet": 0.463,
|
||||
"Claquesous": 0.452,
|
||||
"Montparnasse": 0.458,
|
||||
"Toussaint": 0.402,
|
||||
"Child1": 0.342,
|
||||
"Child2": 0.342,
|
||||
"Brujon": 0.380,
|
||||
"MmeHucheloup": 0.353,
|
||||
}
|
||||
for n in sorted(self.LM):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_weighted_closeness(self):
|
||||
edges = [
|
||||
("s", "u", 10),
|
||||
("s", "x", 5),
|
||||
("u", "v", 1),
|
||||
("u", "x", 2),
|
||||
("v", "y", 1),
|
||||
("x", "u", 3),
|
||||
("x", "v", 5),
|
||||
("x", "y", 2),
|
||||
("y", "s", 7),
|
||||
("y", "v", 6),
|
||||
]
|
||||
XG = nx.Graph()
|
||||
XG.add_weighted_edges_from(edges)
|
||||
c = nx.closeness_centrality(XG, distance="weight")
|
||||
d = {"y": 0.200, "x": 0.286, "s": 0.138, "u": 0.235, "v": 0.200}
|
||||
for n in sorted(XG):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
#
|
||||
# Tests for incremental closeness centrality.
|
||||
#
|
||||
@staticmethod
|
||||
def pick_add_edge(g):
|
||||
u = nx.utils.arbitrary_element(g)
|
||||
possible_nodes = set(g.nodes())
|
||||
neighbors = list(g.neighbors(u)) + [u]
|
||||
possible_nodes.difference_update(neighbors)
|
||||
v = nx.utils.arbitrary_element(possible_nodes)
|
||||
return (u, v)
|
||||
|
||||
@staticmethod
|
||||
def pick_remove_edge(g):
|
||||
u = nx.utils.arbitrary_element(g)
|
||||
possible_nodes = list(g.neighbors(u))
|
||||
v = nx.utils.arbitrary_element(possible_nodes)
|
||||
return (u, v)
|
||||
|
||||
def test_directed_raises(self):
|
||||
with pytest.raises(nx.NetworkXNotImplemented):
|
||||
dir_G = nx.gn_graph(n=5)
|
||||
prev_cc = None
|
||||
edge = self.pick_add_edge(dir_G)
|
||||
insert = True
|
||||
nx.incremental_closeness_centrality(dir_G, edge, prev_cc, insert)
|
||||
|
||||
def test_wrong_size_prev_cc_raises(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = self.undirected_G.copy()
|
||||
edge = self.pick_add_edge(G)
|
||||
insert = True
|
||||
prev_cc = self.undirected_G_cc.copy()
|
||||
prev_cc.pop(0)
|
||||
nx.incremental_closeness_centrality(G, edge, prev_cc, insert)
|
||||
|
||||
def test_wrong_nodes_prev_cc_raises(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = self.undirected_G.copy()
|
||||
edge = self.pick_add_edge(G)
|
||||
insert = True
|
||||
prev_cc = self.undirected_G_cc.copy()
|
||||
num_nodes = len(prev_cc)
|
||||
prev_cc.pop(0)
|
||||
prev_cc[num_nodes] = 0.5
|
||||
nx.incremental_closeness_centrality(G, edge, prev_cc, insert)
|
||||
|
||||
def test_zero_centrality(self):
|
||||
G = nx.path_graph(3)
|
||||
prev_cc = nx.closeness_centrality(G)
|
||||
edge = self.pick_remove_edge(G)
|
||||
test_cc = nx.incremental_closeness_centrality(G, edge, prev_cc, insertion=False)
|
||||
G.remove_edges_from([edge])
|
||||
real_cc = nx.closeness_centrality(G)
|
||||
shared_items = set(test_cc.items()) & set(real_cc.items())
|
||||
assert len(shared_items) == len(real_cc)
|
||||
assert 0 in test_cc.values()
|
||||
|
||||
def test_incremental(self):
|
||||
# Check that incremental and regular give same output
|
||||
G = self.undirected_G.copy()
|
||||
prev_cc = None
|
||||
for i in range(5):
|
||||
if i % 2 == 0:
|
||||
# Remove an edge
|
||||
insert = False
|
||||
edge = self.pick_remove_edge(G)
|
||||
else:
|
||||
# Add an edge
|
||||
insert = True
|
||||
edge = self.pick_add_edge(G)
|
||||
|
||||
# start = timeit.default_timer()
|
||||
test_cc = nx.incremental_closeness_centrality(G, edge, prev_cc, insert)
|
||||
# inc_elapsed = (timeit.default_timer() - start)
|
||||
# print(f"incremental time: {inc_elapsed}")
|
||||
|
||||
if insert:
|
||||
G.add_edges_from([edge])
|
||||
else:
|
||||
G.remove_edges_from([edge])
|
||||
|
||||
# start = timeit.default_timer()
|
||||
real_cc = nx.closeness_centrality(G)
|
||||
# reg_elapsed = (timeit.default_timer() - start)
|
||||
# print(f"regular time: {reg_elapsed}")
|
||||
# Example output:
|
||||
# incremental time: 0.208
|
||||
# regular time: 0.276
|
||||
# incremental time: 0.00683
|
||||
# regular time: 0.260
|
||||
# incremental time: 0.0224
|
||||
# regular time: 0.278
|
||||
# incremental time: 0.00804
|
||||
# regular time: 0.208
|
||||
# incremental time: 0.00947
|
||||
# regular time: 0.188
|
||||
|
||||
assert set(test_cc.items()) == set(real_cc.items())
|
||||
|
||||
prev_cc = test_cc
|
||||
@@ -0,0 +1,197 @@
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx import approximate_current_flow_betweenness_centrality as approximate_cfbc
|
||||
from networkx import edge_current_flow_betweenness_centrality as edge_current_flow
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
|
||||
class TestFlowBetweennessCentrality:
|
||||
def test_K4_normalized(self):
|
||||
"""Betweenness centrality: K4"""
|
||||
G = nx.complete_graph(4)
|
||||
b = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
b_answer = {0: 0.25, 1: 0.25, 2: 0.25, 3: 0.25}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
G.add_edge(0, 1, weight=0.5, other=0.3)
|
||||
b = nx.current_flow_betweenness_centrality(G, normalized=True, weight=None)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
wb_answer = {0: 0.2222222, 1: 0.2222222, 2: 0.30555555, 3: 0.30555555}
|
||||
b = nx.current_flow_betweenness_centrality(G, normalized=True, weight="weight")
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(wb_answer[n], abs=1e-7)
|
||||
wb_answer = {0: 0.2051282, 1: 0.2051282, 2: 0.33974358, 3: 0.33974358}
|
||||
b = nx.current_flow_betweenness_centrality(G, normalized=True, weight="other")
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(wb_answer[n], abs=1e-7)
|
||||
|
||||
def test_K4(self):
|
||||
"""Betweenness centrality: K4"""
|
||||
G = nx.complete_graph(4)
|
||||
for solver in ["full", "lu", "cg"]:
|
||||
b = nx.current_flow_betweenness_centrality(
|
||||
G, normalized=False, solver=solver
|
||||
)
|
||||
b_answer = {0: 0.75, 1: 0.75, 2: 0.75, 3: 0.75}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P4_normalized(self):
|
||||
"""Betweenness centrality: P4 normalized"""
|
||||
G = nx.path_graph(4)
|
||||
b = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
b_answer = {0: 0, 1: 2.0 / 3, 2: 2.0 / 3, 3: 0}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P4(self):
|
||||
"""Betweenness centrality: P4"""
|
||||
G = nx.path_graph(4)
|
||||
b = nx.current_flow_betweenness_centrality(G, normalized=False)
|
||||
b_answer = {0: 0, 1: 2, 2: 2, 3: 0}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_star(self):
|
||||
"""Betweenness centrality: star"""
|
||||
G = nx.Graph()
|
||||
nx.add_star(G, ["a", "b", "c", "d"])
|
||||
b = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
b_answer = {"a": 1.0, "b": 0.0, "c": 0.0, "d": 0.0}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_solvers2(self):
|
||||
"""Betweenness centrality: alternate solvers"""
|
||||
G = nx.complete_graph(4)
|
||||
for solver in ["full", "lu", "cg"]:
|
||||
b = nx.current_flow_betweenness_centrality(
|
||||
G, normalized=False, solver=solver
|
||||
)
|
||||
b_answer = {0: 0.75, 1: 0.75, 2: 0.75, 3: 0.75}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
|
||||
class TestApproximateFlowBetweennessCentrality:
|
||||
def test_K4_normalized(self):
|
||||
"Approximate current-flow betweenness centrality: K4 normalized"
|
||||
G = nx.complete_graph(4)
|
||||
b = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
epsilon = 0.1
|
||||
ba = approximate_cfbc(G, normalized=True, epsilon=0.5 * epsilon)
|
||||
for n in sorted(G):
|
||||
np.testing.assert_allclose(b[n], ba[n], atol=epsilon)
|
||||
|
||||
def test_K4(self):
|
||||
"Approximate current-flow betweenness centrality: K4"
|
||||
G = nx.complete_graph(4)
|
||||
b = nx.current_flow_betweenness_centrality(G, normalized=False)
|
||||
epsilon = 0.1
|
||||
ba = approximate_cfbc(G, normalized=False, epsilon=0.5 * epsilon)
|
||||
for n in sorted(G):
|
||||
np.testing.assert_allclose(b[n], ba[n], atol=epsilon * len(G) ** 2)
|
||||
|
||||
def test_star(self):
|
||||
"Approximate current-flow betweenness centrality: star"
|
||||
G = nx.Graph()
|
||||
nx.add_star(G, ["a", "b", "c", "d"])
|
||||
b = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
epsilon = 0.1
|
||||
ba = approximate_cfbc(G, normalized=True, epsilon=0.5 * epsilon)
|
||||
for n in sorted(G):
|
||||
np.testing.assert_allclose(b[n], ba[n], atol=epsilon)
|
||||
|
||||
def test_grid(self):
|
||||
"Approximate current-flow betweenness centrality: 2d grid"
|
||||
G = nx.grid_2d_graph(4, 4)
|
||||
b = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
epsilon = 0.1
|
||||
ba = approximate_cfbc(G, normalized=True, epsilon=0.5 * epsilon)
|
||||
for n in sorted(G):
|
||||
np.testing.assert_allclose(b[n], ba[n], atol=epsilon)
|
||||
|
||||
def test_seed(self):
|
||||
G = nx.complete_graph(4)
|
||||
b = approximate_cfbc(G, normalized=False, epsilon=0.05, seed=1)
|
||||
b_answer = {0: 0.75, 1: 0.75, 2: 0.75, 3: 0.75}
|
||||
for n in sorted(G):
|
||||
np.testing.assert_allclose(b[n], b_answer[n], atol=0.1)
|
||||
|
||||
def test_solvers(self):
|
||||
"Approximate current-flow betweenness centrality: solvers"
|
||||
G = nx.complete_graph(4)
|
||||
epsilon = 0.1
|
||||
for solver in ["full", "lu", "cg"]:
|
||||
b = approximate_cfbc(
|
||||
G, normalized=False, solver=solver, epsilon=0.5 * epsilon
|
||||
)
|
||||
b_answer = {0: 0.75, 1: 0.75, 2: 0.75, 3: 0.75}
|
||||
for n in sorted(G):
|
||||
np.testing.assert_allclose(b[n], b_answer[n], atol=epsilon)
|
||||
|
||||
def test_lower_kmax(self):
|
||||
G = nx.complete_graph(4)
|
||||
with pytest.raises(nx.NetworkXError, match="Increase kmax or epsilon"):
|
||||
nx.approximate_current_flow_betweenness_centrality(G, kmax=4)
|
||||
|
||||
|
||||
class TestWeightedFlowBetweennessCentrality:
|
||||
pass
|
||||
|
||||
|
||||
class TestEdgeFlowBetweennessCentrality:
|
||||
def test_K4(self):
|
||||
"""Edge flow betweenness centrality: K4"""
|
||||
G = nx.complete_graph(4)
|
||||
b = edge_current_flow(G, normalized=True)
|
||||
b_answer = dict.fromkeys(G.edges(), 0.25)
|
||||
for (s, t), v1 in b_answer.items():
|
||||
v2 = b.get((s, t), b.get((t, s)))
|
||||
assert v1 == pytest.approx(v2, abs=1e-7)
|
||||
|
||||
def test_K4_normalized(self):
|
||||
"""Edge flow betweenness centrality: K4"""
|
||||
G = nx.complete_graph(4)
|
||||
b = edge_current_flow(G, normalized=False)
|
||||
b_answer = dict.fromkeys(G.edges(), 0.75)
|
||||
for (s, t), v1 in b_answer.items():
|
||||
v2 = b.get((s, t), b.get((t, s)))
|
||||
assert v1 == pytest.approx(v2, abs=1e-7)
|
||||
|
||||
def test_C4(self):
|
||||
"""Edge flow betweenness centrality: C4"""
|
||||
G = nx.cycle_graph(4)
|
||||
b = edge_current_flow(G, normalized=False)
|
||||
b_answer = {(0, 1): 1.25, (0, 3): 1.25, (1, 2): 1.25, (2, 3): 1.25}
|
||||
for (s, t), v1 in b_answer.items():
|
||||
v2 = b.get((s, t), b.get((t, s)))
|
||||
assert v1 == pytest.approx(v2, abs=1e-7)
|
||||
|
||||
def test_P4(self):
|
||||
"""Edge betweenness centrality: P4"""
|
||||
G = nx.path_graph(4)
|
||||
b = edge_current_flow(G, normalized=False)
|
||||
b_answer = {(0, 1): 1.5, (1, 2): 2.0, (2, 3): 1.5}
|
||||
for (s, t), v1 in b_answer.items():
|
||||
v2 = b.get((s, t), b.get((t, s)))
|
||||
assert v1 == pytest.approx(v2, abs=1e-7)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"centrality_func",
|
||||
(
|
||||
nx.current_flow_betweenness_centrality,
|
||||
nx.edge_current_flow_betweenness_centrality,
|
||||
nx.approximate_current_flow_betweenness_centrality,
|
||||
),
|
||||
)
|
||||
def test_unconnected_graphs_betweenness_centrality(centrality_func):
|
||||
G = nx.Graph([(1, 2), (3, 4)])
|
||||
G.add_node(5)
|
||||
with pytest.raises(nx.NetworkXError, match="Graph not connected"):
|
||||
centrality_func(G)
|
||||
@@ -0,0 +1,147 @@
|
||||
import pytest
|
||||
|
||||
pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
import networkx as nx
|
||||
from networkx import edge_current_flow_betweenness_centrality as edge_current_flow
|
||||
from networkx import (
|
||||
edge_current_flow_betweenness_centrality_subset as edge_current_flow_subset,
|
||||
)
|
||||
|
||||
|
||||
class TestFlowBetweennessCentrality:
|
||||
def test_K4_normalized(self):
|
||||
"""Betweenness centrality: K4"""
|
||||
G = nx.complete_graph(4)
|
||||
b = nx.current_flow_betweenness_centrality_subset(
|
||||
G, list(G), list(G), normalized=True
|
||||
)
|
||||
b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_K4(self):
|
||||
"""Betweenness centrality: K4"""
|
||||
G = nx.complete_graph(4)
|
||||
b = nx.current_flow_betweenness_centrality_subset(
|
||||
G, list(G), list(G), normalized=True
|
||||
)
|
||||
b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
# test weighted network
|
||||
G.add_edge(0, 1, weight=0.5, other=0.3)
|
||||
b = nx.current_flow_betweenness_centrality_subset(
|
||||
G, list(G), list(G), normalized=True, weight=None
|
||||
)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
b = nx.current_flow_betweenness_centrality_subset(
|
||||
G, list(G), list(G), normalized=True
|
||||
)
|
||||
b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
b = nx.current_flow_betweenness_centrality_subset(
|
||||
G, list(G), list(G), normalized=True, weight="other"
|
||||
)
|
||||
b_answer = nx.current_flow_betweenness_centrality(
|
||||
G, normalized=True, weight="other"
|
||||
)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P4_normalized(self):
|
||||
"""Betweenness centrality: P4 normalized"""
|
||||
G = nx.path_graph(4)
|
||||
b = nx.current_flow_betweenness_centrality_subset(
|
||||
G, list(G), list(G), normalized=True
|
||||
)
|
||||
b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P4(self):
|
||||
"""Betweenness centrality: P4"""
|
||||
G = nx.path_graph(4)
|
||||
b = nx.current_flow_betweenness_centrality_subset(
|
||||
G, list(G), list(G), normalized=True
|
||||
)
|
||||
b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_star(self):
|
||||
"""Betweenness centrality: star"""
|
||||
G = nx.Graph()
|
||||
nx.add_star(G, ["a", "b", "c", "d"])
|
||||
b = nx.current_flow_betweenness_centrality_subset(
|
||||
G, list(G), list(G), normalized=True
|
||||
)
|
||||
b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
|
||||
# class TestWeightedFlowBetweennessCentrality():
|
||||
# pass
|
||||
|
||||
|
||||
class TestEdgeFlowBetweennessCentrality:
|
||||
def test_K4_normalized(self):
|
||||
"""Betweenness centrality: K4"""
|
||||
G = nx.complete_graph(4)
|
||||
b = edge_current_flow_subset(G, list(G), list(G), normalized=True)
|
||||
b_answer = edge_current_flow(G, normalized=True)
|
||||
for (s, t), v1 in b_answer.items():
|
||||
v2 = b.get((s, t), b.get((t, s)))
|
||||
assert v1 == pytest.approx(v2, abs=1e-7)
|
||||
|
||||
def test_K4(self):
|
||||
"""Betweenness centrality: K4"""
|
||||
G = nx.complete_graph(4)
|
||||
b = edge_current_flow_subset(G, list(G), list(G), normalized=False)
|
||||
b_answer = edge_current_flow(G, normalized=False)
|
||||
for (s, t), v1 in b_answer.items():
|
||||
v2 = b.get((s, t), b.get((t, s)))
|
||||
assert v1 == pytest.approx(v2, abs=1e-7)
|
||||
# test weighted network
|
||||
G.add_edge(0, 1, weight=0.5, other=0.3)
|
||||
b = edge_current_flow_subset(G, list(G), list(G), normalized=False, weight=None)
|
||||
# weight is None => same as unweighted network
|
||||
for (s, t), v1 in b_answer.items():
|
||||
v2 = b.get((s, t), b.get((t, s)))
|
||||
assert v1 == pytest.approx(v2, abs=1e-7)
|
||||
|
||||
b = edge_current_flow_subset(G, list(G), list(G), normalized=False)
|
||||
b_answer = edge_current_flow(G, normalized=False)
|
||||
for (s, t), v1 in b_answer.items():
|
||||
v2 = b.get((s, t), b.get((t, s)))
|
||||
assert v1 == pytest.approx(v2, abs=1e-7)
|
||||
|
||||
b = edge_current_flow_subset(
|
||||
G, list(G), list(G), normalized=False, weight="other"
|
||||
)
|
||||
b_answer = edge_current_flow(G, normalized=False, weight="other")
|
||||
for (s, t), v1 in b_answer.items():
|
||||
v2 = b.get((s, t), b.get((t, s)))
|
||||
assert v1 == pytest.approx(v2, abs=1e-7)
|
||||
|
||||
def test_C4(self):
|
||||
"""Edge betweenness centrality: C4"""
|
||||
G = nx.cycle_graph(4)
|
||||
b = edge_current_flow_subset(G, list(G), list(G), normalized=True)
|
||||
b_answer = edge_current_flow(G, normalized=True)
|
||||
for (s, t), v1 in b_answer.items():
|
||||
v2 = b.get((s, t), b.get((t, s)))
|
||||
assert v1 == pytest.approx(v2, abs=1e-7)
|
||||
|
||||
def test_P4(self):
|
||||
"""Edge betweenness centrality: P4"""
|
||||
G = nx.path_graph(4)
|
||||
b = edge_current_flow_subset(G, list(G), list(G), normalized=True)
|
||||
b_answer = edge_current_flow(G, normalized=True)
|
||||
for (s, t), v1 in b_answer.items():
|
||||
v2 = b.get((s, t), b.get((t, s)))
|
||||
assert v1 == pytest.approx(v2, abs=1e-7)
|
||||
@@ -0,0 +1,43 @@
|
||||
import pytest
|
||||
|
||||
pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestFlowClosenessCentrality:
|
||||
def test_K4(self):
|
||||
"""Closeness centrality: K4"""
|
||||
G = nx.complete_graph(4)
|
||||
b = nx.current_flow_closeness_centrality(G)
|
||||
b_answer = {0: 2.0 / 3, 1: 2.0 / 3, 2: 2.0 / 3, 3: 2.0 / 3}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P4(self):
|
||||
"""Closeness centrality: P4"""
|
||||
G = nx.path_graph(4)
|
||||
b = nx.current_flow_closeness_centrality(G)
|
||||
b_answer = {0: 1.0 / 6, 1: 1.0 / 4, 2: 1.0 / 4, 3: 1.0 / 6}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_star(self):
|
||||
"""Closeness centrality: star"""
|
||||
G = nx.Graph()
|
||||
nx.add_star(G, ["a", "b", "c", "d"])
|
||||
b = nx.current_flow_closeness_centrality(G)
|
||||
b_answer = {"a": 1.0 / 3, "b": 0.6 / 3, "c": 0.6 / 3, "d": 0.6 / 3}
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_current_flow_closeness_centrality_not_connected(self):
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from([1, 2, 3])
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
nx.current_flow_closeness_centrality(G)
|
||||
|
||||
|
||||
class TestWeightedFlowClosenessCentrality:
|
||||
pass
|
||||
144
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_degree_centrality.py
vendored
Normal file
144
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_degree_centrality.py
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
"""
|
||||
Unit tests for degree centrality.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestDegreeCentrality:
|
||||
def setup_method(self):
|
||||
self.K = nx.krackhardt_kite_graph()
|
||||
self.P3 = nx.path_graph(3)
|
||||
self.K5 = nx.complete_graph(5)
|
||||
|
||||
F = nx.Graph() # Florentine families
|
||||
F.add_edge("Acciaiuoli", "Medici")
|
||||
F.add_edge("Castellani", "Peruzzi")
|
||||
F.add_edge("Castellani", "Strozzi")
|
||||
F.add_edge("Castellani", "Barbadori")
|
||||
F.add_edge("Medici", "Barbadori")
|
||||
F.add_edge("Medici", "Ridolfi")
|
||||
F.add_edge("Medici", "Tornabuoni")
|
||||
F.add_edge("Medici", "Albizzi")
|
||||
F.add_edge("Medici", "Salviati")
|
||||
F.add_edge("Salviati", "Pazzi")
|
||||
F.add_edge("Peruzzi", "Strozzi")
|
||||
F.add_edge("Peruzzi", "Bischeri")
|
||||
F.add_edge("Strozzi", "Ridolfi")
|
||||
F.add_edge("Strozzi", "Bischeri")
|
||||
F.add_edge("Ridolfi", "Tornabuoni")
|
||||
F.add_edge("Tornabuoni", "Guadagni")
|
||||
F.add_edge("Albizzi", "Ginori")
|
||||
F.add_edge("Albizzi", "Guadagni")
|
||||
F.add_edge("Bischeri", "Guadagni")
|
||||
F.add_edge("Guadagni", "Lamberteschi")
|
||||
self.F = F
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(0, 5)
|
||||
G.add_edge(1, 5)
|
||||
G.add_edge(2, 5)
|
||||
G.add_edge(3, 5)
|
||||
G.add_edge(4, 5)
|
||||
G.add_edge(5, 6)
|
||||
G.add_edge(5, 7)
|
||||
G.add_edge(5, 8)
|
||||
self.G = G
|
||||
|
||||
def test_degree_centrality_1(self):
|
||||
d = nx.degree_centrality(self.K5)
|
||||
exact = dict(zip(range(5), [1] * 5))
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
def test_degree_centrality_2(self):
|
||||
d = nx.degree_centrality(self.P3)
|
||||
exact = {0: 0.5, 1: 1, 2: 0.5}
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
def test_degree_centrality_3(self):
|
||||
d = nx.degree_centrality(self.K)
|
||||
exact = {
|
||||
0: 0.444,
|
||||
1: 0.444,
|
||||
2: 0.333,
|
||||
3: 0.667,
|
||||
4: 0.333,
|
||||
5: 0.556,
|
||||
6: 0.556,
|
||||
7: 0.333,
|
||||
8: 0.222,
|
||||
9: 0.111,
|
||||
}
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(float(f"{dc:.3f}"), abs=1e-7)
|
||||
|
||||
def test_degree_centrality_4(self):
|
||||
d = nx.degree_centrality(self.F)
|
||||
names = sorted(self.F.nodes())
|
||||
dcs = [
|
||||
0.071,
|
||||
0.214,
|
||||
0.143,
|
||||
0.214,
|
||||
0.214,
|
||||
0.071,
|
||||
0.286,
|
||||
0.071,
|
||||
0.429,
|
||||
0.071,
|
||||
0.214,
|
||||
0.214,
|
||||
0.143,
|
||||
0.286,
|
||||
0.214,
|
||||
]
|
||||
exact = dict(zip(names, dcs))
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(float(f"{dc:.3f}"), abs=1e-7)
|
||||
|
||||
def test_indegree_centrality(self):
|
||||
d = nx.in_degree_centrality(self.G)
|
||||
exact = {
|
||||
0: 0.0,
|
||||
1: 0.0,
|
||||
2: 0.0,
|
||||
3: 0.0,
|
||||
4: 0.0,
|
||||
5: 0.625,
|
||||
6: 0.125,
|
||||
7: 0.125,
|
||||
8: 0.125,
|
||||
}
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
def test_outdegree_centrality(self):
|
||||
d = nx.out_degree_centrality(self.G)
|
||||
exact = {
|
||||
0: 0.125,
|
||||
1: 0.125,
|
||||
2: 0.125,
|
||||
3: 0.125,
|
||||
4: 0.125,
|
||||
5: 0.375,
|
||||
6: 0.0,
|
||||
7: 0.0,
|
||||
8: 0.0,
|
||||
}
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
def test_small_graph_centrality(self):
|
||||
G = nx.empty_graph(create_using=nx.DiGraph)
|
||||
assert {} == nx.degree_centrality(G)
|
||||
assert {} == nx.out_degree_centrality(G)
|
||||
assert {} == nx.in_degree_centrality(G)
|
||||
|
||||
G = nx.empty_graph(1, create_using=nx.DiGraph)
|
||||
assert {0: 1} == nx.degree_centrality(G)
|
||||
assert {0: 1} == nx.out_degree_centrality(G)
|
||||
assert {0: 1} == nx.in_degree_centrality(G)
|
||||
73
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_dispersion.py
vendored
Normal file
73
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_dispersion.py
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def small_ego_G():
|
||||
"""The sample network from https://arxiv.org/pdf/1310.6753v1.pdf"""
|
||||
edges = [
|
||||
("a", "b"),
|
||||
("a", "c"),
|
||||
("b", "c"),
|
||||
("b", "d"),
|
||||
("b", "e"),
|
||||
("b", "f"),
|
||||
("c", "d"),
|
||||
("c", "f"),
|
||||
("c", "h"),
|
||||
("d", "f"),
|
||||
("e", "f"),
|
||||
("f", "h"),
|
||||
("h", "j"),
|
||||
("h", "k"),
|
||||
("i", "j"),
|
||||
("i", "k"),
|
||||
("j", "k"),
|
||||
("u", "a"),
|
||||
("u", "b"),
|
||||
("u", "c"),
|
||||
("u", "d"),
|
||||
("u", "e"),
|
||||
("u", "f"),
|
||||
("u", "g"),
|
||||
("u", "h"),
|
||||
("u", "i"),
|
||||
("u", "j"),
|
||||
("u", "k"),
|
||||
]
|
||||
G = nx.Graph()
|
||||
G.add_edges_from(edges)
|
||||
|
||||
return G
|
||||
|
||||
|
||||
class TestDispersion:
|
||||
def test_article(self):
|
||||
"""our algorithm matches article's"""
|
||||
G = small_ego_G()
|
||||
disp_uh = nx.dispersion(G, "u", "h", normalized=False)
|
||||
disp_ub = nx.dispersion(G, "u", "b", normalized=False)
|
||||
assert disp_uh == 4
|
||||
assert disp_ub == 1
|
||||
|
||||
def test_results_length(self):
|
||||
"""there is a result for every node"""
|
||||
G = small_ego_G()
|
||||
disp = nx.dispersion(G)
|
||||
disp_Gu = nx.dispersion(G, "u")
|
||||
disp_uv = nx.dispersion(G, "u", "h")
|
||||
assert len(disp) == len(G)
|
||||
assert len(disp_Gu) == len(G) - 1
|
||||
assert isinstance(disp_uv, float)
|
||||
|
||||
def test_dispersion_v_only(self):
|
||||
G = small_ego_G()
|
||||
disp_G_h = nx.dispersion(G, v="h", normalized=False)
|
||||
disp_G_h_normalized = nx.dispersion(G, v="h", normalized=True)
|
||||
assert disp_G_h == {"c": 0, "f": 0, "j": 0, "k": 0, "u": 4}
|
||||
assert disp_G_h_normalized == {"c": 0.0, "f": 0.0, "j": 0.0, "k": 0.0, "u": 1.0}
|
||||
|
||||
def test_impossible_things(self):
|
||||
G = nx.karate_club_graph()
|
||||
disp = nx.dispersion(G)
|
||||
for u in disp:
|
||||
for v in disp[u]:
|
||||
assert disp[u][v] >= 0
|
||||
175
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py
vendored
Normal file
175
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
import math
|
||||
|
||||
import pytest
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestEigenvectorCentrality:
|
||||
def test_K5(self):
|
||||
"""Eigenvector centrality: K5"""
|
||||
G = nx.complete_graph(5)
|
||||
b = nx.eigenvector_centrality(G)
|
||||
v = math.sqrt(1 / 5.0)
|
||||
b_answer = dict.fromkeys(G, v)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
nstart = {n: 1 for n in G}
|
||||
b = nx.eigenvector_centrality(G, nstart=nstart)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
b = nx.eigenvector_centrality_numpy(G)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_P3(self):
|
||||
"""Eigenvector centrality: P3"""
|
||||
G = nx.path_graph(3)
|
||||
b_answer = {0: 0.5, 1: 0.7071, 2: 0.5}
|
||||
b = nx.eigenvector_centrality_numpy(G)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
|
||||
b = nx.eigenvector_centrality(G)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
|
||||
|
||||
def test_P3_unweighted(self):
|
||||
"""Eigenvector centrality: P3"""
|
||||
G = nx.path_graph(3)
|
||||
b_answer = {0: 0.5, 1: 0.7071, 2: 0.5}
|
||||
b = nx.eigenvector_centrality_numpy(G, weight=None)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
|
||||
|
||||
def test_maxiter(self):
|
||||
with pytest.raises(nx.PowerIterationFailedConvergence):
|
||||
G = nx.path_graph(3)
|
||||
nx.eigenvector_centrality(G, max_iter=0)
|
||||
|
||||
|
||||
class TestEigenvectorCentralityDirected:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
G = nx.DiGraph()
|
||||
|
||||
edges = [
|
||||
(1, 2),
|
||||
(1, 3),
|
||||
(2, 4),
|
||||
(3, 2),
|
||||
(3, 5),
|
||||
(4, 2),
|
||||
(4, 5),
|
||||
(4, 6),
|
||||
(5, 6),
|
||||
(5, 7),
|
||||
(5, 8),
|
||||
(6, 8),
|
||||
(7, 1),
|
||||
(7, 5),
|
||||
(7, 8),
|
||||
(8, 6),
|
||||
(8, 7),
|
||||
]
|
||||
|
||||
G.add_edges_from(edges, weight=2.0)
|
||||
cls.G = G.reverse()
|
||||
cls.G.evc = [
|
||||
0.25368793,
|
||||
0.19576478,
|
||||
0.32817092,
|
||||
0.40430835,
|
||||
0.48199885,
|
||||
0.15724483,
|
||||
0.51346196,
|
||||
0.32475403,
|
||||
]
|
||||
|
||||
H = nx.DiGraph()
|
||||
|
||||
edges = [
|
||||
(1, 2),
|
||||
(1, 3),
|
||||
(2, 4),
|
||||
(3, 2),
|
||||
(3, 5),
|
||||
(4, 2),
|
||||
(4, 5),
|
||||
(4, 6),
|
||||
(5, 6),
|
||||
(5, 7),
|
||||
(5, 8),
|
||||
(6, 8),
|
||||
(7, 1),
|
||||
(7, 5),
|
||||
(7, 8),
|
||||
(8, 6),
|
||||
(8, 7),
|
||||
]
|
||||
|
||||
G.add_edges_from(edges)
|
||||
cls.H = G.reverse()
|
||||
cls.H.evc = [
|
||||
0.25368793,
|
||||
0.19576478,
|
||||
0.32817092,
|
||||
0.40430835,
|
||||
0.48199885,
|
||||
0.15724483,
|
||||
0.51346196,
|
||||
0.32475403,
|
||||
]
|
||||
|
||||
def test_eigenvector_centrality_weighted(self):
|
||||
G = self.G
|
||||
p = nx.eigenvector_centrality(G)
|
||||
for a, b in zip(list(p.values()), self.G.evc):
|
||||
assert a == pytest.approx(b, abs=1e-4)
|
||||
|
||||
def test_eigenvector_centrality_weighted_numpy(self):
|
||||
G = self.G
|
||||
p = nx.eigenvector_centrality_numpy(G)
|
||||
for a, b in zip(list(p.values()), self.G.evc):
|
||||
assert a == pytest.approx(b, abs=1e-7)
|
||||
|
||||
def test_eigenvector_centrality_unweighted(self):
|
||||
G = self.H
|
||||
p = nx.eigenvector_centrality(G)
|
||||
for a, b in zip(list(p.values()), self.G.evc):
|
||||
assert a == pytest.approx(b, abs=1e-4)
|
||||
|
||||
def test_eigenvector_centrality_unweighted_numpy(self):
|
||||
G = self.H
|
||||
p = nx.eigenvector_centrality_numpy(G)
|
||||
for a, b in zip(list(p.values()), self.G.evc):
|
||||
assert a == pytest.approx(b, abs=1e-7)
|
||||
|
||||
|
||||
class TestEigenvectorCentralityExceptions:
|
||||
def test_multigraph(self):
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
nx.eigenvector_centrality(nx.MultiGraph())
|
||||
|
||||
def test_multigraph_numpy(self):
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
nx.eigenvector_centrality_numpy(nx.MultiGraph())
|
||||
|
||||
def test_empty(self):
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
nx.eigenvector_centrality(nx.Graph())
|
||||
|
||||
def test_empty_numpy(self):
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
nx.eigenvector_centrality_numpy(nx.Graph())
|
||||
|
||||
def test_zero_nstart(self):
|
||||
G = nx.Graph([(1, 2), (1, 3), (2, 3)])
|
||||
with pytest.raises(
|
||||
nx.NetworkXException, match="initial vector cannot have all zero values"
|
||||
):
|
||||
nx.eigenvector_centrality(G, nstart={v: 0 for v in G})
|
||||
278
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_group.py
vendored
Normal file
278
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_group.py
vendored
Normal file
@@ -0,0 +1,278 @@
|
||||
"""
|
||||
Tests for Group Centrality Measures
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestGroupBetweennessCentrality:
|
||||
def test_group_betweenness_single_node(self):
|
||||
"""
|
||||
Group betweenness centrality for single node group
|
||||
"""
|
||||
G = nx.path_graph(5)
|
||||
C = [1]
|
||||
b = nx.group_betweenness_centrality(
|
||||
G, C, weight=None, normalized=False, endpoints=False
|
||||
)
|
||||
b_answer = 3.0
|
||||
assert b == b_answer
|
||||
|
||||
def test_group_betweenness_with_endpoints(self):
|
||||
"""
|
||||
Group betweenness centrality for single node group
|
||||
"""
|
||||
G = nx.path_graph(5)
|
||||
C = [1]
|
||||
b = nx.group_betweenness_centrality(
|
||||
G, C, weight=None, normalized=False, endpoints=True
|
||||
)
|
||||
b_answer = 7.0
|
||||
assert b == b_answer
|
||||
|
||||
def test_group_betweenness_normalized(self):
|
||||
"""
|
||||
Group betweenness centrality for group with more than
|
||||
1 node and normalized
|
||||
"""
|
||||
G = nx.path_graph(5)
|
||||
C = [1, 3]
|
||||
b = nx.group_betweenness_centrality(
|
||||
G, C, weight=None, normalized=True, endpoints=False
|
||||
)
|
||||
b_answer = 1.0
|
||||
assert b == b_answer
|
||||
|
||||
def test_two_group_betweenness_value_zero(self):
|
||||
"""
|
||||
Group betweenness centrality value of 0
|
||||
"""
|
||||
G = nx.cycle_graph(7)
|
||||
C = [[0, 1, 6], [0, 1, 5]]
|
||||
b = nx.group_betweenness_centrality(G, C, weight=None, normalized=False)
|
||||
b_answer = [0.0, 3.0]
|
||||
assert b == b_answer
|
||||
|
||||
def test_group_betweenness_value_zero(self):
|
||||
"""
|
||||
Group betweenness centrality value of 0
|
||||
"""
|
||||
G = nx.cycle_graph(6)
|
||||
C = [0, 1, 5]
|
||||
b = nx.group_betweenness_centrality(G, C, weight=None, normalized=False)
|
||||
b_answer = 0.0
|
||||
assert b == b_answer
|
||||
|
||||
def test_group_betweenness_disconnected_graph(self):
|
||||
"""
|
||||
Group betweenness centrality in a disconnected graph
|
||||
"""
|
||||
G = nx.path_graph(5)
|
||||
G.remove_edge(0, 1)
|
||||
C = [1]
|
||||
b = nx.group_betweenness_centrality(G, C, weight=None, normalized=False)
|
||||
b_answer = 0.0
|
||||
assert b == b_answer
|
||||
|
||||
def test_group_betweenness_node_not_in_graph(self):
|
||||
"""
|
||||
Node(s) in C not in graph, raises NodeNotFound exception
|
||||
"""
|
||||
with pytest.raises(nx.NodeNotFound):
|
||||
nx.group_betweenness_centrality(nx.path_graph(5), [4, 7, 8])
|
||||
|
||||
def test_group_betweenness_directed_weighted(self):
|
||||
"""
|
||||
Group betweenness centrality in a directed and weighted graph
|
||||
"""
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(1, 0, weight=1)
|
||||
G.add_edge(0, 2, weight=2)
|
||||
G.add_edge(1, 2, weight=3)
|
||||
G.add_edge(3, 1, weight=4)
|
||||
G.add_edge(2, 3, weight=1)
|
||||
G.add_edge(4, 3, weight=6)
|
||||
G.add_edge(2, 4, weight=7)
|
||||
C = [1, 2]
|
||||
b = nx.group_betweenness_centrality(G, C, weight="weight", normalized=False)
|
||||
b_answer = 5.0
|
||||
assert b == b_answer
|
||||
|
||||
|
||||
class TestProminentGroup:
|
||||
np = pytest.importorskip("numpy")
|
||||
pd = pytest.importorskip("pandas")
|
||||
|
||||
def test_prominent_group_single_node(self):
|
||||
"""
|
||||
Prominent group for single node
|
||||
"""
|
||||
G = nx.path_graph(5)
|
||||
k = 1
|
||||
b, g = nx.prominent_group(G, k, normalized=False, endpoints=False)
|
||||
b_answer, g_answer = 4.0, [2]
|
||||
assert b == b_answer and g == g_answer
|
||||
|
||||
def test_prominent_group_with_c(self):
|
||||
"""
|
||||
Prominent group without some nodes
|
||||
"""
|
||||
G = nx.path_graph(5)
|
||||
k = 1
|
||||
b, g = nx.prominent_group(G, k, normalized=False, C=[2])
|
||||
b_answer, g_answer = 3.0, [1]
|
||||
assert b == b_answer and g == g_answer
|
||||
|
||||
def test_prominent_group_normalized_endpoints(self):
|
||||
"""
|
||||
Prominent group with normalized result, with endpoints
|
||||
"""
|
||||
G = nx.cycle_graph(7)
|
||||
k = 2
|
||||
b, g = nx.prominent_group(G, k, normalized=True, endpoints=True)
|
||||
b_answer, g_answer = 1.7, [2, 5]
|
||||
assert b == b_answer and g == g_answer
|
||||
|
||||
def test_prominent_group_disconnected_graph(self):
|
||||
"""
|
||||
Prominent group of disconnected graph
|
||||
"""
|
||||
G = nx.path_graph(6)
|
||||
G.remove_edge(0, 1)
|
||||
k = 1
|
||||
b, g = nx.prominent_group(G, k, weight=None, normalized=False)
|
||||
b_answer, g_answer = 4.0, [3]
|
||||
assert b == b_answer and g == g_answer
|
||||
|
||||
def test_prominent_group_node_not_in_graph(self):
|
||||
"""
|
||||
Node(s) in C not in graph, raises NodeNotFound exception
|
||||
"""
|
||||
with pytest.raises(nx.NodeNotFound):
|
||||
nx.prominent_group(nx.path_graph(5), 1, C=[10])
|
||||
|
||||
def test_group_betweenness_directed_weighted(self):
|
||||
"""
|
||||
Group betweenness centrality in a directed and weighted graph
|
||||
"""
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(1, 0, weight=1)
|
||||
G.add_edge(0, 2, weight=2)
|
||||
G.add_edge(1, 2, weight=3)
|
||||
G.add_edge(3, 1, weight=4)
|
||||
G.add_edge(2, 3, weight=1)
|
||||
G.add_edge(4, 3, weight=6)
|
||||
G.add_edge(2, 4, weight=7)
|
||||
k = 2
|
||||
b, g = nx.prominent_group(G, k, weight="weight", normalized=False)
|
||||
b_answer, g_answer = 5.0, [1, 2]
|
||||
assert b == b_answer and g == g_answer
|
||||
|
||||
def test_prominent_group_greedy_algorithm(self):
|
||||
"""
|
||||
Group betweenness centrality in a greedy algorithm
|
||||
"""
|
||||
G = nx.cycle_graph(7)
|
||||
k = 2
|
||||
b, g = nx.prominent_group(G, k, normalized=True, endpoints=True, greedy=True)
|
||||
b_answer, g_answer = 1.7, [6, 3]
|
||||
assert b == b_answer and g == g_answer
|
||||
|
||||
|
||||
class TestGroupClosenessCentrality:
|
||||
def test_group_closeness_single_node(self):
|
||||
"""
|
||||
Group closeness centrality for a single node group
|
||||
"""
|
||||
G = nx.path_graph(5)
|
||||
c = nx.group_closeness_centrality(G, [1])
|
||||
c_answer = nx.closeness_centrality(G, 1)
|
||||
assert c == c_answer
|
||||
|
||||
def test_group_closeness_disconnected(self):
|
||||
"""
|
||||
Group closeness centrality for a disconnected graph
|
||||
"""
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from([1, 2, 3, 4])
|
||||
c = nx.group_closeness_centrality(G, [1, 2])
|
||||
c_answer = 0
|
||||
assert c == c_answer
|
||||
|
||||
def test_group_closeness_multiple_node(self):
|
||||
"""
|
||||
Group closeness centrality for a group with more than
|
||||
1 node
|
||||
"""
|
||||
G = nx.path_graph(4)
|
||||
c = nx.group_closeness_centrality(G, [1, 2])
|
||||
c_answer = 1
|
||||
assert c == c_answer
|
||||
|
||||
def test_group_closeness_node_not_in_graph(self):
|
||||
"""
|
||||
Node(s) in S not in graph, raises NodeNotFound exception
|
||||
"""
|
||||
with pytest.raises(nx.NodeNotFound):
|
||||
nx.group_closeness_centrality(nx.path_graph(5), [6, 7, 8])
|
||||
|
||||
|
||||
class TestGroupDegreeCentrality:
|
||||
def test_group_degree_centrality_single_node(self):
|
||||
"""
|
||||
Group degree centrality for a single node group
|
||||
"""
|
||||
G = nx.path_graph(4)
|
||||
d = nx.group_degree_centrality(G, [1])
|
||||
d_answer = nx.degree_centrality(G)[1]
|
||||
assert d == d_answer
|
||||
|
||||
def test_group_degree_centrality_multiple_node(self):
|
||||
"""
|
||||
Group degree centrality for group with more than
|
||||
1 node
|
||||
"""
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from([1, 2, 3, 4, 5, 6, 7, 8])
|
||||
G.add_edges_from(
|
||||
[(1, 2), (1, 3), (1, 6), (1, 7), (1, 8), (2, 3), (2, 4), (2, 5)]
|
||||
)
|
||||
d = nx.group_degree_centrality(G, [1, 2])
|
||||
d_answer = 1
|
||||
assert d == d_answer
|
||||
|
||||
def test_group_in_degree_centrality(self):
|
||||
"""
|
||||
Group in-degree centrality in a DiGraph
|
||||
"""
|
||||
G = nx.DiGraph()
|
||||
G.add_nodes_from([1, 2, 3, 4, 5, 6, 7, 8])
|
||||
G.add_edges_from(
|
||||
[(1, 2), (1, 3), (1, 6), (1, 7), (1, 8), (2, 3), (2, 4), (2, 5)]
|
||||
)
|
||||
d = nx.group_in_degree_centrality(G, [1, 2])
|
||||
d_answer = 0
|
||||
assert d == d_answer
|
||||
|
||||
def test_group_out_degree_centrality(self):
|
||||
"""
|
||||
Group out-degree centrality in a DiGraph
|
||||
"""
|
||||
G = nx.DiGraph()
|
||||
G.add_nodes_from([1, 2, 3, 4, 5, 6, 7, 8])
|
||||
G.add_edges_from(
|
||||
[(1, 2), (1, 3), (1, 6), (1, 7), (1, 8), (2, 3), (2, 4), (2, 5)]
|
||||
)
|
||||
d = nx.group_out_degree_centrality(G, [1, 2])
|
||||
d_answer = 1
|
||||
assert d == d_answer
|
||||
|
||||
def test_group_degree_centrality_node_not_in_graph(self):
|
||||
"""
|
||||
Node(s) in S not in graph, raises NetworkXError
|
||||
"""
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
nx.group_degree_centrality(nx.path_graph(5), [6, 7, 8])
|
||||
115
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_harmonic_centrality.py
vendored
Normal file
115
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_harmonic_centrality.py
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Tests for degree centrality.
|
||||
"""
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms.centrality import harmonic_centrality
|
||||
|
||||
|
||||
class TestClosenessCentrality:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.P3 = nx.path_graph(3)
|
||||
cls.P4 = nx.path_graph(4)
|
||||
cls.K5 = nx.complete_graph(5)
|
||||
|
||||
cls.C4 = nx.cycle_graph(4)
|
||||
cls.C4_directed = nx.cycle_graph(4, create_using=nx.DiGraph)
|
||||
|
||||
cls.C5 = nx.cycle_graph(5)
|
||||
|
||||
cls.T = nx.balanced_tree(r=2, h=2)
|
||||
|
||||
cls.Gb = nx.DiGraph()
|
||||
cls.Gb.add_edges_from([(0, 1), (0, 2), (0, 4), (2, 1), (2, 3), (4, 3)])
|
||||
|
||||
def test_p3_harmonic(self):
|
||||
c = harmonic_centrality(self.P3)
|
||||
d = {0: 1.5, 1: 2, 2: 1.5}
|
||||
for n in sorted(self.P3):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_p4_harmonic(self):
|
||||
c = harmonic_centrality(self.P4)
|
||||
d = {0: 1.8333333, 1: 2.5, 2: 2.5, 3: 1.8333333}
|
||||
for n in sorted(self.P4):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_clique_complete(self):
|
||||
c = harmonic_centrality(self.K5)
|
||||
d = {0: 4, 1: 4, 2: 4, 3: 4, 4: 4}
|
||||
for n in sorted(self.P3):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_cycle_C4(self):
|
||||
c = harmonic_centrality(self.C4)
|
||||
d = {0: 2.5, 1: 2.5, 2: 2.5, 3: 2.5}
|
||||
for n in sorted(self.C4):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_cycle_C5(self):
|
||||
c = harmonic_centrality(self.C5)
|
||||
d = {0: 3, 1: 3, 2: 3, 3: 3, 4: 3, 5: 4}
|
||||
for n in sorted(self.C5):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_bal_tree(self):
|
||||
c = harmonic_centrality(self.T)
|
||||
d = {0: 4.0, 1: 4.1666, 2: 4.1666, 3: 2.8333, 4: 2.8333, 5: 2.8333, 6: 2.8333}
|
||||
for n in sorted(self.T):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_exampleGraph(self):
|
||||
c = harmonic_centrality(self.Gb)
|
||||
d = {0: 0, 1: 2, 2: 1, 3: 2.5, 4: 1}
|
||||
for n in sorted(self.Gb):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_weighted_harmonic(self):
|
||||
XG = nx.DiGraph()
|
||||
XG.add_weighted_edges_from(
|
||||
[
|
||||
("a", "b", 10),
|
||||
("d", "c", 5),
|
||||
("a", "c", 1),
|
||||
("e", "f", 2),
|
||||
("f", "c", 1),
|
||||
("a", "f", 3),
|
||||
]
|
||||
)
|
||||
c = harmonic_centrality(XG, distance="weight")
|
||||
d = {"a": 0, "b": 0.1, "c": 2.533, "d": 0, "e": 0, "f": 0.83333}
|
||||
for n in sorted(XG):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_empty(self):
|
||||
G = nx.DiGraph()
|
||||
c = harmonic_centrality(G, distance="weight")
|
||||
d = {}
|
||||
assert c == d
|
||||
|
||||
def test_singleton(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_node(0)
|
||||
c = harmonic_centrality(G, distance="weight")
|
||||
d = {0: 0}
|
||||
assert c == d
|
||||
|
||||
def test_cycle_c4_directed(self):
|
||||
c = harmonic_centrality(self.C4_directed, nbunch=[0, 1], sources=[1, 2])
|
||||
d = {0: 0.833, 1: 0.333}
|
||||
for n in [0, 1]:
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_p3_harmonic_subset(self):
|
||||
c = harmonic_centrality(self.P3, sources=[0, 1])
|
||||
d = {0: 1, 1: 1, 2: 1.5}
|
||||
for n in self.P3:
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_p4_harmonic_subset(self):
|
||||
c = harmonic_centrality(self.P4, nbunch=[2, 3], sources=[0, 1])
|
||||
d = {2: 1.5, 3: 0.8333333}
|
||||
for n in [2, 3]:
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
345
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_katz_centrality.py
vendored
Normal file
345
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_katz_centrality.py
vendored
Normal file
@@ -0,0 +1,345 @@
|
||||
import math
|
||||
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestKatzCentrality:
|
||||
def test_K5(self):
|
||||
"""Katz centrality: K5"""
|
||||
G = nx.complete_graph(5)
|
||||
alpha = 0.1
|
||||
b = nx.katz_centrality(G, alpha)
|
||||
v = math.sqrt(1 / 5.0)
|
||||
b_answer = dict.fromkeys(G, v)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
nstart = {n: 1 for n in G}
|
||||
b = nx.katz_centrality(G, alpha, nstart=nstart)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
|
||||
def test_P3(self):
|
||||
"""Katz centrality: P3"""
|
||||
alpha = 0.1
|
||||
G = nx.path_graph(3)
|
||||
b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
|
||||
b = nx.katz_centrality(G, alpha)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
|
||||
|
||||
def test_maxiter(self):
|
||||
with pytest.raises(nx.PowerIterationFailedConvergence):
|
||||
nx.katz_centrality(nx.path_graph(3), 0.1, max_iter=0)
|
||||
|
||||
def test_beta_as_scalar(self):
|
||||
alpha = 0.1
|
||||
beta = 0.1
|
||||
b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
|
||||
G = nx.path_graph(3)
|
||||
b = nx.katz_centrality(G, alpha, beta)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
|
||||
|
||||
def test_beta_as_dict(self):
|
||||
alpha = 0.1
|
||||
beta = {0: 1.0, 1: 1.0, 2: 1.0}
|
||||
b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
|
||||
G = nx.path_graph(3)
|
||||
b = nx.katz_centrality(G, alpha, beta)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
|
||||
|
||||
def test_multiple_alpha(self):
|
||||
alpha_list = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
|
||||
for alpha in alpha_list:
|
||||
b_answer = {
|
||||
0.1: {
|
||||
0: 0.5598852584152165,
|
||||
1: 0.6107839182711449,
|
||||
2: 0.5598852584152162,
|
||||
},
|
||||
0.2: {
|
||||
0: 0.5454545454545454,
|
||||
1: 0.6363636363636365,
|
||||
2: 0.5454545454545454,
|
||||
},
|
||||
0.3: {
|
||||
0: 0.5333964609104419,
|
||||
1: 0.6564879518897746,
|
||||
2: 0.5333964609104419,
|
||||
},
|
||||
0.4: {
|
||||
0: 0.5232045649263551,
|
||||
1: 0.6726915834767423,
|
||||
2: 0.5232045649263551,
|
||||
},
|
||||
0.5: {
|
||||
0: 0.5144957746691622,
|
||||
1: 0.6859943117075809,
|
||||
2: 0.5144957746691622,
|
||||
},
|
||||
0.6: {
|
||||
0: 0.5069794004195823,
|
||||
1: 0.6970966755769258,
|
||||
2: 0.5069794004195823,
|
||||
},
|
||||
}
|
||||
G = nx.path_graph(3)
|
||||
b = nx.katz_centrality(G, alpha)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[alpha][n], abs=1e-4)
|
||||
|
||||
def test_multigraph(self):
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
nx.katz_centrality(nx.MultiGraph(), 0.1)
|
||||
|
||||
def test_empty(self):
|
||||
e = nx.katz_centrality(nx.Graph(), 0.1)
|
||||
assert e == {}
|
||||
|
||||
def test_bad_beta(self):
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
G = nx.Graph([(0, 1)])
|
||||
beta = {0: 77}
|
||||
nx.katz_centrality(G, 0.1, beta=beta)
|
||||
|
||||
def test_bad_beta_number(self):
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
G = nx.Graph([(0, 1)])
|
||||
nx.katz_centrality(G, 0.1, beta="foo")
|
||||
|
||||
|
||||
class TestKatzCentralityNumpy:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
global np
|
||||
np = pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
def test_K5(self):
|
||||
"""Katz centrality: K5"""
|
||||
G = nx.complete_graph(5)
|
||||
alpha = 0.1
|
||||
b = nx.katz_centrality(G, alpha)
|
||||
v = math.sqrt(1 / 5.0)
|
||||
b_answer = dict.fromkeys(G, v)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
b = nx.eigenvector_centrality_numpy(G)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_P3(self):
|
||||
"""Katz centrality: P3"""
|
||||
alpha = 0.1
|
||||
G = nx.path_graph(3)
|
||||
b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
|
||||
b = nx.katz_centrality_numpy(G, alpha)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
|
||||
|
||||
def test_beta_as_scalar(self):
|
||||
alpha = 0.1
|
||||
beta = 0.1
|
||||
b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
|
||||
G = nx.path_graph(3)
|
||||
b = nx.katz_centrality_numpy(G, alpha, beta)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
|
||||
|
||||
def test_beta_as_dict(self):
|
||||
alpha = 0.1
|
||||
beta = {0: 1.0, 1: 1.0, 2: 1.0}
|
||||
b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
|
||||
G = nx.path_graph(3)
|
||||
b = nx.katz_centrality_numpy(G, alpha, beta)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
|
||||
|
||||
def test_multiple_alpha(self):
|
||||
alpha_list = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
|
||||
for alpha in alpha_list:
|
||||
b_answer = {
|
||||
0.1: {
|
||||
0: 0.5598852584152165,
|
||||
1: 0.6107839182711449,
|
||||
2: 0.5598852584152162,
|
||||
},
|
||||
0.2: {
|
||||
0: 0.5454545454545454,
|
||||
1: 0.6363636363636365,
|
||||
2: 0.5454545454545454,
|
||||
},
|
||||
0.3: {
|
||||
0: 0.5333964609104419,
|
||||
1: 0.6564879518897746,
|
||||
2: 0.5333964609104419,
|
||||
},
|
||||
0.4: {
|
||||
0: 0.5232045649263551,
|
||||
1: 0.6726915834767423,
|
||||
2: 0.5232045649263551,
|
||||
},
|
||||
0.5: {
|
||||
0: 0.5144957746691622,
|
||||
1: 0.6859943117075809,
|
||||
2: 0.5144957746691622,
|
||||
},
|
||||
0.6: {
|
||||
0: 0.5069794004195823,
|
||||
1: 0.6970966755769258,
|
||||
2: 0.5069794004195823,
|
||||
},
|
||||
}
|
||||
G = nx.path_graph(3)
|
||||
b = nx.katz_centrality_numpy(G, alpha)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[alpha][n], abs=1e-4)
|
||||
|
||||
def test_multigraph(self):
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
nx.katz_centrality(nx.MultiGraph(), 0.1)
|
||||
|
||||
def test_empty(self):
|
||||
e = nx.katz_centrality(nx.Graph(), 0.1)
|
||||
assert e == {}
|
||||
|
||||
def test_bad_beta(self):
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
G = nx.Graph([(0, 1)])
|
||||
beta = {0: 77}
|
||||
nx.katz_centrality_numpy(G, 0.1, beta=beta)
|
||||
|
||||
def test_bad_beta_numbe(self):
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
G = nx.Graph([(0, 1)])
|
||||
nx.katz_centrality_numpy(G, 0.1, beta="foo")
|
||||
|
||||
def test_K5_unweighted(self):
|
||||
"""Katz centrality: K5"""
|
||||
G = nx.complete_graph(5)
|
||||
alpha = 0.1
|
||||
b = nx.katz_centrality(G, alpha, weight=None)
|
||||
v = math.sqrt(1 / 5.0)
|
||||
b_answer = dict.fromkeys(G, v)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
|
||||
b = nx.eigenvector_centrality_numpy(G, weight=None)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
|
||||
|
||||
def test_P3_unweighted(self):
|
||||
"""Katz centrality: P3"""
|
||||
alpha = 0.1
|
||||
G = nx.path_graph(3)
|
||||
b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
|
||||
b = nx.katz_centrality_numpy(G, alpha, weight=None)
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
|
||||
|
||||
|
||||
class TestKatzCentralityDirected:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
G = nx.DiGraph()
|
||||
edges = [
|
||||
(1, 2),
|
||||
(1, 3),
|
||||
(2, 4),
|
||||
(3, 2),
|
||||
(3, 5),
|
||||
(4, 2),
|
||||
(4, 5),
|
||||
(4, 6),
|
||||
(5, 6),
|
||||
(5, 7),
|
||||
(5, 8),
|
||||
(6, 8),
|
||||
(7, 1),
|
||||
(7, 5),
|
||||
(7, 8),
|
||||
(8, 6),
|
||||
(8, 7),
|
||||
]
|
||||
G.add_edges_from(edges, weight=2.0)
|
||||
cls.G = G.reverse()
|
||||
cls.G.alpha = 0.1
|
||||
cls.G.evc = [
|
||||
0.3289589783189635,
|
||||
0.2832077296243516,
|
||||
0.3425906003685471,
|
||||
0.3970420865198392,
|
||||
0.41074871061646284,
|
||||
0.272257430756461,
|
||||
0.4201989685435462,
|
||||
0.34229059218038554,
|
||||
]
|
||||
|
||||
H = nx.DiGraph(edges)
|
||||
cls.H = G.reverse()
|
||||
cls.H.alpha = 0.1
|
||||
cls.H.evc = [
|
||||
0.3289589783189635,
|
||||
0.2832077296243516,
|
||||
0.3425906003685471,
|
||||
0.3970420865198392,
|
||||
0.41074871061646284,
|
||||
0.272257430756461,
|
||||
0.4201989685435462,
|
||||
0.34229059218038554,
|
||||
]
|
||||
|
||||
def test_katz_centrality_weighted(self):
|
||||
G = self.G
|
||||
alpha = self.G.alpha
|
||||
p = nx.katz_centrality(G, alpha, weight="weight")
|
||||
for a, b in zip(list(p.values()), self.G.evc):
|
||||
assert a == pytest.approx(b, abs=1e-7)
|
||||
|
||||
def test_katz_centrality_unweighted(self):
|
||||
H = self.H
|
||||
alpha = self.H.alpha
|
||||
p = nx.katz_centrality(H, alpha, weight="weight")
|
||||
for a, b in zip(list(p.values()), self.H.evc):
|
||||
assert a == pytest.approx(b, abs=1e-7)
|
||||
|
||||
|
||||
class TestKatzCentralityDirectedNumpy(TestKatzCentralityDirected):
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
global np
|
||||
np = pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
super().setup_class()
|
||||
|
||||
def test_katz_centrality_weighted(self):
|
||||
G = self.G
|
||||
alpha = self.G.alpha
|
||||
p = nx.katz_centrality_numpy(G, alpha, weight="weight")
|
||||
for a, b in zip(list(p.values()), self.G.evc):
|
||||
assert a == pytest.approx(b, abs=1e-7)
|
||||
|
||||
def test_katz_centrality_unweighted(self):
|
||||
H = self.H
|
||||
alpha = self.H.alpha
|
||||
p = nx.katz_centrality_numpy(H, alpha, weight="weight")
|
||||
for a, b in zip(list(p.values()), self.H.evc):
|
||||
assert a == pytest.approx(b, abs=1e-7)
|
||||
|
||||
|
||||
class TestKatzEigenvectorVKatz:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
global np
|
||||
np = pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
def test_eigenvector_v_katz_random(self):
|
||||
G = nx.gnp_random_graph(10, 0.5, seed=1234)
|
||||
l = max(np.linalg.eigvals(nx.adjacency_matrix(G).todense()))
|
||||
e = nx.eigenvector_centrality_numpy(G)
|
||||
k = nx.katz_centrality_numpy(G, 1.0 / l)
|
||||
for n in G:
|
||||
assert e[n] == pytest.approx(k[n], abs=1e-7)
|
||||
221
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_laplacian_centrality.py
vendored
Normal file
221
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_laplacian_centrality.py
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
sp = pytest.importorskip("scipy")
|
||||
|
||||
|
||||
def test_laplacian_centrality_null_graph():
|
||||
G = nx.Graph()
|
||||
with pytest.raises(nx.NetworkXPointlessConcept):
|
||||
d = nx.laplacian_centrality(G, normalized=False)
|
||||
|
||||
|
||||
def test_laplacian_centrality_single_node():
|
||||
"""See gh-6571"""
|
||||
G = nx.empty_graph(1)
|
||||
assert nx.laplacian_centrality(G, normalized=False) == {0: 0}
|
||||
with pytest.raises(ZeroDivisionError):
|
||||
nx.laplacian_centrality(G, normalized=True)
|
||||
|
||||
|
||||
def test_laplacian_centrality_unconnected_nodes():
|
||||
"""laplacian_centrality on a unconnected node graph should return 0
|
||||
|
||||
For graphs without edges, the Laplacian energy is 0 and is unchanged with
|
||||
node removal, so::
|
||||
|
||||
LC(v) = LE(G) - LE(G - v) = 0 - 0 = 0
|
||||
"""
|
||||
G = nx.empty_graph(3)
|
||||
assert nx.laplacian_centrality(G, normalized=False) == {0: 0, 1: 0, 2: 0}
|
||||
|
||||
|
||||
def test_laplacian_centrality_empty_graph():
|
||||
G = nx.empty_graph(3)
|
||||
with pytest.raises(ZeroDivisionError):
|
||||
d = nx.laplacian_centrality(G, normalized=True)
|
||||
|
||||
|
||||
def test_laplacian_centrality_E():
|
||||
E = nx.Graph()
|
||||
E.add_weighted_edges_from(
|
||||
[(0, 1, 4), (4, 5, 1), (0, 2, 2), (2, 1, 1), (1, 3, 2), (1, 4, 2)]
|
||||
)
|
||||
d = nx.laplacian_centrality(E)
|
||||
exact = {
|
||||
0: 0.700000,
|
||||
1: 0.900000,
|
||||
2: 0.280000,
|
||||
3: 0.220000,
|
||||
4: 0.260000,
|
||||
5: 0.040000,
|
||||
}
|
||||
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
# Check not normalized
|
||||
full_energy = 200
|
||||
dnn = nx.laplacian_centrality(E, normalized=False)
|
||||
for n, dc in dnn.items():
|
||||
assert exact[n] * full_energy == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
# Check unweighted not-normalized version
|
||||
duw_nn = nx.laplacian_centrality(E, normalized=False, weight=None)
|
||||
print(duw_nn)
|
||||
exact_uw_nn = {
|
||||
0: 18,
|
||||
1: 34,
|
||||
2: 18,
|
||||
3: 10,
|
||||
4: 16,
|
||||
5: 6,
|
||||
}
|
||||
for n, dc in duw_nn.items():
|
||||
assert exact_uw_nn[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
# Check unweighted version
|
||||
duw = nx.laplacian_centrality(E, weight=None)
|
||||
full_energy = 42
|
||||
for n, dc in duw.items():
|
||||
assert exact_uw_nn[n] / full_energy == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
|
||||
def test_laplacian_centrality_KC():
|
||||
KC = nx.karate_club_graph()
|
||||
d = nx.laplacian_centrality(KC)
|
||||
exact = {
|
||||
0: 0.2543593,
|
||||
1: 0.1724524,
|
||||
2: 0.2166053,
|
||||
3: 0.0964646,
|
||||
4: 0.0350344,
|
||||
5: 0.0571109,
|
||||
6: 0.0540713,
|
||||
7: 0.0788674,
|
||||
8: 0.1222204,
|
||||
9: 0.0217565,
|
||||
10: 0.0308751,
|
||||
11: 0.0215965,
|
||||
12: 0.0174372,
|
||||
13: 0.118861,
|
||||
14: 0.0366341,
|
||||
15: 0.0548712,
|
||||
16: 0.0172772,
|
||||
17: 0.0191969,
|
||||
18: 0.0225564,
|
||||
19: 0.0331147,
|
||||
20: 0.0279955,
|
||||
21: 0.0246361,
|
||||
22: 0.0382339,
|
||||
23: 0.1294193,
|
||||
24: 0.0227164,
|
||||
25: 0.0644697,
|
||||
26: 0.0281555,
|
||||
27: 0.075188,
|
||||
28: 0.0364742,
|
||||
29: 0.0707087,
|
||||
30: 0.0708687,
|
||||
31: 0.131019,
|
||||
32: 0.2370821,
|
||||
33: 0.3066709,
|
||||
}
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
# Check not normalized
|
||||
full_energy = 12502
|
||||
dnn = nx.laplacian_centrality(KC, normalized=False)
|
||||
for n, dc in dnn.items():
|
||||
assert exact[n] * full_energy == pytest.approx(dc, abs=1e-3)
|
||||
|
||||
|
||||
def test_laplacian_centrality_K():
|
||||
K = nx.krackhardt_kite_graph()
|
||||
d = nx.laplacian_centrality(K)
|
||||
exact = {
|
||||
0: 0.3010753,
|
||||
1: 0.3010753,
|
||||
2: 0.2258065,
|
||||
3: 0.483871,
|
||||
4: 0.2258065,
|
||||
5: 0.3870968,
|
||||
6: 0.3870968,
|
||||
7: 0.1935484,
|
||||
8: 0.0752688,
|
||||
9: 0.0322581,
|
||||
}
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
# Check not normalized
|
||||
full_energy = 186
|
||||
dnn = nx.laplacian_centrality(K, normalized=False)
|
||||
for n, dc in dnn.items():
|
||||
assert exact[n] * full_energy == pytest.approx(dc, abs=1e-3)
|
||||
|
||||
|
||||
def test_laplacian_centrality_P3():
|
||||
P3 = nx.path_graph(3)
|
||||
d = nx.laplacian_centrality(P3)
|
||||
exact = {0: 0.6, 1: 1.0, 2: 0.6}
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
|
||||
def test_laplacian_centrality_K5():
|
||||
K5 = nx.complete_graph(5)
|
||||
d = nx.laplacian_centrality(K5)
|
||||
exact = {0: 0.52, 1: 0.52, 2: 0.52, 3: 0.52, 4: 0.52}
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
|
||||
def test_laplacian_centrality_FF():
|
||||
FF = nx.florentine_families_graph()
|
||||
d = nx.laplacian_centrality(FF)
|
||||
exact = {
|
||||
"Acciaiuoli": 0.0804598,
|
||||
"Medici": 0.4022989,
|
||||
"Castellani": 0.1724138,
|
||||
"Peruzzi": 0.183908,
|
||||
"Strozzi": 0.2528736,
|
||||
"Barbadori": 0.137931,
|
||||
"Ridolfi": 0.2183908,
|
||||
"Tornabuoni": 0.2183908,
|
||||
"Albizzi": 0.1954023,
|
||||
"Salviati": 0.1149425,
|
||||
"Pazzi": 0.0344828,
|
||||
"Bischeri": 0.1954023,
|
||||
"Guadagni": 0.2298851,
|
||||
"Ginori": 0.045977,
|
||||
"Lamberteschi": 0.0574713,
|
||||
}
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
|
||||
def test_laplacian_centrality_DG():
|
||||
DG = nx.DiGraph([(0, 5), (1, 5), (2, 5), (3, 5), (4, 5), (5, 6), (5, 7), (5, 8)])
|
||||
d = nx.laplacian_centrality(DG)
|
||||
exact = {
|
||||
0: 0.2123352,
|
||||
5: 0.515391,
|
||||
1: 0.2123352,
|
||||
2: 0.2123352,
|
||||
3: 0.2123352,
|
||||
4: 0.2123352,
|
||||
6: 0.2952031,
|
||||
7: 0.2952031,
|
||||
8: 0.2952031,
|
||||
}
|
||||
for n, dc in d.items():
|
||||
assert exact[n] == pytest.approx(dc, abs=1e-7)
|
||||
|
||||
# Check not normalized
|
||||
full_energy = 9.50704
|
||||
dnn = nx.laplacian_centrality(DG, normalized=False)
|
||||
for n, dc in dnn.items():
|
||||
assert exact[n] * full_energy == pytest.approx(dc, abs=1e-4)
|
||||
344
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_load_centrality.py
vendored
Normal file
344
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_load_centrality.py
vendored
Normal file
@@ -0,0 +1,344 @@
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestLoadCentrality:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
G = nx.Graph()
|
||||
G.add_edge(0, 1, weight=3)
|
||||
G.add_edge(0, 2, weight=2)
|
||||
G.add_edge(0, 3, weight=6)
|
||||
G.add_edge(0, 4, weight=4)
|
||||
G.add_edge(1, 3, weight=5)
|
||||
G.add_edge(1, 5, weight=5)
|
||||
G.add_edge(2, 4, weight=1)
|
||||
G.add_edge(3, 4, weight=2)
|
||||
G.add_edge(3, 5, weight=1)
|
||||
G.add_edge(4, 5, weight=4)
|
||||
cls.G = G
|
||||
cls.exact_weighted = {0: 4.0, 1: 0.0, 2: 8.0, 3: 6.0, 4: 8.0, 5: 0.0}
|
||||
cls.K = nx.krackhardt_kite_graph()
|
||||
cls.P3 = nx.path_graph(3)
|
||||
cls.P4 = nx.path_graph(4)
|
||||
cls.K5 = nx.complete_graph(5)
|
||||
cls.P2 = nx.path_graph(2)
|
||||
|
||||
cls.C4 = nx.cycle_graph(4)
|
||||
cls.T = nx.balanced_tree(r=2, h=2)
|
||||
cls.Gb = nx.Graph()
|
||||
cls.Gb.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (4, 5), (3, 5)])
|
||||
cls.F = nx.florentine_families_graph()
|
||||
cls.LM = nx.les_miserables_graph()
|
||||
cls.D = nx.cycle_graph(3, create_using=nx.DiGraph())
|
||||
cls.D.add_edges_from([(3, 0), (4, 3)])
|
||||
|
||||
def test_not_strongly_connected(self):
|
||||
b = nx.load_centrality(self.D)
|
||||
result = {0: 5.0 / 12, 1: 1.0 / 4, 2: 1.0 / 12, 3: 1.0 / 4, 4: 0.000}
|
||||
for n in sorted(self.D):
|
||||
assert result[n] == pytest.approx(b[n], abs=1e-3)
|
||||
assert result[n] == pytest.approx(nx.load_centrality(self.D, n), abs=1e-3)
|
||||
|
||||
def test_P2_normalized_load(self):
|
||||
G = self.P2
|
||||
c = nx.load_centrality(G, normalized=True)
|
||||
d = {0: 0.000, 1: 0.000}
|
||||
for n in sorted(G):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_weighted_load(self):
|
||||
b = nx.load_centrality(self.G, weight="weight", normalized=False)
|
||||
for n in sorted(self.G):
|
||||
assert b[n] == self.exact_weighted[n]
|
||||
|
||||
def test_k5_load(self):
|
||||
G = self.K5
|
||||
c = nx.load_centrality(G)
|
||||
d = {0: 0.000, 1: 0.000, 2: 0.000, 3: 0.000, 4: 0.000}
|
||||
for n in sorted(G):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_p3_load(self):
|
||||
G = self.P3
|
||||
c = nx.load_centrality(G)
|
||||
d = {0: 0.000, 1: 1.000, 2: 0.000}
|
||||
for n in sorted(G):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
c = nx.load_centrality(G, v=1)
|
||||
assert c == pytest.approx(1.0, abs=1e-7)
|
||||
c = nx.load_centrality(G, v=1, normalized=True)
|
||||
assert c == pytest.approx(1.0, abs=1e-7)
|
||||
|
||||
def test_p2_load(self):
|
||||
G = nx.path_graph(2)
|
||||
c = nx.load_centrality(G)
|
||||
d = {0: 0.000, 1: 0.000}
|
||||
for n in sorted(G):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_krackhardt_load(self):
|
||||
G = self.K
|
||||
c = nx.load_centrality(G)
|
||||
d = {
|
||||
0: 0.023,
|
||||
1: 0.023,
|
||||
2: 0.000,
|
||||
3: 0.102,
|
||||
4: 0.000,
|
||||
5: 0.231,
|
||||
6: 0.231,
|
||||
7: 0.389,
|
||||
8: 0.222,
|
||||
9: 0.000,
|
||||
}
|
||||
for n in sorted(G):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_florentine_families_load(self):
|
||||
G = self.F
|
||||
c = nx.load_centrality(G)
|
||||
d = {
|
||||
"Acciaiuoli": 0.000,
|
||||
"Albizzi": 0.211,
|
||||
"Barbadori": 0.093,
|
||||
"Bischeri": 0.104,
|
||||
"Castellani": 0.055,
|
||||
"Ginori": 0.000,
|
||||
"Guadagni": 0.251,
|
||||
"Lamberteschi": 0.000,
|
||||
"Medici": 0.522,
|
||||
"Pazzi": 0.000,
|
||||
"Peruzzi": 0.022,
|
||||
"Ridolfi": 0.117,
|
||||
"Salviati": 0.143,
|
||||
"Strozzi": 0.106,
|
||||
"Tornabuoni": 0.090,
|
||||
}
|
||||
for n in sorted(G):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_les_miserables_load(self):
|
||||
G = self.LM
|
||||
c = nx.load_centrality(G)
|
||||
d = {
|
||||
"Napoleon": 0.000,
|
||||
"Myriel": 0.177,
|
||||
"MlleBaptistine": 0.000,
|
||||
"MmeMagloire": 0.000,
|
||||
"CountessDeLo": 0.000,
|
||||
"Geborand": 0.000,
|
||||
"Champtercier": 0.000,
|
||||
"Cravatte": 0.000,
|
||||
"Count": 0.000,
|
||||
"OldMan": 0.000,
|
||||
"Valjean": 0.567,
|
||||
"Labarre": 0.000,
|
||||
"Marguerite": 0.000,
|
||||
"MmeDeR": 0.000,
|
||||
"Isabeau": 0.000,
|
||||
"Gervais": 0.000,
|
||||
"Listolier": 0.000,
|
||||
"Tholomyes": 0.043,
|
||||
"Fameuil": 0.000,
|
||||
"Blacheville": 0.000,
|
||||
"Favourite": 0.000,
|
||||
"Dahlia": 0.000,
|
||||
"Zephine": 0.000,
|
||||
"Fantine": 0.128,
|
||||
"MmeThenardier": 0.029,
|
||||
"Thenardier": 0.075,
|
||||
"Cosette": 0.024,
|
||||
"Javert": 0.054,
|
||||
"Fauchelevent": 0.026,
|
||||
"Bamatabois": 0.008,
|
||||
"Perpetue": 0.000,
|
||||
"Simplice": 0.009,
|
||||
"Scaufflaire": 0.000,
|
||||
"Woman1": 0.000,
|
||||
"Judge": 0.000,
|
||||
"Champmathieu": 0.000,
|
||||
"Brevet": 0.000,
|
||||
"Chenildieu": 0.000,
|
||||
"Cochepaille": 0.000,
|
||||
"Pontmercy": 0.007,
|
||||
"Boulatruelle": 0.000,
|
||||
"Eponine": 0.012,
|
||||
"Anzelma": 0.000,
|
||||
"Woman2": 0.000,
|
||||
"MotherInnocent": 0.000,
|
||||
"Gribier": 0.000,
|
||||
"MmeBurgon": 0.026,
|
||||
"Jondrette": 0.000,
|
||||
"Gavroche": 0.164,
|
||||
"Gillenormand": 0.021,
|
||||
"Magnon": 0.000,
|
||||
"MlleGillenormand": 0.047,
|
||||
"MmePontmercy": 0.000,
|
||||
"MlleVaubois": 0.000,
|
||||
"LtGillenormand": 0.000,
|
||||
"Marius": 0.133,
|
||||
"BaronessT": 0.000,
|
||||
"Mabeuf": 0.028,
|
||||
"Enjolras": 0.041,
|
||||
"Combeferre": 0.001,
|
||||
"Prouvaire": 0.000,
|
||||
"Feuilly": 0.001,
|
||||
"Courfeyrac": 0.006,
|
||||
"Bahorel": 0.002,
|
||||
"Bossuet": 0.032,
|
||||
"Joly": 0.002,
|
||||
"Grantaire": 0.000,
|
||||
"MotherPlutarch": 0.000,
|
||||
"Gueulemer": 0.005,
|
||||
"Babet": 0.005,
|
||||
"Claquesous": 0.005,
|
||||
"Montparnasse": 0.004,
|
||||
"Toussaint": 0.000,
|
||||
"Child1": 0.000,
|
||||
"Child2": 0.000,
|
||||
"Brujon": 0.000,
|
||||
"MmeHucheloup": 0.000,
|
||||
}
|
||||
for n in sorted(G):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_unnormalized_k5_load(self):
|
||||
G = self.K5
|
||||
c = nx.load_centrality(G, normalized=False)
|
||||
d = {0: 0.000, 1: 0.000, 2: 0.000, 3: 0.000, 4: 0.000}
|
||||
for n in sorted(G):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_unnormalized_p3_load(self):
|
||||
G = self.P3
|
||||
c = nx.load_centrality(G, normalized=False)
|
||||
d = {0: 0.000, 1: 2.000, 2: 0.000}
|
||||
for n in sorted(G):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_unnormalized_krackhardt_load(self):
|
||||
G = self.K
|
||||
c = nx.load_centrality(G, normalized=False)
|
||||
d = {
|
||||
0: 1.667,
|
||||
1: 1.667,
|
||||
2: 0.000,
|
||||
3: 7.333,
|
||||
4: 0.000,
|
||||
5: 16.667,
|
||||
6: 16.667,
|
||||
7: 28.000,
|
||||
8: 16.000,
|
||||
9: 0.000,
|
||||
}
|
||||
|
||||
for n in sorted(G):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_unnormalized_florentine_families_load(self):
|
||||
G = self.F
|
||||
c = nx.load_centrality(G, normalized=False)
|
||||
|
||||
d = {
|
||||
"Acciaiuoli": 0.000,
|
||||
"Albizzi": 38.333,
|
||||
"Barbadori": 17.000,
|
||||
"Bischeri": 19.000,
|
||||
"Castellani": 10.000,
|
||||
"Ginori": 0.000,
|
||||
"Guadagni": 45.667,
|
||||
"Lamberteschi": 0.000,
|
||||
"Medici": 95.000,
|
||||
"Pazzi": 0.000,
|
||||
"Peruzzi": 4.000,
|
||||
"Ridolfi": 21.333,
|
||||
"Salviati": 26.000,
|
||||
"Strozzi": 19.333,
|
||||
"Tornabuoni": 16.333,
|
||||
}
|
||||
for n in sorted(G):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_load_betweenness_difference(self):
|
||||
# Difference Between Load and Betweenness
|
||||
# --------------------------------------- The smallest graph
|
||||
# that shows the difference between load and betweenness is
|
||||
# G=ladder_graph(3) (Graph B below)
|
||||
|
||||
# Graph A and B are from Tao Zhou, Jian-Guo Liu, Bing-Hong
|
||||
# Wang: Comment on "Scientific collaboration
|
||||
# networks. II. Shortest paths, weighted networks, and
|
||||
# centrality". https://arxiv.org/pdf/physics/0511084
|
||||
|
||||
# Notice that unlike here, their calculation adds to 1 to the
|
||||
# betweenness of every node i for every path from i to every
|
||||
# other node. This is exactly what it should be, based on
|
||||
# Eqn. (1) in their paper: the eqn is B(v) = \sum_{s\neq t,
|
||||
# s\neq v}{\frac{\sigma_{st}(v)}{\sigma_{st}}}, therefore,
|
||||
# they allow v to be the target node.
|
||||
|
||||
# We follow Brandes 2001, who follows Freeman 1977 that make
|
||||
# the sum for betweenness of v exclude paths where v is either
|
||||
# the source or target node. To agree with their numbers, we
|
||||
# must additionally, remove edge (4,8) from the graph, see AC
|
||||
# example following (there is a mistake in the figure in their
|
||||
# paper - personal communication).
|
||||
|
||||
# A = nx.Graph()
|
||||
# A.add_edges_from([(0,1), (1,2), (1,3), (2,4),
|
||||
# (3,5), (4,6), (4,7), (4,8),
|
||||
# (5,8), (6,9), (7,9), (8,9)])
|
||||
B = nx.Graph() # ladder_graph(3)
|
||||
B.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (4, 5), (3, 5)])
|
||||
c = nx.load_centrality(B, normalized=False)
|
||||
d = {0: 1.750, 1: 1.750, 2: 6.500, 3: 6.500, 4: 1.750, 5: 1.750}
|
||||
for n in sorted(B):
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_c4_edge_load(self):
|
||||
G = self.C4
|
||||
c = nx.edge_load_centrality(G)
|
||||
d = {(0, 1): 6.000, (0, 3): 6.000, (1, 2): 6.000, (2, 3): 6.000}
|
||||
for n in G.edges():
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_p4_edge_load(self):
|
||||
G = self.P4
|
||||
c = nx.edge_load_centrality(G)
|
||||
d = {(0, 1): 6.000, (1, 2): 8.000, (2, 3): 6.000}
|
||||
for n in G.edges():
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_k5_edge_load(self):
|
||||
G = self.K5
|
||||
c = nx.edge_load_centrality(G)
|
||||
d = {
|
||||
(0, 1): 5.000,
|
||||
(0, 2): 5.000,
|
||||
(0, 3): 5.000,
|
||||
(0, 4): 5.000,
|
||||
(1, 2): 5.000,
|
||||
(1, 3): 5.000,
|
||||
(1, 4): 5.000,
|
||||
(2, 3): 5.000,
|
||||
(2, 4): 5.000,
|
||||
(3, 4): 5.000,
|
||||
}
|
||||
for n in G.edges():
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
|
||||
def test_tree_edge_load(self):
|
||||
G = self.T
|
||||
c = nx.edge_load_centrality(G)
|
||||
d = {
|
||||
(0, 1): 24.000,
|
||||
(0, 2): 24.000,
|
||||
(1, 3): 12.000,
|
||||
(1, 4): 12.000,
|
||||
(2, 5): 12.000,
|
||||
(2, 6): 12.000,
|
||||
}
|
||||
for n in G.edges():
|
||||
assert c[n] == pytest.approx(d[n], abs=1e-3)
|
||||
@@ -0,0 +1,87 @@
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def example1a_G():
|
||||
G = nx.Graph()
|
||||
G.add_node(1, percolation=0.1)
|
||||
G.add_node(2, percolation=0.2)
|
||||
G.add_node(3, percolation=0.2)
|
||||
G.add_node(4, percolation=0.2)
|
||||
G.add_node(5, percolation=0.3)
|
||||
G.add_node(6, percolation=0.2)
|
||||
G.add_node(7, percolation=0.5)
|
||||
G.add_node(8, percolation=0.5)
|
||||
G.add_edges_from([(1, 4), (2, 4), (3, 4), (4, 5), (5, 6), (6, 7), (6, 8)])
|
||||
return G
|
||||
|
||||
|
||||
def example1b_G():
|
||||
G = nx.Graph()
|
||||
G.add_node(1, percolation=0.3)
|
||||
G.add_node(2, percolation=0.5)
|
||||
G.add_node(3, percolation=0.5)
|
||||
G.add_node(4, percolation=0.2)
|
||||
G.add_node(5, percolation=0.3)
|
||||
G.add_node(6, percolation=0.2)
|
||||
G.add_node(7, percolation=0.1)
|
||||
G.add_node(8, percolation=0.1)
|
||||
G.add_edges_from([(1, 4), (2, 4), (3, 4), (4, 5), (5, 6), (6, 7), (6, 8)])
|
||||
return G
|
||||
|
||||
|
||||
def test_percolation_example1a():
|
||||
"""percolation centrality: example 1a"""
|
||||
G = example1a_G()
|
||||
p = nx.percolation_centrality(G)
|
||||
p_answer = {4: 0.625, 6: 0.667}
|
||||
for n, k in p_answer.items():
|
||||
assert p[n] == pytest.approx(k, abs=1e-3)
|
||||
|
||||
|
||||
def test_percolation_example1b():
|
||||
"""percolation centrality: example 1a"""
|
||||
G = example1b_G()
|
||||
p = nx.percolation_centrality(G)
|
||||
p_answer = {4: 0.825, 6: 0.4}
|
||||
for n, k in p_answer.items():
|
||||
assert p[n] == pytest.approx(k, abs=1e-3)
|
||||
|
||||
|
||||
def test_converge_to_betweenness():
|
||||
"""percolation centrality: should converge to betweenness
|
||||
centrality when all nodes are percolated the same"""
|
||||
# taken from betweenness test test_florentine_families_graph
|
||||
G = nx.florentine_families_graph()
|
||||
b_answer = {
|
||||
"Acciaiuoli": 0.000,
|
||||
"Albizzi": 0.212,
|
||||
"Barbadori": 0.093,
|
||||
"Bischeri": 0.104,
|
||||
"Castellani": 0.055,
|
||||
"Ginori": 0.000,
|
||||
"Guadagni": 0.255,
|
||||
"Lamberteschi": 0.000,
|
||||
"Medici": 0.522,
|
||||
"Pazzi": 0.000,
|
||||
"Peruzzi": 0.022,
|
||||
"Ridolfi": 0.114,
|
||||
"Salviati": 0.143,
|
||||
"Strozzi": 0.103,
|
||||
"Tornabuoni": 0.092,
|
||||
}
|
||||
|
||||
# If no initial state is provided, state for
|
||||
# every node defaults to 1
|
||||
p_answer = nx.percolation_centrality(G)
|
||||
assert p_answer == pytest.approx(b_answer, abs=1e-3)
|
||||
|
||||
p_states = {k: 0.3 for k, v in b_answer.items()}
|
||||
p_answer = nx.percolation_centrality(G, states=p_states)
|
||||
assert p_answer == pytest.approx(b_answer, abs=1e-3)
|
||||
|
||||
|
||||
def test_default_percolation():
|
||||
G = nx.erdos_renyi_graph(42, 0.42, seed=42)
|
||||
assert nx.percolation_centrality(G) == pytest.approx(nx.betweenness_centrality(G))
|
||||
117
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_reaching.py
vendored
Normal file
117
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_reaching.py
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
"""Unit tests for the :mod:`networkx.algorithms.centrality.reaching` module."""
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestGlobalReachingCentrality:
|
||||
"""Unit tests for the global reaching centrality function."""
|
||||
|
||||
def test_non_positive_weights(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.DiGraph()
|
||||
nx.global_reaching_centrality(G, weight="weight")
|
||||
|
||||
def test_negatively_weighted(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from([(0, 1, -2), (1, 2, +1)])
|
||||
nx.global_reaching_centrality(G, weight="weight")
|
||||
|
||||
def test_directed_star(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_weighted_edges_from([(1, 2, 0.5), (1, 3, 0.5)])
|
||||
grc = nx.global_reaching_centrality
|
||||
assert grc(G, normalized=False, weight="weight") == 0.5
|
||||
assert grc(G) == 1
|
||||
|
||||
def test_undirected_unweighted_star(self):
|
||||
G = nx.star_graph(2)
|
||||
grc = nx.global_reaching_centrality
|
||||
assert grc(G, normalized=False, weight=None) == 0.25
|
||||
|
||||
def test_undirected_weighted_star(self):
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from([(1, 2, 1), (1, 3, 2)])
|
||||
grc = nx.global_reaching_centrality
|
||||
assert grc(G, normalized=False, weight="weight") == 0.375
|
||||
|
||||
def test_cycle_directed_unweighted(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(1, 2)
|
||||
G.add_edge(2, 1)
|
||||
assert nx.global_reaching_centrality(G, weight=None) == 0
|
||||
|
||||
def test_cycle_undirected_unweighted(self):
|
||||
G = nx.Graph()
|
||||
G.add_edge(1, 2)
|
||||
assert nx.global_reaching_centrality(G, weight=None) == 0
|
||||
|
||||
def test_cycle_directed_weighted(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_weighted_edges_from([(1, 2, 1), (2, 1, 1)])
|
||||
assert nx.global_reaching_centrality(G) == 0
|
||||
|
||||
def test_cycle_undirected_weighted(self):
|
||||
G = nx.Graph()
|
||||
G.add_edge(1, 2, weight=1)
|
||||
grc = nx.global_reaching_centrality
|
||||
assert grc(G, normalized=False) == 0
|
||||
|
||||
def test_directed_weighted(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("A", "B", weight=5)
|
||||
G.add_edge("B", "C", weight=1)
|
||||
G.add_edge("B", "D", weight=0.25)
|
||||
G.add_edge("D", "E", weight=1)
|
||||
|
||||
denom = len(G) - 1
|
||||
A_local = sum([5, 3, 2.625, 2.0833333333333]) / denom
|
||||
B_local = sum([1, 0.25, 0.625]) / denom
|
||||
C_local = 0
|
||||
D_local = sum([1]) / denom
|
||||
E_local = 0
|
||||
|
||||
local_reach_ctrs = [A_local, C_local, B_local, D_local, E_local]
|
||||
max_local = max(local_reach_ctrs)
|
||||
expected = sum(max_local - lrc for lrc in local_reach_ctrs) / denom
|
||||
grc = nx.global_reaching_centrality
|
||||
actual = grc(G, normalized=False, weight="weight")
|
||||
assert expected == pytest.approx(actual, abs=1e-7)
|
||||
|
||||
|
||||
class TestLocalReachingCentrality:
|
||||
"""Unit tests for the local reaching centrality function."""
|
||||
|
||||
def test_non_positive_weights(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.DiGraph()
|
||||
G.add_weighted_edges_from([(0, 1, 0)])
|
||||
nx.local_reaching_centrality(G, 0, weight="weight")
|
||||
|
||||
def test_negatively_weighted(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from([(0, 1, -2), (1, 2, +1)])
|
||||
nx.local_reaching_centrality(G, 0, weight="weight")
|
||||
|
||||
def test_undirected_unweighted_star(self):
|
||||
G = nx.star_graph(2)
|
||||
grc = nx.local_reaching_centrality
|
||||
assert grc(G, 1, weight=None, normalized=False) == 0.75
|
||||
|
||||
def test_undirected_weighted_star(self):
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from([(1, 2, 1), (1, 3, 2)])
|
||||
centrality = nx.local_reaching_centrality(
|
||||
G, 1, normalized=False, weight="weight"
|
||||
)
|
||||
assert centrality == 1.5
|
||||
|
||||
def test_undirected_weighted_normalized(self):
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from([(1, 2, 1), (1, 3, 2)])
|
||||
centrality = nx.local_reaching_centrality(
|
||||
G, 1, normalized=True, weight="weight"
|
||||
)
|
||||
assert centrality == 1.0
|
||||
@@ -0,0 +1,82 @@
|
||||
"""
|
||||
Tests for second order centrality.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def test_empty():
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
G = nx.empty_graph()
|
||||
nx.second_order_centrality(G)
|
||||
|
||||
|
||||
def test_non_connected():
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
G = nx.Graph()
|
||||
G.add_node(0)
|
||||
G.add_node(1)
|
||||
nx.second_order_centrality(G)
|
||||
|
||||
|
||||
def test_non_negative_edge_weights():
|
||||
with pytest.raises(nx.NetworkXException):
|
||||
G = nx.path_graph(2)
|
||||
G.add_edge(0, 1, weight=-1)
|
||||
nx.second_order_centrality(G)
|
||||
|
||||
|
||||
def test_weight_attribute():
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from([(0, 1, 1.0), (1, 2, 3.5)], weight="w")
|
||||
expected = {0: 3.431, 1: 3.082, 2: 5.612}
|
||||
b = nx.second_order_centrality(G, weight="w")
|
||||
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(expected[n], abs=1e-2)
|
||||
|
||||
|
||||
def test_one_node_graph():
|
||||
"""Second order centrality: single node"""
|
||||
G = nx.Graph()
|
||||
G.add_node(0)
|
||||
G.add_edge(0, 0)
|
||||
assert nx.second_order_centrality(G)[0] == 0
|
||||
|
||||
|
||||
def test_P3():
|
||||
"""Second order centrality: line graph, as defined in paper"""
|
||||
G = nx.path_graph(3)
|
||||
b_answer = {0: 3.741, 1: 1.414, 2: 3.741}
|
||||
|
||||
b = nx.second_order_centrality(G)
|
||||
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-2)
|
||||
|
||||
|
||||
def test_K3():
|
||||
"""Second order centrality: complete graph, as defined in paper"""
|
||||
G = nx.complete_graph(3)
|
||||
b_answer = {0: 1.414, 1: 1.414, 2: 1.414}
|
||||
|
||||
b = nx.second_order_centrality(G)
|
||||
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-2)
|
||||
|
||||
|
||||
def test_ring_graph():
|
||||
"""Second order centrality: ring graph, as defined in paper"""
|
||||
G = nx.cycle_graph(5)
|
||||
b_answer = {0: 4.472, 1: 4.472, 2: 4.472, 3: 4.472, 4: 4.472}
|
||||
|
||||
b = nx.second_order_centrality(G)
|
||||
|
||||
for n in sorted(G):
|
||||
assert b[n] == pytest.approx(b_answer[n], abs=1e-2)
|
||||
110
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_subgraph.py
vendored
Normal file
110
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_subgraph.py
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
import pytest
|
||||
|
||||
pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms.centrality.subgraph_alg import (
|
||||
communicability_betweenness_centrality,
|
||||
estrada_index,
|
||||
subgraph_centrality,
|
||||
subgraph_centrality_exp,
|
||||
)
|
||||
|
||||
|
||||
class TestSubgraph:
|
||||
def test_subgraph_centrality(self):
|
||||
answer = {0: 1.5430806348152433, 1: 1.5430806348152433}
|
||||
result = subgraph_centrality(nx.path_graph(2))
|
||||
for k, v in result.items():
|
||||
assert answer[k] == pytest.approx(v, abs=1e-7)
|
||||
|
||||
answer1 = {
|
||||
"1": 1.6445956054135658,
|
||||
"Albert": 2.4368257358712189,
|
||||
"Aric": 2.4368257358712193,
|
||||
"Dan": 3.1306328496328168,
|
||||
"Franck": 2.3876142275231915,
|
||||
}
|
||||
G1 = nx.Graph(
|
||||
[
|
||||
("Franck", "Aric"),
|
||||
("Aric", "Dan"),
|
||||
("Dan", "Albert"),
|
||||
("Albert", "Franck"),
|
||||
("Dan", "1"),
|
||||
("Franck", "Albert"),
|
||||
]
|
||||
)
|
||||
result1 = subgraph_centrality(G1)
|
||||
for k, v in result1.items():
|
||||
assert answer1[k] == pytest.approx(v, abs=1e-7)
|
||||
result1 = subgraph_centrality_exp(G1)
|
||||
for k, v in result1.items():
|
||||
assert answer1[k] == pytest.approx(v, abs=1e-7)
|
||||
|
||||
def test_subgraph_centrality_big_graph(self):
|
||||
g199 = nx.complete_graph(199)
|
||||
g200 = nx.complete_graph(200)
|
||||
|
||||
comm199 = nx.subgraph_centrality(g199)
|
||||
comm199_exp = nx.subgraph_centrality_exp(g199)
|
||||
|
||||
comm200 = nx.subgraph_centrality(g200)
|
||||
comm200_exp = nx.subgraph_centrality_exp(g200)
|
||||
|
||||
def test_communicability_betweenness_centrality_small(self):
|
||||
result = communicability_betweenness_centrality(nx.path_graph(2))
|
||||
assert result == {0: 0, 1: 0}
|
||||
|
||||
result = communicability_betweenness_centrality(nx.path_graph(1))
|
||||
assert result == {0: 0}
|
||||
|
||||
result = communicability_betweenness_centrality(nx.path_graph(0))
|
||||
assert result == {}
|
||||
|
||||
answer = {0: 0.1411224421177313, 1: 1.0, 2: 0.1411224421177313}
|
||||
result = communicability_betweenness_centrality(nx.path_graph(3))
|
||||
for k, v in result.items():
|
||||
assert answer[k] == pytest.approx(v, abs=1e-7)
|
||||
|
||||
result = communicability_betweenness_centrality(nx.complete_graph(3))
|
||||
for k, v in result.items():
|
||||
assert 0.49786143366223296 == pytest.approx(v, abs=1e-7)
|
||||
|
||||
def test_communicability_betweenness_centrality(self):
|
||||
answer = {
|
||||
0: 0.07017447951484615,
|
||||
1: 0.71565598701107991,
|
||||
2: 0.71565598701107991,
|
||||
3: 0.07017447951484615,
|
||||
}
|
||||
result = communicability_betweenness_centrality(nx.path_graph(4))
|
||||
for k, v in result.items():
|
||||
assert answer[k] == pytest.approx(v, abs=1e-7)
|
||||
|
||||
answer1 = {
|
||||
"1": 0.060039074193949521,
|
||||
"Albert": 0.315470761661372,
|
||||
"Aric": 0.31547076166137211,
|
||||
"Dan": 0.68297778678316201,
|
||||
"Franck": 0.21977926617449497,
|
||||
}
|
||||
G1 = nx.Graph(
|
||||
[
|
||||
("Franck", "Aric"),
|
||||
("Aric", "Dan"),
|
||||
("Dan", "Albert"),
|
||||
("Albert", "Franck"),
|
||||
("Dan", "1"),
|
||||
("Franck", "Albert"),
|
||||
]
|
||||
)
|
||||
result1 = communicability_betweenness_centrality(G1)
|
||||
for k, v in result1.items():
|
||||
assert answer1[k] == pytest.approx(v, abs=1e-7)
|
||||
|
||||
def test_estrada_index(self):
|
||||
answer = 1041.2470334195475
|
||||
result = estrada_index(nx.karate_club_graph())
|
||||
assert answer == pytest.approx(result, abs=1e-7)
|
||||
302
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_trophic.py
vendored
Normal file
302
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_trophic.py
vendored
Normal file
@@ -0,0 +1,302 @@
|
||||
"""Test trophic levels, trophic differences and trophic coherence
|
||||
"""
|
||||
import pytest
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def test_trophic_levels():
|
||||
"""Trivial example"""
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("a", "b")
|
||||
G.add_edge("b", "c")
|
||||
|
||||
d = nx.trophic_levels(G)
|
||||
assert d == {"a": 1, "b": 2, "c": 3}
|
||||
|
||||
|
||||
def test_trophic_levels_levine():
|
||||
"""Example from Figure 5 in Stephen Levine (1980) J. theor. Biol. 83,
|
||||
195-207
|
||||
"""
|
||||
S = nx.DiGraph()
|
||||
S.add_edge(1, 2, weight=1.0)
|
||||
S.add_edge(1, 3, weight=0.2)
|
||||
S.add_edge(1, 4, weight=0.8)
|
||||
S.add_edge(2, 3, weight=0.2)
|
||||
S.add_edge(2, 5, weight=0.3)
|
||||
S.add_edge(4, 3, weight=0.6)
|
||||
S.add_edge(4, 5, weight=0.7)
|
||||
S.add_edge(5, 4, weight=0.2)
|
||||
|
||||
# save copy for later, test intermediate implementation details first
|
||||
S2 = S.copy()
|
||||
|
||||
# drop nodes of in-degree zero
|
||||
z = [nid for nid, d in S.in_degree if d == 0]
|
||||
for nid in z:
|
||||
S.remove_node(nid)
|
||||
|
||||
# find adjacency matrix
|
||||
q = nx.linalg.graphmatrix.adjacency_matrix(S).T
|
||||
|
||||
# fmt: off
|
||||
expected_q = np.array([
|
||||
[0, 0, 0., 0],
|
||||
[0.2, 0, 0.6, 0],
|
||||
[0, 0, 0, 0.2],
|
||||
[0.3, 0, 0.7, 0]
|
||||
])
|
||||
# fmt: on
|
||||
assert np.array_equal(q.todense(), expected_q)
|
||||
|
||||
# must be square, size of number of nodes
|
||||
assert len(q.shape) == 2
|
||||
assert q.shape[0] == q.shape[1]
|
||||
assert q.shape[0] == len(S)
|
||||
|
||||
nn = q.shape[0]
|
||||
|
||||
i = np.eye(nn)
|
||||
n = np.linalg.inv(i - q)
|
||||
y = np.asarray(n) @ np.ones(nn)
|
||||
|
||||
expected_y = np.array([1, 2.07906977, 1.46511628, 2.3255814])
|
||||
assert np.allclose(y, expected_y)
|
||||
|
||||
expected_d = {1: 1, 2: 2, 3: 3.07906977, 4: 2.46511628, 5: 3.3255814}
|
||||
|
||||
d = nx.trophic_levels(S2)
|
||||
|
||||
for nid, level in d.items():
|
||||
expected_level = expected_d[nid]
|
||||
assert expected_level == pytest.approx(level, abs=1e-7)
|
||||
|
||||
|
||||
def test_trophic_levels_simple():
|
||||
matrix_a = np.array([[0, 0], [1, 0]])
|
||||
G = nx.from_numpy_array(matrix_a, create_using=nx.DiGraph)
|
||||
d = nx.trophic_levels(G)
|
||||
assert d[0] == pytest.approx(2, abs=1e-7)
|
||||
assert d[1] == pytest.approx(1, abs=1e-7)
|
||||
|
||||
|
||||
def test_trophic_levels_more_complex():
|
||||
# fmt: off
|
||||
matrix = np.array([
|
||||
[0, 1, 0, 0],
|
||||
[0, 0, 1, 0],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0]
|
||||
])
|
||||
# fmt: on
|
||||
G = nx.from_numpy_array(matrix, create_using=nx.DiGraph)
|
||||
d = nx.trophic_levels(G)
|
||||
expected_result = [1, 2, 3, 4]
|
||||
for ind in range(4):
|
||||
assert d[ind] == pytest.approx(expected_result[ind], abs=1e-7)
|
||||
|
||||
# fmt: off
|
||||
matrix = np.array([
|
||||
[0, 1, 1, 0],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0]
|
||||
])
|
||||
# fmt: on
|
||||
G = nx.from_numpy_array(matrix, create_using=nx.DiGraph)
|
||||
d = nx.trophic_levels(G)
|
||||
|
||||
expected_result = [1, 2, 2.5, 3.25]
|
||||
print("Calculated result: ", d)
|
||||
print("Expected Result: ", expected_result)
|
||||
|
||||
for ind in range(4):
|
||||
assert d[ind] == pytest.approx(expected_result[ind], abs=1e-7)
|
||||
|
||||
|
||||
def test_trophic_levels_even_more_complex():
|
||||
# fmt: off
|
||||
# Another, bigger matrix
|
||||
matrix = np.array([
|
||||
[0, 0, 0, 0, 0],
|
||||
[0, 1, 0, 1, 0],
|
||||
[1, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0]
|
||||
])
|
||||
# Generated this linear system using pen and paper:
|
||||
K = np.array([
|
||||
[1, 0, -1, 0, 0],
|
||||
[0, 0.5, 0, -0.5, 0],
|
||||
[0, 0, 1, 0, 0],
|
||||
[0, -0.5, 0, 1, -0.5],
|
||||
[0, 0, 0, 0, 1],
|
||||
])
|
||||
# fmt: on
|
||||
result_1 = np.ravel(np.linalg.inv(K) @ np.ones(5))
|
||||
G = nx.from_numpy_array(matrix, create_using=nx.DiGraph)
|
||||
result_2 = nx.trophic_levels(G)
|
||||
|
||||
for ind in range(5):
|
||||
assert result_1[ind] == pytest.approx(result_2[ind], abs=1e-7)
|
||||
|
||||
|
||||
def test_trophic_levels_singular_matrix():
|
||||
"""Should raise an error with graphs with only non-basal nodes"""
|
||||
matrix = np.identity(4)
|
||||
G = nx.from_numpy_array(matrix, create_using=nx.DiGraph)
|
||||
with pytest.raises(nx.NetworkXError) as e:
|
||||
nx.trophic_levels(G)
|
||||
msg = (
|
||||
"Trophic levels are only defined for graphs where every node "
|
||||
+ "has a path from a basal node (basal nodes are nodes with no "
|
||||
+ "incoming edges)."
|
||||
)
|
||||
assert msg in str(e.value)
|
||||
|
||||
|
||||
def test_trophic_levels_singular_with_basal():
|
||||
"""Should fail to compute if there are any parts of the graph which are not
|
||||
reachable from any basal node (with in-degree zero).
|
||||
"""
|
||||
G = nx.DiGraph()
|
||||
# a has in-degree zero
|
||||
G.add_edge("a", "b")
|
||||
|
||||
# b is one level above a, c and d
|
||||
G.add_edge("c", "b")
|
||||
G.add_edge("d", "b")
|
||||
|
||||
# c and d form a loop, neither are reachable from a
|
||||
G.add_edge("c", "d")
|
||||
G.add_edge("d", "c")
|
||||
|
||||
with pytest.raises(nx.NetworkXError) as e:
|
||||
nx.trophic_levels(G)
|
||||
msg = (
|
||||
"Trophic levels are only defined for graphs where every node "
|
||||
+ "has a path from a basal node (basal nodes are nodes with no "
|
||||
+ "incoming edges)."
|
||||
)
|
||||
assert msg in str(e.value)
|
||||
|
||||
# if self-loops are allowed, smaller example:
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("a", "b") # a has in-degree zero
|
||||
G.add_edge("c", "b") # b is one level above a and c
|
||||
G.add_edge("c", "c") # c has a self-loop
|
||||
with pytest.raises(nx.NetworkXError) as e:
|
||||
nx.trophic_levels(G)
|
||||
msg = (
|
||||
"Trophic levels are only defined for graphs where every node "
|
||||
+ "has a path from a basal node (basal nodes are nodes with no "
|
||||
+ "incoming edges)."
|
||||
)
|
||||
assert msg in str(e.value)
|
||||
|
||||
|
||||
def test_trophic_differences():
|
||||
matrix_a = np.array([[0, 1], [0, 0]])
|
||||
G = nx.from_numpy_array(matrix_a, create_using=nx.DiGraph)
|
||||
diffs = nx.trophic_differences(G)
|
||||
assert diffs[(0, 1)] == pytest.approx(1, abs=1e-7)
|
||||
|
||||
# fmt: off
|
||||
matrix_b = np.array([
|
||||
[0, 1, 1, 0],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0]
|
||||
])
|
||||
# fmt: on
|
||||
G = nx.from_numpy_array(matrix_b, create_using=nx.DiGraph)
|
||||
diffs = nx.trophic_differences(G)
|
||||
|
||||
assert diffs[(0, 1)] == pytest.approx(1, abs=1e-7)
|
||||
assert diffs[(0, 2)] == pytest.approx(1.5, abs=1e-7)
|
||||
assert diffs[(1, 2)] == pytest.approx(0.5, abs=1e-7)
|
||||
assert diffs[(1, 3)] == pytest.approx(1.25, abs=1e-7)
|
||||
assert diffs[(2, 3)] == pytest.approx(0.75, abs=1e-7)
|
||||
|
||||
|
||||
def test_trophic_incoherence_parameter_no_cannibalism():
|
||||
matrix_a = np.array([[0, 1], [0, 0]])
|
||||
G = nx.from_numpy_array(matrix_a, create_using=nx.DiGraph)
|
||||
q = nx.trophic_incoherence_parameter(G, cannibalism=False)
|
||||
assert q == pytest.approx(0, abs=1e-7)
|
||||
|
||||
# fmt: off
|
||||
matrix_b = np.array([
|
||||
[0, 1, 1, 0],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0]
|
||||
])
|
||||
# fmt: on
|
||||
G = nx.from_numpy_array(matrix_b, create_using=nx.DiGraph)
|
||||
q = nx.trophic_incoherence_parameter(G, cannibalism=False)
|
||||
assert q == pytest.approx(np.std([1, 1.5, 0.5, 0.75, 1.25]), abs=1e-7)
|
||||
|
||||
# fmt: off
|
||||
matrix_c = np.array([
|
||||
[0, 1, 1, 0],
|
||||
[0, 1, 1, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 1]
|
||||
])
|
||||
# fmt: on
|
||||
G = nx.from_numpy_array(matrix_c, create_using=nx.DiGraph)
|
||||
q = nx.trophic_incoherence_parameter(G, cannibalism=False)
|
||||
# Ignore the -link
|
||||
assert q == pytest.approx(np.std([1, 1.5, 0.5, 0.75, 1.25]), abs=1e-7)
|
||||
|
||||
# no self-loops case
|
||||
# fmt: off
|
||||
matrix_d = np.array([
|
||||
[0, 1, 1, 0],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0]
|
||||
])
|
||||
# fmt: on
|
||||
G = nx.from_numpy_array(matrix_d, create_using=nx.DiGraph)
|
||||
q = nx.trophic_incoherence_parameter(G, cannibalism=False)
|
||||
# Ignore the -link
|
||||
assert q == pytest.approx(np.std([1, 1.5, 0.5, 0.75, 1.25]), abs=1e-7)
|
||||
|
||||
|
||||
def test_trophic_incoherence_parameter_cannibalism():
|
||||
matrix_a = np.array([[0, 1], [0, 0]])
|
||||
G = nx.from_numpy_array(matrix_a, create_using=nx.DiGraph)
|
||||
q = nx.trophic_incoherence_parameter(G, cannibalism=True)
|
||||
assert q == pytest.approx(0, abs=1e-7)
|
||||
|
||||
# fmt: off
|
||||
matrix_b = np.array([
|
||||
[0, 0, 0, 0, 0],
|
||||
[0, 1, 0, 1, 0],
|
||||
[1, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0]
|
||||
])
|
||||
# fmt: on
|
||||
G = nx.from_numpy_array(matrix_b, create_using=nx.DiGraph)
|
||||
q = nx.trophic_incoherence_parameter(G, cannibalism=True)
|
||||
assert q == pytest.approx(2, abs=1e-7)
|
||||
|
||||
# fmt: off
|
||||
matrix_c = np.array([
|
||||
[0, 1, 1, 0],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0]
|
||||
])
|
||||
# fmt: on
|
||||
G = nx.from_numpy_array(matrix_c, create_using=nx.DiGraph)
|
||||
q = nx.trophic_incoherence_parameter(G, cannibalism=True)
|
||||
# Ignore the -link
|
||||
assert q == pytest.approx(np.std([1, 1.5, 0.5, 0.75, 1.25]), abs=1e-7)
|
||||
65
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_voterank.py
vendored
Normal file
65
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/tests/test_voterank.py
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
Unit tests for VoteRank.
|
||||
"""
|
||||
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestVoteRankCentrality:
|
||||
# Example Graph present in reference paper
|
||||
def test_voterank_centrality_1(self):
|
||||
G = nx.Graph()
|
||||
G.add_edges_from(
|
||||
[
|
||||
(7, 8),
|
||||
(7, 5),
|
||||
(7, 9),
|
||||
(5, 0),
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(0, 3),
|
||||
(0, 4),
|
||||
(1, 6),
|
||||
(2, 6),
|
||||
(3, 6),
|
||||
(4, 6),
|
||||
]
|
||||
)
|
||||
assert [0, 7, 6] == nx.voterank(G)
|
||||
|
||||
def test_voterank_emptygraph(self):
|
||||
G = nx.Graph()
|
||||
assert [] == nx.voterank(G)
|
||||
|
||||
# Graph unit test
|
||||
def test_voterank_centrality_2(self):
|
||||
G = nx.florentine_families_graph()
|
||||
d = nx.voterank(G, 4)
|
||||
exact = ["Medici", "Strozzi", "Guadagni", "Castellani"]
|
||||
assert exact == d
|
||||
|
||||
# DiGraph unit test
|
||||
def test_voterank_centrality_3(self):
|
||||
G = nx.gnc_graph(10, seed=7)
|
||||
d = nx.voterank(G, 4)
|
||||
exact = [3, 6, 8]
|
||||
assert exact == d
|
||||
|
||||
# MultiGraph unit test
|
||||
def test_voterank_centrality_4(self):
|
||||
G = nx.MultiGraph()
|
||||
G.add_edges_from(
|
||||
[(0, 1), (0, 1), (1, 2), (2, 5), (2, 5), (5, 6), (5, 6), (2, 4), (4, 3)]
|
||||
)
|
||||
exact = [2, 1, 5, 4]
|
||||
assert exact == nx.voterank(G)
|
||||
|
||||
# MultiDiGraph unit test
|
||||
def test_voterank_centrality_5(self):
|
||||
G = nx.MultiDiGraph()
|
||||
G.add_edges_from(
|
||||
[(0, 1), (0, 1), (1, 2), (2, 5), (2, 5), (5, 6), (5, 6), (2, 4), (4, 3)]
|
||||
)
|
||||
exact = [2, 0, 5, 4]
|
||||
assert exact == nx.voterank(G)
|
||||
162
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/trophic.py
vendored
Normal file
162
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/trophic.py
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
"""Trophic levels"""
|
||||
import networkx as nx
|
||||
from networkx.utils import not_implemented_for
|
||||
|
||||
__all__ = ["trophic_levels", "trophic_differences", "trophic_incoherence_parameter"]
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def trophic_levels(G, weight="weight"):
|
||||
r"""Compute the trophic levels of nodes.
|
||||
|
||||
The trophic level of a node $i$ is
|
||||
|
||||
.. math::
|
||||
|
||||
s_i = 1 + \frac{1}{k^{in}_i} \sum_{j} a_{ij} s_j
|
||||
|
||||
where $k^{in}_i$ is the in-degree of i
|
||||
|
||||
.. math::
|
||||
|
||||
k^{in}_i = \sum_{j} a_{ij}
|
||||
|
||||
and nodes with $k^{in}_i = 0$ have $s_i = 1$ by convention.
|
||||
|
||||
These are calculated using the method outlined in Levine [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : DiGraph
|
||||
A directed networkx graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : dict
|
||||
Dictionary of nodes with trophic level as the value.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Stephen Levine (1980) J. theor. Biol. 83, 195-207
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
# find adjacency matrix
|
||||
a = nx.adjacency_matrix(G, weight=weight).T.toarray()
|
||||
|
||||
# drop rows/columns where in-degree is zero
|
||||
rowsum = np.sum(a, axis=1)
|
||||
p = a[rowsum != 0][:, rowsum != 0]
|
||||
# normalise so sum of in-degree weights is 1 along each row
|
||||
p = p / rowsum[rowsum != 0][:, np.newaxis]
|
||||
|
||||
# calculate trophic levels
|
||||
nn = p.shape[0]
|
||||
i = np.eye(nn)
|
||||
try:
|
||||
n = np.linalg.inv(i - p)
|
||||
except np.linalg.LinAlgError as err:
|
||||
# LinAlgError is raised when there is a non-basal node
|
||||
msg = (
|
||||
"Trophic levels are only defined for graphs where every "
|
||||
+ "node has a path from a basal node (basal nodes are nodes "
|
||||
+ "with no incoming edges)."
|
||||
)
|
||||
raise nx.NetworkXError(msg) from err
|
||||
y = n.sum(axis=1) + 1
|
||||
|
||||
levels = {}
|
||||
|
||||
# all nodes with in-degree zero have trophic level == 1
|
||||
zero_node_ids = (node_id for node_id, degree in G.in_degree if degree == 0)
|
||||
for node_id in zero_node_ids:
|
||||
levels[node_id] = 1
|
||||
|
||||
# all other nodes have levels as calculated
|
||||
nonzero_node_ids = (node_id for node_id, degree in G.in_degree if degree != 0)
|
||||
for i, node_id in enumerate(nonzero_node_ids):
|
||||
levels[node_id] = y.item(i)
|
||||
|
||||
return levels
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def trophic_differences(G, weight="weight"):
|
||||
r"""Compute the trophic differences of the edges of a directed graph.
|
||||
|
||||
The trophic difference $x_ij$ for each edge is defined in Johnson et al.
|
||||
[1]_ as:
|
||||
|
||||
.. math::
|
||||
x_ij = s_j - s_i
|
||||
|
||||
Where $s_i$ is the trophic level of node $i$.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : DiGraph
|
||||
A directed networkx graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
diffs : dict
|
||||
Dictionary of edges with trophic differences as the value.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Samuel Johnson, Virginia Dominguez-Garcia, Luca Donetti, Miguel A.
|
||||
Munoz (2014) PNAS "Trophic coherence determines food-web stability"
|
||||
"""
|
||||
levels = trophic_levels(G, weight=weight)
|
||||
diffs = {}
|
||||
for u, v in G.edges:
|
||||
diffs[(u, v)] = levels[v] - levels[u]
|
||||
return diffs
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
@nx._dispatchable(edge_attrs="weight")
|
||||
def trophic_incoherence_parameter(G, weight="weight", cannibalism=False):
|
||||
r"""Compute the trophic incoherence parameter of a graph.
|
||||
|
||||
Trophic coherence is defined as the homogeneity of the distribution of
|
||||
trophic distances: the more similar, the more coherent. This is measured by
|
||||
the standard deviation of the trophic differences and referred to as the
|
||||
trophic incoherence parameter $q$ by [1].
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : DiGraph
|
||||
A directed networkx graph
|
||||
|
||||
cannibalism: Boolean
|
||||
If set to False, self edges are not considered in the calculation
|
||||
|
||||
Returns
|
||||
-------
|
||||
trophic_incoherence_parameter : float
|
||||
The trophic coherence of a graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Samuel Johnson, Virginia Dominguez-Garcia, Luca Donetti, Miguel A.
|
||||
Munoz (2014) PNAS "Trophic coherence determines food-web stability"
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
if cannibalism:
|
||||
diffs = trophic_differences(G, weight=weight)
|
||||
else:
|
||||
# If no cannibalism, remove self-edges
|
||||
self_loops = list(nx.selfloop_edges(G))
|
||||
if self_loops:
|
||||
# Make a copy so we do not change G's edges in memory
|
||||
G_2 = G.copy()
|
||||
G_2.remove_edges_from(self_loops)
|
||||
else:
|
||||
# Avoid copy otherwise
|
||||
G_2 = G
|
||||
diffs = trophic_differences(G_2, weight=weight)
|
||||
return float(np.std(list(diffs.values())))
|
||||
94
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/voterank_alg.py
vendored
Normal file
94
.CondaPkg/env/Lib/site-packages/networkx/algorithms/centrality/voterank_alg.py
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
"""Algorithm to select influential nodes in a graph using VoteRank."""
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["voterank"]
|
||||
|
||||
|
||||
@nx._dispatchable
|
||||
def voterank(G, number_of_nodes=None):
|
||||
"""Select a list of influential nodes in a graph using VoteRank algorithm
|
||||
|
||||
VoteRank [1]_ computes a ranking of the nodes in a graph G based on a
|
||||
voting scheme. With VoteRank, all nodes vote for each of its in-neighbors
|
||||
and the node with the highest votes is elected iteratively. The voting
|
||||
ability of out-neighbors of elected nodes is decreased in subsequent turns.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph.
|
||||
|
||||
number_of_nodes : integer, optional
|
||||
Number of ranked nodes to extract (default all nodes).
|
||||
|
||||
Returns
|
||||
-------
|
||||
voterank : list
|
||||
Ordered list of computed seeds.
|
||||
Only nodes with positive number of votes are returned.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph([(0, 1), (0, 2), (0, 3), (1, 4)])
|
||||
>>> nx.voterank(G)
|
||||
[0, 1]
|
||||
|
||||
The algorithm can be used both for undirected and directed graphs.
|
||||
However, the directed version is different in two ways:
|
||||
(i) nodes only vote for their in-neighbors and
|
||||
(ii) only the voting ability of elected node and its out-neighbors are updated:
|
||||
|
||||
>>> G = nx.DiGraph([(0, 1), (2, 1), (2, 3), (3, 4)])
|
||||
>>> nx.voterank(G)
|
||||
[2, 3]
|
||||
|
||||
Notes
|
||||
-----
|
||||
Each edge is treated independently in case of multigraphs.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Zhang, J.-X. et al. (2016).
|
||||
Identifying a set of influential spreaders in complex networks.
|
||||
Sci. Rep. 6, 27823; doi: 10.1038/srep27823.
|
||||
"""
|
||||
influential_nodes = []
|
||||
vote_rank = {}
|
||||
if len(G) == 0:
|
||||
return influential_nodes
|
||||
if number_of_nodes is None or number_of_nodes > len(G):
|
||||
number_of_nodes = len(G)
|
||||
if G.is_directed():
|
||||
# For directed graphs compute average out-degree
|
||||
avgDegree = sum(deg for _, deg in G.out_degree()) / len(G)
|
||||
else:
|
||||
# For undirected graphs compute average degree
|
||||
avgDegree = sum(deg for _, deg in G.degree()) / len(G)
|
||||
# step 1 - initiate all nodes to (0,1) (score, voting ability)
|
||||
for n in G.nodes():
|
||||
vote_rank[n] = [0, 1]
|
||||
# Repeat steps 1b to 4 until num_seeds are elected.
|
||||
for _ in range(number_of_nodes):
|
||||
# step 1b - reset rank
|
||||
for n in G.nodes():
|
||||
vote_rank[n][0] = 0
|
||||
# step 2 - vote
|
||||
for n, nbr in G.edges():
|
||||
# In directed graphs nodes only vote for their in-neighbors
|
||||
vote_rank[n][0] += vote_rank[nbr][1]
|
||||
if not G.is_directed():
|
||||
vote_rank[nbr][0] += vote_rank[n][1]
|
||||
for n in influential_nodes:
|
||||
vote_rank[n][0] = 0
|
||||
# step 3 - select top node
|
||||
n = max(G.nodes, key=lambda x: vote_rank[x][0])
|
||||
if vote_rank[n][0] == 0:
|
||||
return influential_nodes
|
||||
influential_nodes.append(n)
|
||||
# weaken the selected node
|
||||
vote_rank[n] = [0, 0]
|
||||
# step 4 - update voterank properties
|
||||
for _, nbr in G.edges(n):
|
||||
vote_rank[nbr][1] -= 1 / avgDegree
|
||||
vote_rank[nbr][1] = max(vote_rank[nbr][1], 0)
|
||||
return influential_nodes
|
||||
Reference in New Issue
Block a user