docstr: Generate g._nodes based on g._edges
  
  Uses g._node for node id if exists, else 'id'
  
  Edges must be dataframe-like: cudf, pandas, ...
  
  When reuse=True and g._nodes is not None, use it
  
  **Example: Generate nodes**
  
      ::
  
          edges = pd.DataFrame({'s': ['a','b','c','d'], 'd': ['c','c','e','e']})
          g = graphistry.edges(edges, 's', 'd')
          print(g._nodes)  # None
          g2 = g.materialize_nodes()
          print(g2._nodes)  # pd.DataFrame
docstr_tok: ["Generate","g._nodes","based","on","g._edges","Uses","g._node","for","node","id","if","exists",",","else","'id'","Edges","must","be","dataframe-like",":","cudf",",","pandas",",","...","When","reuse=True","and","g._nodes","is","not","None",",","use","it","*","*","Example",":","Generate","nodes","*","*",":",":","edges","=","pd.DataFrame","(","{","'s","'",":","[","'","a","'",",","'","b","'",",","'","c","'",",","'d","'","]",",","'d","'",":","[","'","c","'",",","'","c","'",",","'","e","'",",","'","e","'","]","}",")","g","=","graphistry.edges","(","edges",",","'s","'",",","'d","'",")","print","(","g._nodes",")","#","None","g2","=","g.materialize_nodes","(",")","print","(","g2._nodes",")","#","pd.DataFrame"]
code: def materialize_nodes(
      self,
      reuse: bool = True,
      engine: Union[EngineAbstract, str] = EngineAbstract.AUTO,
  ) -> "Plottable":
      """
      Generate g._nodes based on g._edges
  
      Uses g._node for node id if exists, else 'id'
  
      Edges must be dataframe-like: cudf, pandas, ...
  
      When reuse=True and g._nodes is not None, use it
  
      **Example: Generate nodes**
  
          ::
  
              edges = pd.DataFrame({'s': ['a','b','c','d'], 'd': ['c','c','e','e']})
              g = graphistry.edges(edges, 's', 'd')
              print(g._nodes)  # None
              g2 = g.materialize_nodes()
              print(g2._nodes)  # pd.DataFrame
  
      """
  
      if isinstance(engine, str):
          engine = EngineAbstract(engine)
  
      g = self
      if g._edges is None:
          raise ValueError("Missing edges")
      if g._source is None or g._destination is None:
          raise ValueError(
              "Missing source/destination bindings; set via .bind() or .edges()"
          )
      if len(g._edges) == 0:
          return g
      # TODO use built-ins for igraph/nx/...
  
      if reuse:
          if g._nodes is not None and len(g._nodes) > 0:
              if g._node is None:
                  logger.warning(
                      "Must set node id binding, not just nodes; set via .bind() or .nodes()"
                  )
                  # raise ValueError('Must set node id binding, not just nodes; set via .bind() or .nodes()')
              else:
                  return g
  
      node_id = g._node if g._node is not None else "id"
      engine_concrete: Engine
      if engine == EngineAbstract.AUTO:
          if isinstance(g._edges, pd.DataFrame):
              engine_concrete = Engine.PANDAS
          else:
              try:
                  import cudf
  
                  if isinstance(g._edges, cudf.DataFrame):
                      engine_concrete = Engine.CUDF
              except ImportError:
                  pass
              if engine == EngineAbstract.AUTO:
                  raise ValueError(
                      "Could not determine engine for edges, expected pandas or cudf dataframe, got: {}".format(
                          type(g._edges)
                      )
                  )
      else:
          engine_concrete = Engine(engine.value)
  
      if engine_concrete == Engine.PANDAS:
          concat_df = pd.concat(
              [g._edges[g._source], g._edges[g._destination]]
          )
      elif engine_concrete == Engine.CUDF:
          import cudf
  
          if isinstance(g._edges, cudf.DataFrame):
              edges_gdf = g._edges
          elif isinstance(g._edges, pd.DataFrame):
              edges_gdf = cudf.from_pandas(g._edges)
          else:
              raise ValueError(
                  "Unexpected edges type; convert edges to cudf.DataFrame"
              )
          concat_df = cudf.concat(
              [
                  edges_gdf[g._source].rename(node_id),
                  edges_gdf[g._destination].rename(node_id),
              ]
          )
      else:
          raise ValueError(
              "Expected engine to be pandas or cudf, got: {}".format(
                  engine_concrete
              )
          )
      nodes_df = (
          concat_df.rename(node_id)
          .drop_duplicates()
          .to_frame()
          .reset_index(drop=True)
      )
      return g.nodes(nodes_df, node_id)
  
imports: import logging, numpy, pandas
  from typing import Any, List, Union, TYPE_CHECKING
  from typing_extensions import Literal
  from graphistry.Engine import Engine, EngineAbstract
  from graphistry.Plottable import Plottable
  from graphistry.util import setup_logger
  from .chain import chain
  from .collapse import collapse_by
  from .hop import hop
  from .filter_by_dict import filter_edges_by_dict, filter_nodes_by_dict
label_desc: Use image node_id 2 for calling the ComputeMixin obj's underlying member method code with example usage: obj.materialize_nodes(reuse, engine) and returns: g, g, g