katz.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. """Katz centrality."""
  2. import math
  3. import networkx as nx
  4. from networkx.utils import not_implemented_for
  5. __all__ = ["katz_centrality", "katz_centrality_numpy"]
  6. @nx._dispatch
  7. @not_implemented_for("multigraph")
  8. def katz_centrality(
  9. G,
  10. alpha=0.1,
  11. beta=1.0,
  12. max_iter=1000,
  13. tol=1.0e-6,
  14. nstart=None,
  15. normalized=True,
  16. weight=None,
  17. ):
  18. r"""Compute the Katz centrality for the nodes of the graph G.
  19. Katz centrality computes the centrality for a node based on the centrality
  20. of its neighbors. It is a generalization of the eigenvector centrality. The
  21. Katz centrality for node $i$ is
  22. .. math::
  23. x_i = \alpha \sum_{j} A_{ij} x_j + \beta,
  24. where $A$ is the adjacency matrix of graph G with eigenvalues $\lambda$.
  25. The parameter $\beta$ controls the initial centrality and
  26. .. math::
  27. \alpha < \frac{1}{\lambda_{\max}}.
  28. Katz centrality computes the relative influence of a node within a
  29. network by measuring the number of the immediate neighbors (first
  30. degree nodes) and also all other nodes in the network that connect
  31. to the node under consideration through these immediate neighbors.
  32. Extra weight can be provided to immediate neighbors through the
  33. parameter $\beta$. Connections made with distant neighbors
  34. are, however, penalized by an attenuation factor $\alpha$ which
  35. should be strictly less than the inverse largest eigenvalue of the
  36. adjacency matrix in order for the Katz centrality to be computed
  37. correctly. More information is provided in [1]_.
  38. Parameters
  39. ----------
  40. G : graph
  41. A NetworkX graph.
  42. alpha : float
  43. Attenuation factor
  44. beta : scalar or dictionary, optional (default=1.0)
  45. Weight attributed to the immediate neighborhood. If not a scalar, the
  46. dictionary must have an value for every node.
  47. max_iter : integer, optional (default=1000)
  48. Maximum number of iterations in power method.
  49. tol : float, optional (default=1.0e-6)
  50. Error tolerance used to check convergence in power method iteration.
  51. nstart : dictionary, optional
  52. Starting value of Katz iteration for each node.
  53. normalized : bool, optional (default=True)
  54. If True normalize the resulting values.
  55. weight : None or string, optional (default=None)
  56. If None, all edge weights are considered equal.
  57. Otherwise holds the name of the edge attribute used as weight.
  58. In this measure the weight is interpreted as the connection strength.
  59. Returns
  60. -------
  61. nodes : dictionary
  62. Dictionary of nodes with Katz centrality as the value.
  63. Raises
  64. ------
  65. NetworkXError
  66. If the parameter `beta` is not a scalar but lacks a value for at least
  67. one node
  68. PowerIterationFailedConvergence
  69. If the algorithm fails to converge to the specified tolerance
  70. within the specified number of iterations of the power iteration
  71. method.
  72. Examples
  73. --------
  74. >>> import math
  75. >>> G = nx.path_graph(4)
  76. >>> phi = (1 + math.sqrt(5)) / 2.0 # largest eigenvalue of adj matrix
  77. >>> centrality = nx.katz_centrality(G, 1 / phi - 0.01)
  78. >>> for n, c in sorted(centrality.items()):
  79. ... print(f"{n} {c:.2f}")
  80. 0 0.37
  81. 1 0.60
  82. 2 0.60
  83. 3 0.37
  84. See Also
  85. --------
  86. katz_centrality_numpy
  87. eigenvector_centrality
  88. eigenvector_centrality_numpy
  89. pagerank
  90. hits
  91. Notes
  92. -----
  93. Katz centrality was introduced by [2]_.
  94. This algorithm it uses the power method to find the eigenvector
  95. corresponding to the largest eigenvalue of the adjacency matrix of ``G``.
  96. The parameter ``alpha`` should be strictly less than the inverse of largest
  97. eigenvalue of the adjacency matrix for the algorithm to converge.
  98. You can use ``max(nx.adjacency_spectrum(G))`` to get $\lambda_{\max}$ the largest
  99. eigenvalue of the adjacency matrix.
  100. The iteration will stop after ``max_iter`` iterations or an error tolerance of
  101. ``number_of_nodes(G) * tol`` has been reached.
  102. When $\alpha = 1/\lambda_{\max}$ and $\beta=0$, Katz centrality is the same
  103. as eigenvector centrality.
  104. For directed graphs this finds "left" eigenvectors which corresponds
  105. to the in-edges in the graph. For out-edges Katz centrality
  106. first reverse the graph with ``G.reverse()``.
  107. References
  108. ----------
  109. .. [1] Mark E. J. Newman:
  110. Networks: An Introduction.
  111. Oxford University Press, USA, 2010, p. 720.
  112. .. [2] Leo Katz:
  113. A New Status Index Derived from Sociometric Index.
  114. Psychometrika 18(1):39–43, 1953
  115. https://link.springer.com/content/pdf/10.1007/BF02289026.pdf
  116. """
  117. if len(G) == 0:
  118. return {}
  119. nnodes = G.number_of_nodes()
  120. if nstart is None:
  121. # choose starting vector with entries of 0
  122. x = {n: 0 for n in G}
  123. else:
  124. x = nstart
  125. try:
  126. b = dict.fromkeys(G, float(beta))
  127. except (TypeError, ValueError, AttributeError) as err:
  128. b = beta
  129. if set(beta) != set(G):
  130. raise nx.NetworkXError(
  131. "beta dictionary " "must have a value for every node"
  132. ) from err
  133. # make up to max_iter iterations
  134. for _ in range(max_iter):
  135. xlast = x
  136. x = dict.fromkeys(xlast, 0)
  137. # do the multiplication y^T = Alpha * x^T A + Beta
  138. for n in x:
  139. for nbr in G[n]:
  140. x[nbr] += xlast[n] * G[n][nbr].get(weight, 1)
  141. for n in x:
  142. x[n] = alpha * x[n] + b[n]
  143. # check convergence
  144. error = sum(abs(x[n] - xlast[n]) for n in x)
  145. if error < nnodes * tol:
  146. if normalized:
  147. # normalize vector
  148. try:
  149. s = 1.0 / math.hypot(*x.values())
  150. # this should never be zero?
  151. except ZeroDivisionError:
  152. s = 1.0
  153. else:
  154. s = 1
  155. for n in x:
  156. x[n] *= s
  157. return x
  158. raise nx.PowerIterationFailedConvergence(max_iter)
  159. @not_implemented_for("multigraph")
  160. def katz_centrality_numpy(G, alpha=0.1, beta=1.0, normalized=True, weight=None):
  161. r"""Compute the Katz centrality for the graph G.
  162. Katz centrality computes the centrality for a node based on the centrality
  163. of its neighbors. It is a generalization of the eigenvector centrality. The
  164. Katz centrality for node $i$ is
  165. .. math::
  166. x_i = \alpha \sum_{j} A_{ij} x_j + \beta,
  167. where $A$ is the adjacency matrix of graph G with eigenvalues $\lambda$.
  168. The parameter $\beta$ controls the initial centrality and
  169. .. math::
  170. \alpha < \frac{1}{\lambda_{\max}}.
  171. Katz centrality computes the relative influence of a node within a
  172. network by measuring the number of the immediate neighbors (first
  173. degree nodes) and also all other nodes in the network that connect
  174. to the node under consideration through these immediate neighbors.
  175. Extra weight can be provided to immediate neighbors through the
  176. parameter $\beta$. Connections made with distant neighbors
  177. are, however, penalized by an attenuation factor $\alpha$ which
  178. should be strictly less than the inverse largest eigenvalue of the
  179. adjacency matrix in order for the Katz centrality to be computed
  180. correctly. More information is provided in [1]_.
  181. Parameters
  182. ----------
  183. G : graph
  184. A NetworkX graph
  185. alpha : float
  186. Attenuation factor
  187. beta : scalar or dictionary, optional (default=1.0)
  188. Weight attributed to the immediate neighborhood. If not a scalar the
  189. dictionary must have an value for every node.
  190. normalized : bool
  191. If True normalize the resulting values.
  192. weight : None or string, optional
  193. If None, all edge weights are considered equal.
  194. Otherwise holds the name of the edge attribute used as weight.
  195. In this measure the weight is interpreted as the connection strength.
  196. Returns
  197. -------
  198. nodes : dictionary
  199. Dictionary of nodes with Katz centrality as the value.
  200. Raises
  201. ------
  202. NetworkXError
  203. If the parameter `beta` is not a scalar but lacks a value for at least
  204. one node
  205. Examples
  206. --------
  207. >>> import math
  208. >>> G = nx.path_graph(4)
  209. >>> phi = (1 + math.sqrt(5)) / 2.0 # largest eigenvalue of adj matrix
  210. >>> centrality = nx.katz_centrality_numpy(G, 1 / phi)
  211. >>> for n, c in sorted(centrality.items()):
  212. ... print(f"{n} {c:.2f}")
  213. 0 0.37
  214. 1 0.60
  215. 2 0.60
  216. 3 0.37
  217. See Also
  218. --------
  219. katz_centrality
  220. eigenvector_centrality_numpy
  221. eigenvector_centrality
  222. pagerank
  223. hits
  224. Notes
  225. -----
  226. Katz centrality was introduced by [2]_.
  227. This algorithm uses a direct linear solver to solve the above equation.
  228. The parameter ``alpha`` should be strictly less than the inverse of largest
  229. eigenvalue of the adjacency matrix for there to be a solution.
  230. You can use ``max(nx.adjacency_spectrum(G))`` to get $\lambda_{\max}$ the largest
  231. eigenvalue of the adjacency matrix.
  232. When $\alpha = 1/\lambda_{\max}$ and $\beta=0$, Katz centrality is the same
  233. as eigenvector centrality.
  234. For directed graphs this finds "left" eigenvectors which corresponds
  235. to the in-edges in the graph. For out-edges Katz centrality
  236. first reverse the graph with ``G.reverse()``.
  237. References
  238. ----------
  239. .. [1] Mark E. J. Newman:
  240. Networks: An Introduction.
  241. Oxford University Press, USA, 2010, p. 173.
  242. .. [2] Leo Katz:
  243. A New Status Index Derived from Sociometric Index.
  244. Psychometrika 18(1):39–43, 1953
  245. https://link.springer.com/content/pdf/10.1007/BF02289026.pdf
  246. """
  247. import numpy as np
  248. if len(G) == 0:
  249. return {}
  250. try:
  251. nodelist = beta.keys()
  252. if set(nodelist) != set(G):
  253. raise nx.NetworkXError(
  254. "beta dictionary " "must have a value for every node"
  255. )
  256. b = np.array(list(beta.values()), dtype=float)
  257. except AttributeError:
  258. nodelist = list(G)
  259. try:
  260. b = np.ones((len(nodelist), 1)) * beta
  261. except (TypeError, ValueError, AttributeError) as err:
  262. raise nx.NetworkXError("beta must be a number") from err
  263. A = nx.adjacency_matrix(G, nodelist=nodelist, weight=weight).todense().T
  264. n = A.shape[0]
  265. centrality = np.linalg.solve(np.eye(n, n) - (alpha * A), b)
  266. if normalized:
  267. norm = np.sign(sum(centrality)) * np.linalg.norm(centrality)
  268. else:
  269. norm = 1.0
  270. centrality = dict(zip(nodelist, map(float, centrality / norm)))
  271. return centrality