def data_dict_to_networkx(data_dict):
  """Returns a networkx graph that contains the stored data.

  Depending on the content of `data_dict`, the returned `networkx` instance has
  the following properties:

  - The nodes feature are placed in the nodes attribute dictionary under the
    "features" key. If the `NODES` fields is `None`, a `None` value is placed
    here;

  - If the `RECEIVERS` field is `None`, no edges are added to the graph.
    Otherwise, edges are added with the order in which they appeared in
    `data_dict` stored in the "index" field of their attributes dictionary;

  - The edges features are placed in the edges attribute dictionary under the
    "features" key. If the `EDGES` field is `None`, a `None` value is placed;

  - The global feature are placed under the key "features" of the graph
    property of the returned instance. If the `GLOBALS` field is `None`, a
    `None` global property is created.

  Args:
    data_dict: A graph `dict` of Numpy data.

  Returns:
    The `networkx.OrderedMultiDiGraph`. The node keys will be the data_dict
    integer node indices.

  Raises:
    ValueError: If the `NODES` field of `data_dict` contains `None`, and
      `data_dict` does not have a `N_NODE` field.
  """
  graph_nx = nx.OrderedMultiDiGraph()
  data_dict = _populate_number_fields(data_dict)
  graph_nx.graph[GRAPH_NX_FEATURES_KEY] = data_dict[GLOBALS]

  if data_dict[NODES] is not None:
    if data_dict[NODES].shape[0] > 0:
      nodes_list = _unstack(data_dict[NODES])
      for i, x in enumerate(nodes_list):
        graph_nx.add_node(i, **{GRAPH_NX_FEATURES_KEY: x})
  elif data_dict[N_NODE] is not None:
    for i in range(data_dict[N_NODE]):
      graph_nx.add_node(i, **{GRAPH_NX_FEATURES_KEY: None})
  else:
    raise ValueError("Cannot create a graph with unspecified number of nodes")

  if data_dict[EDGES] is not None and data_dict[EDGES].shape[0] > 0:
    edges_features = [{  # pylint: disable=g-complex-comprehension
        "index": i,
        GRAPH_NX_FEATURES_KEY: x
    } for i, x in enumerate(_unstack(data_dict[EDGES]))]
    edges_data = zip(data_dict[SENDERS], data_dict[RECEIVERS], edges_features)
    graph_nx.add_edges_from(edges_data)
  elif data_dict[RECEIVERS] is not None and data_dict[RECEIVERS].shape[0] > 0:
    edges_features = [{  # pylint: disable=g-complex-comprehension
        "index": i,
        GRAPH_NX_FEATURES_KEY: None
    } for i in range(data_dict[RECEIVERS].shape[0])]
    edges_data = zip(data_dict[SENDERS], data_dict[RECEIVERS], edges_features)
    graph_nx.add_edges_from(edges_data)

  return graph_nx
Returns a networkx graph that contains the stored data.

  Depending on the content of `data_dict`, the returned `networkx` instance has
  the following properties:

  - The nodes feature are placed in the nodes attribute dictionary under the
    "features" key. If the `NODES` fields is `None`, a `None` value is placed
    here;

  - If the `RECEIVERS` field is `None`, no edges are added to the graph.
    Otherwise, edges are added with the order in which they appeared in
    `data_dict` stored in the "index" field of their attributes dictionary;

  - The edges features are placed in the edges attribute dictionary under the
    "features" key. If the `EDGES` field is `None`, a `None` value is placed;

  - The global feature are placed under the key "features" of the graph
    property of the returned instance. If the `GLOBALS` field is `None`, a
    `None` global property is created.

  Args:
    data_dict: A graph `dict` of Numpy data.

  Returns:
    The `networkx.OrderedMultiDiGraph`. The node keys will be the data_dict
    integer node indices.

  Raises:
    ValueError: If the `NODES` field of `data_dict` contains `None`, and
      `data_dict` does not have a `N_NODE` field.
  
graph_nets/utils_np.py