docstr: Returns local environment graph using networkx and
  tolerance specified.
  
  Parameters
  ----------
  pos: np.ndarray
      ns x 3. coordinates of positions. ns is the number of sites.
      sitetypes: ns. sitetype for each site
  sitetypes: List[str]
      List of site properties mentioning if it is a active site "Ai"
      or spectator site "Si".
  
  Returns
  ------
  G: networkx.classes.graph.Graph
      networkx graph used for matching site positions in
      datum.
docstr_tok: ["Returns","local","environment","graph","using","networkx","and","tolerance","specified",".","Parameters","--","--","--","--","--","pos",":","np.ndarray","ns","x","3.","coordinates","of","positions",".","ns","is","the","number","of","sites",".","sitetypes",":","ns",".","sitetype","for","each","site","sitetypes",":","List","[","str","]","List","of","site","properties","mentioning","if","it","is","a","active","site","``","Ai","''","or","spectator","site","``","Si","''",".","Returns","--","--","--","G",":","networkx.classes.graph.Graph","networkx","graph","used","for","matching","site","positions","in","datum","."]
code: def _construct_graph(self, pos: np.ndarray, sitetypes: List[str]):
      """
      Returns local environment graph using networkx and
      tolerance specified.
  
      Parameters
      ----------
      pos: np.ndarray
          ns x 3. coordinates of positions. ns is the number of sites.
          sitetypes: ns. sitetype for each site
      sitetypes: List[str]
          List of site properties mentioning if it is a active site "Ai"
          or spectator site "Si".
  
      Returns
      ------
      G: networkx.classes.graph.Graph
          networkx graph used for matching site positions in
          datum.
      """
      try:
          import networkx as nx
      except:
          raise ImportError(
              "This class requires networkx to be installed."
          )
  
      # construct graph
      G = nx.Graph()
      dists = cdist([[0, 0, 0]], pos - np.mean(pos, 0))[0]
      sdists = np.sort(dists)
      uniquedists = sdists[
          ~(
              np.triu(np.abs(sdists[:, None] - sdists) <= self.grtol, 1)
          ).any(0)
      ]
      orderfromcenter = np.digitize(dists, uniquedists)
      # Add nodes
      for i, o in enumerate(orderfromcenter):
          G.add_node(i, n=str(o) + sitetypes[i])
      # Add edge. distance is edge attribute
      dists = pdist(pos)
      n = 0
      for i in range(len(sitetypes)):
          for j in range(i + 1, len(sitetypes)):
              if dists[n] < self.mindists[
                  frozenset((sitetypes[i], sitetypes[j]))
              ] or (
                  abs(
                      self.mindists[
                          frozenset((sitetypes[i], sitetypes[j]))
                      ]
                      - dists[n]
                  )
                  <= self.Gatol + self.Grtol * abs(dists[n])
              ):
                  G.add_edge(i, j, d=dists[n])
              n += 1
      return G
  
imports: import numpy
  import logging
  from deepchem.feat import MaterialStructureFeaturizer
  from collections import defaultdict
  from typing import List, Dict, Tuple, DefaultDict, Any
  from deepchem.utils.typing import PymatgenStructure
  from deepchem.feat.graph_data import GraphData
  from scipy.spatial.distance import pdist, squareform, cdist
  from scipy.spatial.transform import Rotation
label_desc: Use image node_id 2 for calling the _SiteEnvironment obj's underlying member method code with example usage: obj._construct_graph(pos, sitetypes) and returns: G