class_docstr: Implements the *TextRank* algorithm defined by
  [[mihalcea04textrank]](https://derwen.ai/docs/ptr/biblio/#mihalcea04textrank),
  deployed as a `spaCy` pipeline component.
  
  This class does not get called directly; instantiate its factory
  instead.
      
class_docstr_tok: ["Implements","the","*","TextRank","*","algorithm","defined","by","[","[","mihalcea04textrank","]","]","(","https",":","\/\/derwen.ai\/docs\/ptr\/biblio\/","#","mihalcea04textrank",")",",","deployed","as","a","`","spaCy","`","pipeline","component",".","This","class","does","not","get","called","directly",";","instantiate","its","factory","instead","."]
docstr: Iterate through each sentence in the doc, constructing a
  [*lemma graph*](https://derwen.ai/docs/ptr/glossary/#lemma-graph)
  then returning the top-ranked phrases.
  
  This method represents the heart of the *TextRank* algorithm.
  
      returns:
  list of ranked phrases, in descending order
          
code: def calc_textrank(self) -> typing.List[Phrase]:
      """
      Iterate through each sentence in the doc, constructing a
      [*lemma graph*](https://derwen.ai/docs/ptr/glossary/#lemma-graph)
      then returning the top-ranked phrases.
  
      This method represents the heart of the *TextRank* algorithm.
  
          returns:
      list of ranked phrases, in descending order
      """
      t0 = time.time()
      self.reset()
      self.lemma_graph = self._construct_graph()
  
      # to run the algorithm, we use the NetworkX implementation
      # for PageRank (i.e., based on eigenvector centrality)
      # to calculate a rank for each node in the lemma graph
      self.ranks = nx.pagerank(
          self.lemma_graph,
          personalization=self.get_personalization(),
      )
  
      # agglomerate the lemmas ranked in the lemma graph into ranked
      # phrases, leveraging information from earlier stages of the
      # pipeline: noun chunks and named entities
      nc_phrases: typing.Dict[Span, float] = {}
  
      try:
          nc_phrases = self._collect_phrases(
              self.doc.noun_chunks, self.ranks
          )
      except NotImplementedError as ex:
          # some languages don't have `noun_chunks` support in spaCy models, e.g. "ru"
          ic.disable()
          ic(ex)
          ic.enable()
  
      ent_phrases: typing.Dict[Span, float] = self._collect_phrases(
          self.doc.ents, self.ranks
      )
      all_phrases: typing.Dict[Span, float] = {
          **nc_phrases,
          **ent_phrases,
      }
  
      # since noun chunks can be expressed in different ways (e.g., may
      # have articles or prepositions), we need to find a minimum span
      # for each phrase based on combinations of lemmas
      raw_phrase_list: typing.List[Phrase] = self._get_min_phrases(
          all_phrases
      )
      phrase_list: typing.List[Phrase] = sorted(
          raw_phrase_list, key=lambda p: p.rank, reverse=True
      )
  
      t1 = time.time()
      self.elapsed_time = (t1 - t0) * 1000.0
  
      return phrase_list
  
imports: from collections import Counter, defaultdict, OrderedDict
  from dataclasses import dataclass
  import json
  import math
  import pathlib
  import time
  import typing
  from icecream import ic
  from spacy.tokens import Doc, Span, Token
  import graphviz
  import networkx
  from .util import groupby_apply, default_scrubber
label_desc: Use image node_id 3 for calling the BaseTextRank obj's underlying member method code with example usage: obj.calc_textrank() and returns: phrase_list