braintools.analysis.graph.clustering_coefficient#
- braintools.analysis.graph.clustering_coefficient(conn, *, directed=True, weighted=False, average=True)#
Compute the local clustering coefficient, or its average over nodes.
With
directed=Truethis is Fagiolo’s directed clustering [1], which counts all eight kinds of directed triangles around node \(i\):\[C_i = \frac{\left[(A + A^\top)^3\right]_{ii}} {2\left[d_i^{tot}(d_i^{tot} - 1) - 2 d_i^{\leftrightarrow}\right]},\]with \(d^{tot}\) the in- plus out-degree and \(d^{\leftrightarrow}\) the number of reciprocated neighbours. With
directed=Falsethe graph is symmetrised and \(C_i = [A^3]_{ii} / (k_i (k_i - 1))\) [2]. Nodes with fewer than two neighbours have \(C_i = 0\).With
weighted=Truethe adjacency \(A\) is replaced by \(\hat W^{1/3}\), with \(\hat W = W / \max(W)\) (geometric mean of the triangle weights [3], signed cube root as networkx), while the degrees stay unweighted. The result matchesnetworkx.clusteringandnetworkx.average_clusteringwith the same directedness andweight='weight'/weight=None.- Parameters:
conn (
Any) – Square connectivity, rows pre and columns post. Self-loops are ignored (also in the weight normalisation, where networkx would include them).directed (
bool) – Treat the graph as directed (Fagiolo) or undirected (symmetrised; the pair weight is the mean of the directions present).weighted (
bool) – Use the edge weights. They are normalised by their maximum, so their unit cancels (a Quantity is accepted). The largest weight must be positive.average (
bool) – Return the mean over all nodes (nodes with zero clustering included, asnetworkx.average_clustering) instead of the per-node array.
- Returns:
The average clustering (
nanfor a graph with no nodes), or the per-node clustering of shape(N,).- Return type:
float|ndarray- Raises:
ValueError – If the connectivity is not square, or
weighted=Trueand the largest weight is not positive.
See also
small_world_indexClustering and path length relative to random graphs.
motif_countsDirected triad census.
Notes
The diagonal of the matrix cube is computed as row sums of \((A A) \circ A^\top\) with sparse products evaluated in row chunks, so no dense \(N \times N\) array is formed. The cost is about the number of length-2 paths, \(\sum_k d_k^2\).
Host-only (NumPy/SciPy): not traceable by
jax.jit,jax.vmaporjax.grad.References
Examples
>>> import numpy as np >>> from braintools.analysis.graph import clustering_coefficient >>> triangle_plus_tail = np.array([[0, 1, 1, 0], ... [1, 0, 1, 0], ... [1, 1, 0, 1], ... [0, 0, 1, 0]]) >>> clustering_coefficient(triangle_plus_tail, directed=False, average=False) array([1. , 1. , 0.33333333, 0. ]) >>> >>> round(clustering_coefficient(triangle_plus_tail, directed=False), 4) 0.5833