reciprocity.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. """Algorithms to calculate reciprocity in a directed graph."""
  2. import networkx as nx
  3. from networkx import NetworkXError
  4. from ..utils import not_implemented_for
  5. __all__ = ["reciprocity", "overall_reciprocity"]
  6. @nx._dispatch
  7. @not_implemented_for("undirected", "multigraph")
  8. def reciprocity(G, nodes=None):
  9. r"""Compute the reciprocity in a directed graph.
  10. The reciprocity of a directed graph is defined as the ratio
  11. of the number of edges pointing in both directions to the total
  12. number of edges in the graph.
  13. Formally, $r = |{(u,v) \in G|(v,u) \in G}| / |{(u,v) \in G}|$.
  14. The reciprocity of a single node u is defined similarly,
  15. it is the ratio of the number of edges in both directions to
  16. the total number of edges attached to node u.
  17. Parameters
  18. ----------
  19. G : graph
  20. A networkx directed graph
  21. nodes : container of nodes, optional (default=whole graph)
  22. Compute reciprocity for nodes in this container.
  23. Returns
  24. -------
  25. out : dictionary
  26. Reciprocity keyed by node label.
  27. Notes
  28. -----
  29. The reciprocity is not defined for isolated nodes.
  30. In such cases this function will return None.
  31. """
  32. # If `nodes` is not specified, calculate the reciprocity of the graph.
  33. if nodes is None:
  34. return overall_reciprocity(G)
  35. # If `nodes` represents a single node in the graph, return only its
  36. # reciprocity.
  37. if nodes in G:
  38. reciprocity = next(_reciprocity_iter(G, nodes))[1]
  39. if reciprocity is None:
  40. raise NetworkXError("Not defined for isolated nodes.")
  41. else:
  42. return reciprocity
  43. # Otherwise, `nodes` represents an iterable of nodes, so return a
  44. # dictionary mapping node to its reciprocity.
  45. return dict(_reciprocity_iter(G, nodes))
  46. def _reciprocity_iter(G, nodes):
  47. """Return an iterator of (node, reciprocity)."""
  48. n = G.nbunch_iter(nodes)
  49. for node in n:
  50. pred = set(G.predecessors(node))
  51. succ = set(G.successors(node))
  52. overlap = pred & succ
  53. n_total = len(pred) + len(succ)
  54. # Reciprocity is not defined for isolated nodes.
  55. # Return None.
  56. if n_total == 0:
  57. yield (node, None)
  58. else:
  59. reciprocity = 2 * len(overlap) / n_total
  60. yield (node, reciprocity)
  61. @nx._dispatch
  62. @not_implemented_for("undirected", "multigraph")
  63. def overall_reciprocity(G):
  64. """Compute the reciprocity for the whole graph.
  65. See the doc of reciprocity for the definition.
  66. Parameters
  67. ----------
  68. G : graph
  69. A networkx graph
  70. """
  71. n_all_edge = G.number_of_edges()
  72. n_overlap_edge = (n_all_edge - G.to_undirected().number_of_edges()) * 2
  73. if n_all_edge == 0:
  74. raise NetworkXError("Not defined for empty graphs")
  75. return n_overlap_edge / n_all_edge