code: def collapse_by(
      self: Plottable,
      parent: Union[str, int],
      start_node: Union[str, int],
      attribute: Union[str, int],
      column: Union[str, int],
      seen: dict,
      self_edges: bool = False,
      unwrap: bool = False,
      verbose: bool = True,
  ) -> Plottable:
      """
          Main call in collapse.py, collapses nodes and edges by attribute, and returns normalized graphistry object.
  
      :param self: graphistry instance
      :param parent: parent node to start traversal, in main call, this is set to child.
      :param start_node:
      :param attribute: attribute to collapse by
      :param column: column in nodes dataframe to collapse over.
      :param seen: dict of previously collapsed pairs -- {n1, n2) is seen as different from (n2, n1)
      :param verbose: bool, default True
  
      :returns graphistry instance with collapsed and normalized nodes.
      """
      from time import time
  
      g = copy.deepcopy(self.bind())
      g = check_default_columns_present_and_coerce_to_string(g)
  
      n_edges = len(g._edges)
      complexity_min = int(n_edges * np.log(n_edges))
      complexity_max = int(n_edges ** (3 / 2))
      if (VERBOSE or verbose) and n_edges > 5000:
          logger.info("-" * 108)
          logger.info(
              "This Algorithm runs approximately between n_edges*log(n_edges) and n_edges**(3/2) in un-normalized units"
          )
          logger.info(
              f"Hence, in this case, between O({complexity_min/n_edges:.2f} - {complexity_max/n_edges:.2f}) for "
              f"this graph normalized by {n_edges} edges"
          )
          logger.info(
              "It is not recommended for large graphs -- one can expect a modern laptop CPU to scan 1-6k edges per minute"
          )
          logger.info(
              f"Here we expect collapse to run in under {n_edges/1000:.3f} minutes"
          )
          logger.info("*" * 100)
      t = time()
  
      collapse_algo(g, parent, start_node, attribute, column, seen)
  
      t2 = time()
      delta_mins = (t2 - t) / 60
      if VERBOSE or verbose:
          logger.info("-" * 80)
          logger.info(
              f"Total Collapse took {delta_mins:.2f} minutes or {n_edges/delta_mins:.2f} edges per minute"
          )
      return normalize_graph(g, self_edges=self_edges, unwrap=unwrap)
  
imports: from typing import Union, Optional, List
  import copy, logging, pandas, numpy
  from graphistry.PlotterBase import Plottable
label_desc: Use image node_id 19 for calling a global function with example usage: collapse_by(parent, start_node, attribute, column, seen, _edges, unwrap, verbose) and returns: normalize_graph