class_docstr: Args:
      ids (sequence): the IDs of each element
      sources (numpy.ndarray): the ilocs of the source of each edge
      targets (numpy.ndarray): the ilocs of the target of each edge
      weight (numpy.ndarray): the weight of each edge
      type_info (list of tuple of type name, numpy array): the associated feature vectors of each type, where the size of the first dimension defines the elements of that type
      number_of_nodes (int): the total number of nodes in the graph
class_docstr_tok: ["Args",":","ids","(","sequence",")",":","the","IDs","of","each","element","sources","(","numpy.ndarray",")",":","the","ilocs","of","the","source","of","each","edge","targets","(","numpy.ndarray",")",":","the","ilocs","of","the","target","of","each","edge","weight","(","numpy.ndarray",")",":","the","weight","of","each","edge","type_info","(","list","of","tuple","of","type","name",",","numpy","array",")",":","the","associated","feature","vectors","of","each","type",",","where","the","size","of","the","first","dimension","defines","the","elements","of","that","type","number_of_nodes","(","int",")",":","the","total","number","of","nodes","in","the","graph"]
code: def _create_undirected_adj_lists(self):
      # record the edge ilocs of both-direction edges
      num_edges = len(self.targets)
  
      # the dtype of the edge_ilocs
      # the argsort results in integers in [0, 2 * num_edges),
      # so the dtype potentially needs to be slightly larger
      dtype = np.min_scalar_type(2 * len(self.sources))
  
      # sentinel masks out node_ilocs so must be the same type as node_ilocs node edge_ilocs
      sentinel = np.cast[np.min_scalar_type(self.number_of_nodes)](-1)
      self_loops = self.sources == self.targets
      num_self_loops = self_loops.sum()
  
      combined = np.concatenate([self.sources, self.targets])
      # mask out duplicates of self loops
      combined[num_edges:][self_loops] = sentinel
  
      flat_array = np.argsort(combined).astype(dtype, copy=False)
  
      # get targets without self loops inplace
      # sentinels are sorted to the end
      filtered_targets = combined[num_edges:]
      filtered_targets.sort()
  
      # remove the sentinels if there are any (the full array will be retained
      # forever; we're assume that there's self loops are a small fraction
      # of the total number of edges)
      if num_self_loops > 0:
          flat_array = flat_array[:-num_self_loops]
          filtered_targets = filtered_targets[:-num_self_loops]
  
      flat_array %= num_edges
      neigh_counts = np.bincount(
          self.sources, minlength=self.number_of_nodes
      )
      neigh_counts += np.bincount(
          filtered_targets, minlength=self.number_of_nodes
      )
      splits = np.zeros(len(neigh_counts) + 1, dtype=dtype)
      splits[1:] = np.cumsum(neigh_counts, dtype=dtype)
  
      return FlatAdjacencyList(flat_array, splits)
  
imports: import itertools
  from collections import defaultdict
  import numpy
  import pandas
  import scipy.sparse
  from ..globalvar import SOURCE, TARGET, WEIGHT, TYPE_ATTR_NAME
  from .validation import require_dataframe_has_columns, comma_sep
label_desc: Use image node_id 5 for calling the EdgeData obj's underlying member method code with example usage: obj._create_undirected_adj_lists() and returns: FlatAdjacencyList