123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- """Katz centrality."""
- import math
- import networkx as nx
- from networkx.utils import not_implemented_for
- __all__ = ["katz_centrality", "katz_centrality_numpy"]
- @nx._dispatch
- @not_implemented_for("multigraph")
- 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
- 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
- pagerank
- 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)
- @not_implemented_for("multigraph")
- 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
- pagerank
- 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)
- if normalized:
- norm = np.sign(sum(centrality)) * np.linalg.norm(centrality)
- else:
- norm = 1.0
- centrality = dict(zip(nodelist, map(float, centrality / norm)))
- return centrality
|