"""Node assortativity coefficients and correlation measures."""
import networkx as nx
from networkx.algorithms.assortativity.mixing import (
attribute_mixing_matrix,
degree_mixing_matrix,
)
from networkx.algorithms.assortativity.pairs import node_degree_xy
__all__ = [
"degree_pearson_correlation_coefficient",
"degree_assortativity_coefficient",
"attribute_assortativity_coefficient",
"numeric_assortativity_coefficient",
]
[docs]
@nx._dispatchable(edge_attrs="weight")
def degree_assortativity_coefficient(G, x="out", y="in", weight=None, nodes=None):
"""Compute degree assortativity of graph.
Assortativity measures the similarity of connections
in the graph with respect to the node degree.
Parameters
----------
G : NetworkX graph
x: string ('in','out')
The degree type for source node (directed graphs only).
y: string ('in','out')
The degree type for target node (directed graphs only).
weight: string or None, optional (default=None)
The edge attribute that holds the numerical value used
as a weight. If None, then each edge has weight 1.
The degree is the sum of the edge weights adjacent to the node.
nodes: list or iterable (optional)
Compute degree assortativity only for nodes in container.
The default is all nodes.
Returns
-------
r : float
Assortativity of graph by degree.
Examples
--------
>>> G = nx.path_graph(4)
>>> r = nx.degree_assortativity_coefficient(G)
>>> print(f"{r:3.1f}")
-0.5
See Also
--------
attribute_assortativity_coefficient
numeric_assortativity_coefficient
degree_mixing_dict
degree_mixing_matrix
Notes
-----
This computes Eq. (21) in Ref. [1]_ , where e is the joint
probability distribution (mixing matrix) of the degrees. If G is
directed than the matrix e is the joint probability of the
user-specified degree type for the source and target.
References
----------
.. [1] M. E. J. Newman, Mixing patterns in networks,
Physical Review E, 67 026126, 2003
.. [2] Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M.
Edge direction and the structure of networks, PNAS 107, 10815-20 (2010).
"""
if nodes is None:
nodes = G.nodes
degrees = None
if G.is_directed():
indeg = (
{d for _, d in G.in_degree(nodes, weight=weight)}
if "in" in (x, y)
else set()
)
outdeg = (
{d for _, d in G.out_degree(nodes, weight=weight)}
if "out" in (x, y)
else set()
)
degrees = set.union(indeg, outdeg)
else:
degrees = {d for _, d in G.degree(nodes, weight=weight)}
mapping = {d: i for i, d in enumerate(degrees)}
M = degree_mixing_matrix(G, x=x, y=y, nodes=nodes, weight=weight, mapping=mapping)
return _numeric_ac(M, mapping=mapping)
[docs]
@nx._dispatchable(edge_attrs="weight")
def degree_pearson_correlation_coefficient(G, x="out", y="in", weight=None, nodes=None):
"""Compute degree assortativity of graph.
Assortativity measures the similarity of connections
in the graph with respect to the node degree.
This is the same as degree_assortativity_coefficient but uses the
potentially faster scipy.stats.pearsonr function.
Parameters
----------
G : NetworkX graph
x: string ('in','out')
The degree type for source node (directed graphs only).
y: string ('in','out')
The degree type for target node (directed graphs only).
weight: string or None, optional (default=None)
The edge attribute that holds the numerical value used
as a weight. If None, then each edge has weight 1.
The degree is the sum of the edge weights adjacent to the node.
nodes: list or iterable (optional)
Compute pearson correlation of degrees only for specified nodes.
The default is all nodes.
Returns
-------
r : float
Assortativity of graph by degree.
Examples
--------
>>> G = nx.path_graph(4)
>>> r = nx.degree_pearson_correlation_coefficient(G)
>>> print(f"{r:3.1f}")
-0.5
Notes
-----
This calls scipy.stats.pearsonr.
References
----------
.. [1] M. E. J. Newman, Mixing patterns in networks
Physical Review E, 67 026126, 2003
.. [2] Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M.
Edge direction and the structure of networks, PNAS 107, 10815-20 (2010).
"""
import scipy as sp
xy = node_degree_xy(G, x=x, y=y, nodes=nodes, weight=weight)
x, y = zip(*xy)
return float(sp.stats.pearsonr(x, y)[0])
[docs]
@nx._dispatchable(node_attrs="attribute")
def attribute_assortativity_coefficient(G, attribute, nodes=None):
"""Compute assortativity for node attributes.
Assortativity measures the similarity of connections
in the graph with respect to the given attribute.
Parameters
----------
G : NetworkX graph
attribute : string
Node attribute key
nodes: list or iterable (optional)
Compute attribute assortativity for nodes in container.
The default is all nodes.
Returns
-------
r: float
Assortativity of graph for given attribute
Examples
--------
>>> G = nx.Graph()
>>> G.add_nodes_from([0, 1], color="red")
>>> G.add_nodes_from([2, 3], color="blue")
>>> G.add_edges_from([(0, 1), (2, 3)])
>>> print(nx.attribute_assortativity_coefficient(G, "color"))
1.0
Notes
-----
This computes Eq. (2) in Ref. [1]_ , (trace(M)-sum(M^2))/(1-sum(M^2)),
where M is the joint probability distribution (mixing matrix)
of the specified attribute.
References
----------
.. [1] M. E. J. Newman, Mixing patterns in networks,
Physical Review E, 67 026126, 2003
"""
M = attribute_mixing_matrix(G, attribute, nodes)
return attribute_ac(M)
[docs]
@nx._dispatchable(node_attrs="attribute")
def numeric_assortativity_coefficient(G, attribute, nodes=None):
"""Compute assortativity for numerical node attributes.
Assortativity measures the similarity of connections
in the graph with respect to the given numeric attribute.
Parameters
----------
G : NetworkX graph
attribute : string
Node attribute key.
nodes: list or iterable (optional)
Compute numeric assortativity only for attributes of nodes in
container. The default is all nodes.
Returns
-------
r: float
Assortativity of graph for given attribute
Examples
--------
>>> G = nx.Graph()
>>> G.add_nodes_from([0, 1], size=2)
>>> G.add_nodes_from([2, 3], size=3)
>>> G.add_edges_from([(0, 1), (2, 3)])
>>> print(nx.numeric_assortativity_coefficient(G, "size"))
1.0
Notes
-----
This computes Eq. (21) in Ref. [1]_ , which is the Pearson correlation
coefficient of the specified (scalar valued) attribute across edges.
References
----------
.. [1] M. E. J. Newman, Mixing patterns in networks
Physical Review E, 67 026126, 2003
"""
if nodes is None:
nodes = G.nodes
vals = {G.nodes[n][attribute] for n in nodes}
mapping = {d: i for i, d in enumerate(vals)}
M = attribute_mixing_matrix(G, attribute, nodes, mapping)
return _numeric_ac(M, mapping)
def attribute_ac(M):
"""Compute assortativity for attribute matrix M.
Parameters
----------
M : numpy.ndarray
2D ndarray representing the attribute mixing matrix.
Notes
-----
This computes Eq. (2) in Ref. [1]_ , (trace(e)-sum(e^2))/(1-sum(e^2)),
where e is the joint probability distribution (mixing matrix)
of the specified attribute.
References
----------
.. [1] M. E. J. Newman, Mixing patterns in networks,
Physical Review E, 67 026126, 2003
"""
if M.sum() != 1.0:
M = M / M.sum()
s = (M @ M).sum()
t = M.trace()
r = (t - s) / (1 - s)
return float(r)
def _numeric_ac(M, mapping):
# M is a 2D numpy array
# numeric assortativity coefficient, pearsonr
import numpy as np
if M.sum() != 1.0:
M = M / M.sum()
x = np.array(list(mapping.keys()))
y = x # x and y have the same support
idx = list(mapping.values())
a = M.sum(axis=0)
b = M.sum(axis=1)
vara = (a[idx] * x**2).sum() - ((a[idx] * x).sum()) ** 2
varb = (b[idx] * y**2).sum() - ((b[idx] * y).sum()) ** 2
xy = np.outer(x, y)
ab = np.outer(a[idx], b[idx])
return float((xy * (M - ab)).sum() / np.sqrt(vara * varb))