1"""Node assortativity coefficients and correlation measures."""
2
3import networkx as nx
4from networkx.algorithms.assortativity.mixing import (
5 attribute_mixing_matrix,
6 degree_mixing_matrix,
7)
8from networkx.algorithms.assortativity.pairs import node_degree_xy
9
10__all__ = [
11 "degree_pearson_correlation_coefficient",
12 "degree_assortativity_coefficient",
13 "attribute_assortativity_coefficient",
14 "numeric_assortativity_coefficient",
15]
16
17
18@nx._dispatchable(edge_attrs="weight")
19def degree_assortativity_coefficient(G, x="out", y="in", weight=None, nodes=None):
20 """Compute degree assortativity of graph.
21
22 Assortativity measures the similarity of connections
23 in the graph with respect to the node degree.
24
25 Parameters
26 ----------
27 G : NetworkX graph
28
29 x: string ('in','out')
30 The degree type for source node (directed graphs only).
31
32 y: string ('in','out')
33 The degree type for target node (directed graphs only).
34
35 weight: string or None, optional (default=None)
36 The edge attribute that holds the numerical value used
37 as a weight. If None, then each edge has weight 1.
38 The degree is the sum of the edge weights adjacent to the node.
39
40 nodes: list or iterable (optional)
41 Compute degree assortativity only for nodes in container.
42 The default is all nodes.
43
44 Returns
45 -------
46 r : float
47 Assortativity of graph by degree.
48
49 Examples
50 --------
51 >>> G = nx.path_graph(4)
52 >>> r = nx.degree_assortativity_coefficient(G)
53 >>> print(f"{r:3.1f}")
54 -0.5
55
56 See Also
57 --------
58 attribute_assortativity_coefficient
59 numeric_assortativity_coefficient
60 degree_mixing_dict
61 degree_mixing_matrix
62
63 Notes
64 -----
65 This computes Eq. (21) in Ref. [1]_ , where e is the joint
66 probability distribution (mixing matrix) of the degrees. If G is
67 directed than the matrix e is the joint probability of the
68 user-specified degree type for the source and target.
69
70 References
71 ----------
72 .. [1] M. E. J. Newman, Mixing patterns in networks,
73 Physical Review E, 67 026126, 2003
74 .. [2] Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M.
75 Edge direction and the structure of networks, PNAS 107, 10815-20 (2010).
76 """
77 if nodes is None:
78 nodes = G.nodes
79
80 degrees = None
81
82 if G.is_directed():
83 indeg = (
84 {d for _, d in G.in_degree(nodes, weight=weight)}
85 if "in" in (x, y)
86 else set()
87 )
88 outdeg = (
89 {d for _, d in G.out_degree(nodes, weight=weight)}
90 if "out" in (x, y)
91 else set()
92 )
93 degrees = set.union(indeg, outdeg)
94 else:
95 degrees = {d for _, d in G.degree(nodes, weight=weight)}
96
97 mapping = {d: i for i, d in enumerate(degrees)}
98 M = degree_mixing_matrix(G, x=x, y=y, nodes=nodes, weight=weight, mapping=mapping)
99
100 return _numeric_ac(M, mapping=mapping)
101
102
103@nx._dispatchable(edge_attrs="weight")
104def degree_pearson_correlation_coefficient(G, x="out", y="in", weight=None, nodes=None):
105 """Compute degree assortativity of graph.
106
107 Assortativity measures the similarity of connections
108 in the graph with respect to the node degree.
109
110 This is the same as degree_assortativity_coefficient but uses the
111 potentially faster scipy.stats.pearsonr function.
112
113 Parameters
114 ----------
115 G : NetworkX graph
116
117 x: string ('in','out')
118 The degree type for source node (directed graphs only).
119
120 y: string ('in','out')
121 The degree type for target node (directed graphs only).
122
123 weight: string or None, optional (default=None)
124 The edge attribute that holds the numerical value used
125 as a weight. If None, then each edge has weight 1.
126 The degree is the sum of the edge weights adjacent to the node.
127
128 nodes: list or iterable (optional)
129 Compute pearson correlation of degrees only for specified nodes.
130 The default is all nodes.
131
132 Returns
133 -------
134 r : float
135 Assortativity of graph by degree.
136
137 Examples
138 --------
139 >>> G = nx.path_graph(4)
140 >>> r = nx.degree_pearson_correlation_coefficient(G)
141 >>> print(f"{r:3.1f}")
142 -0.5
143
144 Notes
145 -----
146 This calls scipy.stats.pearsonr.
147
148 References
149 ----------
150 .. [1] M. E. J. Newman, Mixing patterns in networks
151 Physical Review E, 67 026126, 2003
152 .. [2] Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M.
153 Edge direction and the structure of networks, PNAS 107, 10815-20 (2010).
154 """
155 import scipy as sp
156
157 xy = node_degree_xy(G, x=x, y=y, nodes=nodes, weight=weight)
158 x, y = zip(*xy)
159 return float(sp.stats.pearsonr(x, y)[0])
160
161
162@nx._dispatchable(node_attrs="attribute")
163def attribute_assortativity_coefficient(G, attribute, nodes=None):
164 """Compute assortativity for node attributes.
165
166 Assortativity measures the similarity of connections
167 in the graph with respect to the given attribute.
168
169 Parameters
170 ----------
171 G : NetworkX graph
172
173 attribute : string
174 Node attribute key
175
176 nodes: list or iterable (optional)
177 Compute attribute assortativity for nodes in container.
178 The default is all nodes.
179
180 Returns
181 -------
182 r: float
183 Assortativity of graph for given attribute
184
185 Examples
186 --------
187 >>> G = nx.Graph()
188 >>> G.add_nodes_from([0, 1], color="red")
189 >>> G.add_nodes_from([2, 3], color="blue")
190 >>> G.add_edges_from([(0, 1), (2, 3)])
191 >>> print(nx.attribute_assortativity_coefficient(G, "color"))
192 1.0
193
194 Notes
195 -----
196 This computes Eq. (2) in Ref. [1]_ , (trace(M)-sum(M^2))/(1-sum(M^2)),
197 where M is the joint probability distribution (mixing matrix)
198 of the specified attribute.
199
200 References
201 ----------
202 .. [1] M. E. J. Newman, Mixing patterns in networks,
203 Physical Review E, 67 026126, 2003
204 """
205 M = attribute_mixing_matrix(G, attribute, nodes)
206 return attribute_ac(M)
207
208
209@nx._dispatchable(node_attrs="attribute")
210def numeric_assortativity_coefficient(G, attribute, nodes=None):
211 """Compute assortativity for numerical node attributes.
212
213 Assortativity measures the similarity of connections
214 in the graph with respect to the given numeric attribute.
215
216 Parameters
217 ----------
218 G : NetworkX graph
219
220 attribute : string
221 Node attribute key.
222
223 nodes: list or iterable (optional)
224 Compute numeric assortativity only for attributes of nodes in
225 container. The default is all nodes.
226
227 Returns
228 -------
229 r: float
230 Assortativity of graph for given attribute
231
232 Examples
233 --------
234 >>> G = nx.Graph()
235 >>> G.add_nodes_from([0, 1], size=2)
236 >>> G.add_nodes_from([2, 3], size=3)
237 >>> G.add_edges_from([(0, 1), (2, 3)])
238 >>> print(nx.numeric_assortativity_coefficient(G, "size"))
239 1.0
240
241 Notes
242 -----
243 This computes Eq. (21) in Ref. [1]_ , which is the Pearson correlation
244 coefficient of the specified (scalar valued) attribute across edges.
245
246 References
247 ----------
248 .. [1] M. E. J. Newman, Mixing patterns in networks
249 Physical Review E, 67 026126, 2003
250 """
251 if nodes is None:
252 nodes = G.nodes
253 vals = {G.nodes[n][attribute] for n in nodes}
254 mapping = {d: i for i, d in enumerate(vals)}
255 M = attribute_mixing_matrix(G, attribute, nodes, mapping)
256 return _numeric_ac(M, mapping)
257
258
259def attribute_ac(M):
260 """Compute assortativity for attribute matrix M.
261
262 Parameters
263 ----------
264 M : numpy.ndarray
265 2D ndarray representing the attribute mixing matrix.
266
267 Notes
268 -----
269 This computes Eq. (2) in Ref. [1]_ , (trace(e)-sum(e^2))/(1-sum(e^2)),
270 where e is the joint probability distribution (mixing matrix)
271 of the specified attribute.
272
273 References
274 ----------
275 .. [1] M. E. J. Newman, Mixing patterns in networks,
276 Physical Review E, 67 026126, 2003
277 """
278 if M.sum() != 1.0:
279 M = M / M.sum()
280 s = (M @ M).sum()
281 t = M.trace()
282 r = (t - s) / (1 - s)
283 return float(r)
284
285
286def _numeric_ac(M, mapping):
287 # M is a 2D numpy array
288 # numeric assortativity coefficient, pearsonr
289 import numpy as np
290
291 if M.sum() != 1.0:
292 M = M / M.sum()
293 x = np.array(list(mapping.keys()))
294 y = x # x and y have the same support
295 idx = list(mapping.values())
296 a = M.sum(axis=0)
297 b = M.sum(axis=1)
298 vara = (a[idx] * x**2).sum() - ((a[idx] * x).sum()) ** 2
299 varb = (b[idx] * y**2).sum() - ((b[idx] * y).sum()) ** 2
300 xy = np.outer(x, y)
301 ab = np.outer(a[idx], b[idx])
302 return float((xy * (M - ab)).sum() / np.sqrt(vara * varb))