spearman.py 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. # Natural Language Toolkit: Spearman Rank Correlation
  2. #
  3. # Copyright (C) 2001-2020 NLTK Project
  4. # Author: Joel Nothman <jnothman@student.usyd.edu.au>
  5. # URL: <http://nltk.org>
  6. # For license information, see LICENSE.TXT
  7. """
  8. Tools for comparing ranked lists.
  9. """
  10. def _rank_dists(ranks1, ranks2):
  11. """Finds the difference between the values in ranks1 and ranks2 for keys
  12. present in both dicts. If the arguments are not dicts, they are converted
  13. from (key, rank) sequences.
  14. """
  15. ranks1 = dict(ranks1)
  16. ranks2 = dict(ranks2)
  17. for k in ranks1:
  18. try:
  19. yield k, ranks1[k] - ranks2[k]
  20. except KeyError:
  21. pass
  22. def spearman_correlation(ranks1, ranks2):
  23. """Returns the Spearman correlation coefficient for two rankings, which
  24. should be dicts or sequences of (key, rank). The coefficient ranges from
  25. -1.0 (ranks are opposite) to 1.0 (ranks are identical), and is only
  26. calculated for keys in both rankings (for meaningful results, remove keys
  27. present in only one list before ranking)."""
  28. n = 0
  29. res = 0
  30. for k, d in _rank_dists(ranks1, ranks2):
  31. res += d * d
  32. n += 1
  33. try:
  34. return 1 - (6 * res / (n * (n * n - 1)))
  35. except ZeroDivisionError:
  36. # Result is undefined if only one item is ranked
  37. return 0.0
  38. def ranks_from_sequence(seq):
  39. """Given a sequence, yields each element with an increasing rank, suitable
  40. for use as an argument to ``spearman_correlation``.
  41. """
  42. return ((k, i) for i, k in enumerate(seq))
  43. def ranks_from_scores(scores, rank_gap=1e-15):
  44. """Given a sequence of (key, score) tuples, yields each key with an
  45. increasing rank, tying with previous key's rank if the difference between
  46. their scores is less than rank_gap. Suitable for use as an argument to
  47. ``spearman_correlation``.
  48. """
  49. prev_score = None
  50. rank = 0
  51. for i, (key, score) in enumerate(scores):
  52. try:
  53. if abs(score - prev_score) > rank_gap:
  54. rank = i
  55. except TypeError:
  56. pass
  57. yield key, rank
  58. prev_score = score