link_prediction.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. """
  2. Link prediction algorithms.
  3. """
  4. from math import log
  5. import networkx as nx
  6. from networkx.utils import not_implemented_for
  7. __all__ = [
  8. "resource_allocation_index",
  9. "jaccard_coefficient",
  10. "adamic_adar_index",
  11. "preferential_attachment",
  12. "cn_soundarajan_hopcroft",
  13. "ra_index_soundarajan_hopcroft",
  14. "within_inter_cluster",
  15. "common_neighbor_centrality",
  16. ]
  17. def _apply_prediction(G, func, ebunch=None):
  18. """Applies the given function to each edge in the specified iterable
  19. of edges.
  20. `G` is an instance of :class:`networkx.Graph`.
  21. `func` is a function on two inputs, each of which is a node in the
  22. graph. The function can return anything, but it should return a
  23. value representing a prediction of the likelihood of a "link"
  24. joining the two nodes.
  25. `ebunch` is an iterable of pairs of nodes. If not specified, all
  26. non-edges in the graph `G` will be used.
  27. """
  28. if ebunch is None:
  29. ebunch = nx.non_edges(G)
  30. return ((u, v, func(u, v)) for u, v in ebunch)
  31. @not_implemented_for("directed")
  32. @not_implemented_for("multigraph")
  33. def resource_allocation_index(G, ebunch=None):
  34. r"""Compute the resource allocation index of all node pairs in ebunch.
  35. Resource allocation index of `u` and `v` is defined as
  36. .. math::
  37. \sum_{w \in \Gamma(u) \cap \Gamma(v)} \frac{1}{|\Gamma(w)|}
  38. where $\Gamma(u)$ denotes the set of neighbors of $u$.
  39. Parameters
  40. ----------
  41. G : graph
  42. A NetworkX undirected graph.
  43. ebunch : iterable of node pairs, optional (default = None)
  44. Resource allocation index will be computed for each pair of
  45. nodes given in the iterable. The pairs must be given as
  46. 2-tuples (u, v) where u and v are nodes in the graph. If ebunch
  47. is None then all non-existent edges in the graph will be used.
  48. Default value: None.
  49. Returns
  50. -------
  51. piter : iterator
  52. An iterator of 3-tuples in the form (u, v, p) where (u, v) is a
  53. pair of nodes and p is their resource allocation index.
  54. Examples
  55. --------
  56. >>> G = nx.complete_graph(5)
  57. >>> preds = nx.resource_allocation_index(G, [(0, 1), (2, 3)])
  58. >>> for u, v, p in preds:
  59. ... print(f"({u}, {v}) -> {p:.8f}")
  60. (0, 1) -> 0.75000000
  61. (2, 3) -> 0.75000000
  62. References
  63. ----------
  64. .. [1] T. Zhou, L. Lu, Y.-C. Zhang.
  65. Predicting missing links via local information.
  66. Eur. Phys. J. B 71 (2009) 623.
  67. https://arxiv.org/pdf/0901.0553.pdf
  68. """
  69. def predict(u, v):
  70. return sum(1 / G.degree(w) for w in nx.common_neighbors(G, u, v))
  71. return _apply_prediction(G, predict, ebunch)
  72. @nx._dispatch
  73. @not_implemented_for("directed")
  74. @not_implemented_for("multigraph")
  75. def jaccard_coefficient(G, ebunch=None):
  76. r"""Compute the Jaccard coefficient of all node pairs in ebunch.
  77. Jaccard coefficient of nodes `u` and `v` is defined as
  78. .. math::
  79. \frac{|\Gamma(u) \cap \Gamma(v)|}{|\Gamma(u) \cup \Gamma(v)|}
  80. where $\Gamma(u)$ denotes the set of neighbors of $u$.
  81. Parameters
  82. ----------
  83. G : graph
  84. A NetworkX undirected graph.
  85. ebunch : iterable of node pairs, optional (default = None)
  86. Jaccard coefficient will be computed for each pair of nodes
  87. given in the iterable. The pairs must be given as 2-tuples
  88. (u, v) where u and v are nodes in the graph. If ebunch is None
  89. then all non-existent edges in the graph will be used.
  90. Default value: None.
  91. Returns
  92. -------
  93. piter : iterator
  94. An iterator of 3-tuples in the form (u, v, p) where (u, v) is a
  95. pair of nodes and p is their Jaccard coefficient.
  96. Examples
  97. --------
  98. >>> G = nx.complete_graph(5)
  99. >>> preds = nx.jaccard_coefficient(G, [(0, 1), (2, 3)])
  100. >>> for u, v, p in preds:
  101. ... print(f"({u}, {v}) -> {p:.8f}")
  102. (0, 1) -> 0.60000000
  103. (2, 3) -> 0.60000000
  104. References
  105. ----------
  106. .. [1] D. Liben-Nowell, J. Kleinberg.
  107. The Link Prediction Problem for Social Networks (2004).
  108. http://www.cs.cornell.edu/home/kleinber/link-pred.pdf
  109. """
  110. def predict(u, v):
  111. union_size = len(set(G[u]) | set(G[v]))
  112. if union_size == 0:
  113. return 0
  114. return len(list(nx.common_neighbors(G, u, v))) / union_size
  115. return _apply_prediction(G, predict, ebunch)
  116. @not_implemented_for("directed")
  117. @not_implemented_for("multigraph")
  118. def adamic_adar_index(G, ebunch=None):
  119. r"""Compute the Adamic-Adar index of all node pairs in ebunch.
  120. Adamic-Adar index of `u` and `v` is defined as
  121. .. math::
  122. \sum_{w \in \Gamma(u) \cap \Gamma(v)} \frac{1}{\log |\Gamma(w)|}
  123. where $\Gamma(u)$ denotes the set of neighbors of $u$.
  124. This index leads to zero-division for nodes only connected via self-loops.
  125. It is intended to be used when no self-loops are present.
  126. Parameters
  127. ----------
  128. G : graph
  129. NetworkX undirected graph.
  130. ebunch : iterable of node pairs, optional (default = None)
  131. Adamic-Adar index will be computed for each pair of nodes given
  132. in the iterable. The pairs must be given as 2-tuples (u, v)
  133. where u and v are nodes in the graph. If ebunch is None then all
  134. non-existent edges in the graph will be used.
  135. Default value: None.
  136. Returns
  137. -------
  138. piter : iterator
  139. An iterator of 3-tuples in the form (u, v, p) where (u, v) is a
  140. pair of nodes and p is their Adamic-Adar index.
  141. Examples
  142. --------
  143. >>> G = nx.complete_graph(5)
  144. >>> preds = nx.adamic_adar_index(G, [(0, 1), (2, 3)])
  145. >>> for u, v, p in preds:
  146. ... print(f"({u}, {v}) -> {p:.8f}")
  147. (0, 1) -> 2.16404256
  148. (2, 3) -> 2.16404256
  149. References
  150. ----------
  151. .. [1] D. Liben-Nowell, J. Kleinberg.
  152. The Link Prediction Problem for Social Networks (2004).
  153. http://www.cs.cornell.edu/home/kleinber/link-pred.pdf
  154. """
  155. def predict(u, v):
  156. return sum(1 / log(G.degree(w)) for w in nx.common_neighbors(G, u, v))
  157. return _apply_prediction(G, predict, ebunch)
  158. @not_implemented_for("directed")
  159. @not_implemented_for("multigraph")
  160. def common_neighbor_centrality(G, ebunch=None, alpha=0.8):
  161. r"""Return the CCPA score for each pair of nodes.
  162. Compute the Common Neighbor and Centrality based Parameterized Algorithm(CCPA)
  163. score of all node pairs in ebunch.
  164. CCPA score of `u` and `v` is defined as
  165. .. math::
  166. \alpha \cdot (|\Gamma (u){\cap }^{}\Gamma (v)|)+(1-\alpha )\cdot \frac{N}{{d}_{uv}}
  167. where $\Gamma(u)$ denotes the set of neighbors of $u$, $\Gamma(v)$ denotes the
  168. set of neighbors of $v$, $\alpha$ is parameter varies between [0,1], $N$ denotes
  169. total number of nodes in the Graph and ${d}_{uv}$ denotes shortest distance
  170. between $u$ and $v$.
  171. This algorithm is based on two vital properties of nodes, namely the number
  172. of common neighbors and their centrality. Common neighbor refers to the common
  173. nodes between two nodes. Centrality refers to the prestige that a node enjoys
  174. in a network.
  175. .. seealso::
  176. :func:`common_neighbors`
  177. Parameters
  178. ----------
  179. G : graph
  180. NetworkX undirected graph.
  181. ebunch : iterable of node pairs, optional (default = None)
  182. Preferential attachment score will be computed for each pair of
  183. nodes given in the iterable. The pairs must be given as
  184. 2-tuples (u, v) where u and v are nodes in the graph. If ebunch
  185. is None then all non-existent edges in the graph will be used.
  186. Default value: None.
  187. alpha : Parameter defined for participation of Common Neighbor
  188. and Centrality Algorithm share. Values for alpha should
  189. normally be between 0 and 1. Default value set to 0.8
  190. because author found better performance at 0.8 for all the
  191. dataset.
  192. Default value: 0.8
  193. Returns
  194. -------
  195. piter : iterator
  196. An iterator of 3-tuples in the form (u, v, p) where (u, v) is a
  197. pair of nodes and p is their Common Neighbor and Centrality based
  198. Parameterized Algorithm(CCPA) score.
  199. Examples
  200. --------
  201. >>> G = nx.complete_graph(5)
  202. >>> preds = nx.common_neighbor_centrality(G, [(0, 1), (2, 3)])
  203. >>> for u, v, p in preds:
  204. ... print(f"({u}, {v}) -> {p}")
  205. (0, 1) -> 3.4000000000000004
  206. (2, 3) -> 3.4000000000000004
  207. References
  208. ----------
  209. .. [1] Ahmad, I., Akhtar, M.U., Noor, S. et al.
  210. Missing Link Prediction using Common Neighbor and Centrality based Parameterized Algorithm.
  211. Sci Rep 10, 364 (2020).
  212. https://doi.org/10.1038/s41598-019-57304-y
  213. """
  214. # When alpha == 1, the CCPA score simplifies to the number of common neighbors.
  215. if alpha == 1:
  216. def predict(u, v):
  217. if u == v:
  218. raise nx.NetworkXAlgorithmError("Self links are not supported")
  219. return sum(1 for _ in nx.common_neighbors(G, u, v))
  220. else:
  221. spl = dict(nx.shortest_path_length(G))
  222. inf = float("inf")
  223. def predict(u, v):
  224. if u == v:
  225. raise nx.NetworkXAlgorithmError("Self links are not supported")
  226. path_len = spl[u].get(v, inf)
  227. return alpha * sum(1 for _ in nx.common_neighbors(G, u, v)) + (
  228. 1 - alpha
  229. ) * (G.number_of_nodes() / path_len)
  230. return _apply_prediction(G, predict, ebunch)
  231. @not_implemented_for("directed")
  232. @not_implemented_for("multigraph")
  233. def preferential_attachment(G, ebunch=None):
  234. r"""Compute the preferential attachment score of all node pairs in ebunch.
  235. Preferential attachment score of `u` and `v` is defined as
  236. .. math::
  237. |\Gamma(u)| |\Gamma(v)|
  238. where $\Gamma(u)$ denotes the set of neighbors of $u$.
  239. Parameters
  240. ----------
  241. G : graph
  242. NetworkX undirected graph.
  243. ebunch : iterable of node pairs, optional (default = None)
  244. Preferential attachment score will be computed for each pair of
  245. nodes given in the iterable. The pairs must be given as
  246. 2-tuples (u, v) where u and v are nodes in the graph. If ebunch
  247. is None then all non-existent edges in the graph will be used.
  248. Default value: None.
  249. Returns
  250. -------
  251. piter : iterator
  252. An iterator of 3-tuples in the form (u, v, p) where (u, v) is a
  253. pair of nodes and p is their preferential attachment score.
  254. Examples
  255. --------
  256. >>> G = nx.complete_graph(5)
  257. >>> preds = nx.preferential_attachment(G, [(0, 1), (2, 3)])
  258. >>> for u, v, p in preds:
  259. ... print(f"({u}, {v}) -> {p}")
  260. (0, 1) -> 16
  261. (2, 3) -> 16
  262. References
  263. ----------
  264. .. [1] D. Liben-Nowell, J. Kleinberg.
  265. The Link Prediction Problem for Social Networks (2004).
  266. http://www.cs.cornell.edu/home/kleinber/link-pred.pdf
  267. """
  268. def predict(u, v):
  269. return G.degree(u) * G.degree(v)
  270. return _apply_prediction(G, predict, ebunch)
  271. @not_implemented_for("directed")
  272. @not_implemented_for("multigraph")
  273. def cn_soundarajan_hopcroft(G, ebunch=None, community="community"):
  274. r"""Count the number of common neighbors of all node pairs in ebunch
  275. using community information.
  276. For two nodes $u$ and $v$, this function computes the number of
  277. common neighbors and bonus one for each common neighbor belonging to
  278. the same community as $u$ and $v$. Mathematically,
  279. .. math::
  280. |\Gamma(u) \cap \Gamma(v)| + \sum_{w \in \Gamma(u) \cap \Gamma(v)} f(w)
  281. where $f(w)$ equals 1 if $w$ belongs to the same community as $u$
  282. and $v$ or 0 otherwise and $\Gamma(u)$ denotes the set of
  283. neighbors of $u$.
  284. Parameters
  285. ----------
  286. G : graph
  287. A NetworkX undirected graph.
  288. ebunch : iterable of node pairs, optional (default = None)
  289. The score will be computed for each pair of nodes given in the
  290. iterable. The pairs must be given as 2-tuples (u, v) where u
  291. and v are nodes in the graph. If ebunch is None then all
  292. non-existent edges in the graph will be used.
  293. Default value: None.
  294. community : string, optional (default = 'community')
  295. Nodes attribute name containing the community information.
  296. G[u][community] identifies which community u belongs to. Each
  297. node belongs to at most one community. Default value: 'community'.
  298. Returns
  299. -------
  300. piter : iterator
  301. An iterator of 3-tuples in the form (u, v, p) where (u, v) is a
  302. pair of nodes and p is their score.
  303. Examples
  304. --------
  305. >>> G = nx.path_graph(3)
  306. >>> G.nodes[0]["community"] = 0
  307. >>> G.nodes[1]["community"] = 0
  308. >>> G.nodes[2]["community"] = 0
  309. >>> preds = nx.cn_soundarajan_hopcroft(G, [(0, 2)])
  310. >>> for u, v, p in preds:
  311. ... print(f"({u}, {v}) -> {p}")
  312. (0, 2) -> 2
  313. References
  314. ----------
  315. .. [1] Sucheta Soundarajan and John Hopcroft.
  316. Using community information to improve the precision of link
  317. prediction methods.
  318. In Proceedings of the 21st international conference companion on
  319. World Wide Web (WWW '12 Companion). ACM, New York, NY, USA, 607-608.
  320. http://doi.acm.org/10.1145/2187980.2188150
  321. """
  322. def predict(u, v):
  323. Cu = _community(G, u, community)
  324. Cv = _community(G, v, community)
  325. cnbors = list(nx.common_neighbors(G, u, v))
  326. neighbors = (
  327. sum(_community(G, w, community) == Cu for w in cnbors) if Cu == Cv else 0
  328. )
  329. return len(cnbors) + neighbors
  330. return _apply_prediction(G, predict, ebunch)
  331. @not_implemented_for("directed")
  332. @not_implemented_for("multigraph")
  333. def ra_index_soundarajan_hopcroft(G, ebunch=None, community="community"):
  334. r"""Compute the resource allocation index of all node pairs in
  335. ebunch using community information.
  336. For two nodes $u$ and $v$, this function computes the resource
  337. allocation index considering only common neighbors belonging to the
  338. same community as $u$ and $v$. Mathematically,
  339. .. math::
  340. \sum_{w \in \Gamma(u) \cap \Gamma(v)} \frac{f(w)}{|\Gamma(w)|}
  341. where $f(w)$ equals 1 if $w$ belongs to the same community as $u$
  342. and $v$ or 0 otherwise and $\Gamma(u)$ denotes the set of
  343. neighbors of $u$.
  344. Parameters
  345. ----------
  346. G : graph
  347. A NetworkX undirected graph.
  348. ebunch : iterable of node pairs, optional (default = None)
  349. The score will be computed for each pair of nodes given in the
  350. iterable. The pairs must be given as 2-tuples (u, v) where u
  351. and v are nodes in the graph. If ebunch is None then all
  352. non-existent edges in the graph will be used.
  353. Default value: None.
  354. community : string, optional (default = 'community')
  355. Nodes attribute name containing the community information.
  356. G[u][community] identifies which community u belongs to. Each
  357. node belongs to at most one community. Default value: 'community'.
  358. Returns
  359. -------
  360. piter : iterator
  361. An iterator of 3-tuples in the form (u, v, p) where (u, v) is a
  362. pair of nodes and p is their score.
  363. Examples
  364. --------
  365. >>> G = nx.Graph()
  366. >>> G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
  367. >>> G.nodes[0]["community"] = 0
  368. >>> G.nodes[1]["community"] = 0
  369. >>> G.nodes[2]["community"] = 1
  370. >>> G.nodes[3]["community"] = 0
  371. >>> preds = nx.ra_index_soundarajan_hopcroft(G, [(0, 3)])
  372. >>> for u, v, p in preds:
  373. ... print(f"({u}, {v}) -> {p:.8f}")
  374. (0, 3) -> 0.50000000
  375. References
  376. ----------
  377. .. [1] Sucheta Soundarajan and John Hopcroft.
  378. Using community information to improve the precision of link
  379. prediction methods.
  380. In Proceedings of the 21st international conference companion on
  381. World Wide Web (WWW '12 Companion). ACM, New York, NY, USA, 607-608.
  382. http://doi.acm.org/10.1145/2187980.2188150
  383. """
  384. def predict(u, v):
  385. Cu = _community(G, u, community)
  386. Cv = _community(G, v, community)
  387. if Cu != Cv:
  388. return 0
  389. cnbors = nx.common_neighbors(G, u, v)
  390. return sum(1 / G.degree(w) for w in cnbors if _community(G, w, community) == Cu)
  391. return _apply_prediction(G, predict, ebunch)
  392. @not_implemented_for("directed")
  393. @not_implemented_for("multigraph")
  394. def within_inter_cluster(G, ebunch=None, delta=0.001, community="community"):
  395. """Compute the ratio of within- and inter-cluster common neighbors
  396. of all node pairs in ebunch.
  397. For two nodes `u` and `v`, if a common neighbor `w` belongs to the
  398. same community as them, `w` is considered as within-cluster common
  399. neighbor of `u` and `v`. Otherwise, it is considered as
  400. inter-cluster common neighbor of `u` and `v`. The ratio between the
  401. size of the set of within- and inter-cluster common neighbors is
  402. defined as the WIC measure. [1]_
  403. Parameters
  404. ----------
  405. G : graph
  406. A NetworkX undirected graph.
  407. ebunch : iterable of node pairs, optional (default = None)
  408. The WIC measure will be computed for each pair of nodes given in
  409. the iterable. The pairs must be given as 2-tuples (u, v) where
  410. u and v are nodes in the graph. If ebunch is None then all
  411. non-existent edges in the graph will be used.
  412. Default value: None.
  413. delta : float, optional (default = 0.001)
  414. Value to prevent division by zero in case there is no
  415. inter-cluster common neighbor between two nodes. See [1]_ for
  416. details. Default value: 0.001.
  417. community : string, optional (default = 'community')
  418. Nodes attribute name containing the community information.
  419. G[u][community] identifies which community u belongs to. Each
  420. node belongs to at most one community. Default value: 'community'.
  421. Returns
  422. -------
  423. piter : iterator
  424. An iterator of 3-tuples in the form (u, v, p) where (u, v) is a
  425. pair of nodes and p is their WIC measure.
  426. Examples
  427. --------
  428. >>> G = nx.Graph()
  429. >>> G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 4), (2, 4), (3, 4)])
  430. >>> G.nodes[0]["community"] = 0
  431. >>> G.nodes[1]["community"] = 1
  432. >>> G.nodes[2]["community"] = 0
  433. >>> G.nodes[3]["community"] = 0
  434. >>> G.nodes[4]["community"] = 0
  435. >>> preds = nx.within_inter_cluster(G, [(0, 4)])
  436. >>> for u, v, p in preds:
  437. ... print(f"({u}, {v}) -> {p:.8f}")
  438. (0, 4) -> 1.99800200
  439. >>> preds = nx.within_inter_cluster(G, [(0, 4)], delta=0.5)
  440. >>> for u, v, p in preds:
  441. ... print(f"({u}, {v}) -> {p:.8f}")
  442. (0, 4) -> 1.33333333
  443. References
  444. ----------
  445. .. [1] Jorge Carlos Valverde-Rebaza and Alneu de Andrade Lopes.
  446. Link prediction in complex networks based on cluster information.
  447. In Proceedings of the 21st Brazilian conference on Advances in
  448. Artificial Intelligence (SBIA'12)
  449. https://doi.org/10.1007/978-3-642-34459-6_10
  450. """
  451. if delta <= 0:
  452. raise nx.NetworkXAlgorithmError("Delta must be greater than zero")
  453. def predict(u, v):
  454. Cu = _community(G, u, community)
  455. Cv = _community(G, v, community)
  456. if Cu != Cv:
  457. return 0
  458. cnbors = set(nx.common_neighbors(G, u, v))
  459. within = {w for w in cnbors if _community(G, w, community) == Cu}
  460. inter = cnbors - within
  461. return len(within) / (len(inter) + delta)
  462. return _apply_prediction(G, predict, ebunch)
  463. def _community(G, u, community):
  464. """Get the community of the given node."""
  465. node_u = G.nodes[u]
  466. try:
  467. return node_u[community]
  468. except KeyError as err:
  469. raise nx.NetworkXAlgorithmError("No community information") from err