class_docstr: Base class for Bayesian Network (BN), a probabilistic weighted DAG where nodes represent variables,
  edges represent the causal relationships between variables.
  
  ``BayesianNetwork`` stores nodes with their possible states, edges and
  conditional probability distributions (CPDs) of each node.
  
  ``BayesianNetwork`` is built on top of the ``StructureModel``, which is an extension of ``networkx.DiGraph``
  (see :func:`causalnex.structure.structuremodel.StructureModel`).
  
  In order to define the ``BayesianNetwork``, users should provide a relevant ``StructureModel``.
  Once ``BayesianNetwork`` is initialised, no changes to the ``StructureModel`` can be made
  and CPDs can be learned from the data.
  
  The learned CPDs can be then used for likelihood estimation and predictions.
  
  Example:
  ::
      >>> # Create a Bayesian Network with a manually defined DAG.
      >>> from causalnex.structure import StructureModel
      >>> from causalnex.network import BayesianNetwork
      >>>
      >>> sm = StructureModel()
      >>> sm.add_edges_from([
      >>>                    ('rush_hour', 'traffic'),
      >>>                    ('weather', 'traffic')
      >>>                    ])
      >>> bn = BayesianNetwork(sm)
      >>> # A created ``BayesianNetwork`` stores nodes and edges defined by the ``StructureModel``
      >>> bn.nodes
      ['rush_hour', 'traffic', 'weather']
      >>>
      >>> bn.edges
      [('rush_hour', 'traffic'), ('weather', 'traffic')]
      >>> # A ``BayesianNetwork`` doesn't store any CPDs yet
      >>> bn.cpds
      >>> {}
      >>>
      >>> # Learn the nodes' states from the data
      >>> import pandas as pd
      >>> data = pd.DataFrame({
      >>>                      'rush_hour': [True, False, False, False, True, False, True],
      >>>                      'weather': ['Terrible', 'Good', 'Bad', 'Good', 'Bad', 'Bad', 'Good'],
      >>>                      'traffic': ['heavy', 'light', 'heavy', 'light', 'heavy', 'heavy', 'heavy']
      >>>                      })
      >>> bn = bn.fit_node_states(data)
      >>> bn.node_states
      {'rush_hour': {False, True}, 'weather': {'Bad', 'Good', 'Terrible'}, 'traffic': {'heavy', 'light'}}
      >>> # Learn the CPDs from the data
      >>> bn = bn.fit_cpds(data)
      >>> # Use the learned CPDs to make predictions on the unseen data
      >>> test_data = pd.DataFrame({
      >>>                           'rush_hour': [False, False, True, True],
      >>>                           'weather': ['Good', 'Bad', 'Good', 'Bad']
      >>>                           })
      >>> bn.predict(test_data, "traffic").to_dict()
      >>> {'traffic_prediction': {0: 'light', 1: 'heavy', 2: 'heavy', 3: 'heavy'}}
      >>> bn.predict_probability(test_data, "traffic").to_dict()
      {'traffic_prediction': {0: 'light', 1: 'heavy', 2: 'heavy', 3: 'heavy'}}
      {'traffic_light': {0: 0.75, 1: 0.25, 2: 0.3333333333333333, 3: 0.3333333333333333},
       'traffic_heavy': {0: 0.25, 1: 0.75, 2: 0.6666666666666666, 3: 0.6666666666666666}}
docstr: Create a ``BayesianNetwork`` with a DAG defined by ``StructureModel``.
  
  Args:
      structure: a graph representing a causal relationship between variables.
                 In the structure
                     - cycles are not allowed;
                     - multiple (parallel) edges are not allowed;
                     - isolated nodes and multiple components are not allowed.
  
  Raises:
      ValueError: If the structure is not a connected DAG.
docstr_tok: ["Create","a","``","BayesianNetwork","``","with","a","DAG","defined","by","``","StructureModel","``",".","Args",":","structure",":","a","graph","representing","a","causal","relationship","between","variables",".","In","the","structure","-","cycles","are","not","allowed",";","-","multiple","(","parallel",")","edges","are","not","allowed",";","-","isolated","nodes","and","multiple","components","are","not","allowed",".","Raises",":","ValueError",":","If","the","structure","is","not","a","connected","DAG","."]
code: def __init__(self, structure: StructureModel):
      """
      Create a ``BayesianNetwork`` with a DAG defined by ``StructureModel``.
  
      Args:
          structure: a graph representing a causal relationship between variables.
                     In the structure
                         - cycles are not allowed;
                         - multiple (parallel) edges are not allowed;
                         - isolated nodes and multiple components are not allowed.
  
      Raises:
          ValueError: If the structure is not a connected DAG.
      """
      n_components = nx.number_weakly_connected_components(structure)
  
      if n_components > 1:
          raise ValueError(
              f"The given structure has {n_components} separated graph components. "
              "Please make sure it has only one."
          )
  
      if not nx.is_directed_acyclic_graph(structure):
          cycle = nx.find_cycle(structure)
          raise ValueError(
              f"The given structure is not acyclic. Please review the following cycle: {cycle}"
          )
  
      # _node_states is a Dict in the form `dict: {node: dict: {state: index}}`.
      # Underlying libraries expect all states to be integers from zero, and
      # thus this dict is used to convert from state -> idx, and then back from idx -> state as required
      self._node_states = {}  # type: Dict[str: Dict[Hashable, int]]
      self._structure = structure
  
      # _model is a pgmpy Bayesian Model.
      # It is used for:
      #                - probability fitting
      #                - predictions
      self._model = BayesianModel()
      self._model.add_edges_from(structure.edges)
  
imports: import copy
  import re
  from typing import Dict, Hashable, List, Optional, Set, Tuple, Union
  import networkx
  import pandas
  from pgmpy.estimators import BayesianEstimator, MaximumLikelihoodEstimator
  from pgmpy.factors.discrete.CPD import TabularCPD
  from pgmpy.models import BayesianNetwork
  from causalnex.estimator.em import EMSingleLatentVariable
  from causalnex.structure import StructureModel
  from causalnex.utils.pgmpy_utils import pd_to_tabular_cpd
label_desc: Use image node_id 1 to create a new BayesianNetwork object  with example: obj = BayesianNetwork(structure)