class_docstr: Implements the *TopicRank* algorithm described by
  [[bougouin-etal-2013-topicrank]](https://derwen.ai/docs/ptr/biblio/#bougouin-etal-2013-topicrank)
  deployed as a `spaCy` pipeline component.
  
  This class does not get called directly; instantiate its factory
  instead.
  
  Algorithm Overview:
  
  1. Preprocessing: Sentence segmentation, word tokenization, POS tagging.
     After this stage, we have preprocessed text.
  2. Candidate extraction: Extract sequences of nouns and adjectives (i.e. noun chunks)
     After this stage, we have a list of keyphrases that may be topics.
  3. Candidate clustering: Hierarchical Agglomerative Clustering algorithm with average
     linking using simple set-based overlap of lemmas. Similarity is achieved at > 25%
     overlap. **Note**: PyTextRank deviates from the original algorithm here, which uses
     stems rather than lemmas.
     After this stage, we have a list of topics.
  4. Candidate ranking: Apply *TextRank* on a complete graph, with topics as nodes
     (i.e. clusters derived in the last step), where edge weights are higher between
     topics that appear closer together within the document.
     After this stage, we have a ranked list of topics.
  5. Candidate selection: Select the first occurring keyphrase from each topic to
     represent that topic.
     After this stage, we have a ranked list of topics, with a keyphrase to represent
     the topic.
      
docstr: Return a list of spans with candidate topics, such that the start of
  each candidate is a noun chunk that was trimmed of stopwords or tokens
  with POS tags that we wish to ignore.
  
      returns:
  list of candidate spans
          
docstr_tok: ["Return","a","list","of","spans","with","candidate","topics",",","such","that","the","start","of","each","candidate","is","a","noun","chunk","that","was","trimmed","of","stopwords","or","tokens","with","POS","tags","that","we","wish","to","ignore",".","returns",":","list","of","candidate","spans"]
code: def _get_candidates(self) -> typing.List[Span]:
      """
      Return a list of spans with candidate topics, such that the start of
      each candidate is a noun chunk that was trimmed of stopwords or tokens
      with POS tags that we wish to ignore.
  
          returns:
      list of candidate spans
      """
      candidates: typing.List[Span] = []
  
      try:
          noun_chunks = list(self.doc.noun_chunks)
  
          for chunk in noun_chunks:
              for token in chunk:
                  if self._keep_token(token):
                      candidates.append(self.doc[token.i : chunk.end])
                      break
      except NotImplementedError as ex:
          # some languages don't have `noun_chunks` support in spaCy models, e.g. "ru"
          ic.disable()
          ic(ex)
          ic.enable()
  
      return candidates
  
imports: from collections import defaultdict
  from functools import lru_cache
  import time
  import typing
  from icecream import ic
  from scipy.cluster.hierarchy import fcluster, linkage
  from scipy.spatial.distance import pdist
  from spacy.tokens import Doc, Span
  import networkx
  from .base import BaseTextRank, BaseTextRankFactory, Phrase, StopWordsLike
label_desc: Use image node_id 3 for calling the TopicRank obj's underlying member method code with example usage: obj._get_candidates() and returns: candidates