Spaces:
Running
Running
"""Katz centrality.""" | |
import math | |
import networkx as nx | |
from networkx.utils import not_implemented_for | |
__all__ = ["katz_centrality", "katz_centrality_numpy"] | |
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 an 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. | |
When $\alpha = 1/\lambda_{\max}$ and $\beta=0$, Katz centrality is the same | |
as 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()) | |
# this should never be zero? | |
except ZeroDivisionError: | |
s = 1.0 | |
else: | |
s = 1 | |
for n in x: | |
x[n] *= s | |
return x | |
raise nx.PowerIterationFailedConvergence(max_iter) | |
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. | |
When $\alpha = 1/\lambda_{\max}$ and $\beta=0$, Katz centrality is the same | |
as 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 | |
norm = np.sign(sum(centrality)) * np.linalg.norm(centrality) if normalized else 1 | |
return dict(zip(nodelist, centrality / norm)) | |