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: Call `fit_node_states` and then `fit_cpds`.
  
  Args:
      data: dataframe containing one column per node in the Bayesian Network.
      method: how to fit probabilities. One of:
              - "MaximumLikelihoodEstimator": fit probabilities using Maximum Likelihood Estimation;
              - "BayesianEstimator": fit probabilities using Bayesian Parameter Estimation. Use bayes_prior.
      bayes_prior: how to construct the Bayesian prior used by method="BayesianEstimator". One of:
                   - "K2": shorthand for dirichlet where all pseudo_counts are 1
                           regardless of variable cardinality;
                   - "BDeu": equivalent of using dirichlet and using uniform 'pseudo_counts' of
                             `equivalent_sample_size / (node_cardinality * np.prod(parents_cardinalities))`
                             for each node. Use equivelant_sample_size.
      equivalent_sample_size: used by BDeu bayes_prior to compute pseudo_counts.
  
  Returns:
      self
docstr_tok: ["Call","`","fit_node_states","`","and","then","`","fit_cpds","`",".","Args",":","data",":","dataframe","containing","one","column","per","node","in","the","Bayesian","Network",".","method",":","how","to","fit","probabilities",".","One","of",":","-","``","MaximumLikelihoodEstimator","''",":","fit","probabilities","using","Maximum","Likelihood","Estimation",";","-","``","BayesianEstimator","''",":","fit","probabilities","using","Bayesian","Parameter","Estimation",".","Use","bayes_prior",".","bayes_prior",":","how","to","construct","the","Bayesian","prior","used","by","method=","''","BayesianEstimator","''",".","One","of",":","-","``","K2","''",":","shorthand","for","dirichlet","where","all","pseudo_counts","are","1","regardless","of","variable","cardinality",";","-","``","BDeu","''",":","equivalent","of","using","dirichlet","and","using","uniform","'pseudo_counts","'","of","`","equivalent_sample_size","\/","(","node_cardinality","*","np.prod","(","parents_cardinalities",")",")","`","for","each","node",".","Use","equivelant_sample_size",".","equivalent_sample_size",":","used","by","BDeu","bayes_prior","to","compute","pseudo_counts",".","Returns",":","self"]
code: def fit_node_states_and_cpds(
      self,
      data: pd.DataFrame,
      method: str = "MaximumLikelihoodEstimator",
      bayes_prior: Optional[str] = None,
      equivalent_sample_size: Optional[int] = None,
  ) -> "BayesianNetwork":
      """
      Call `fit_node_states` and then `fit_cpds`.
  
      Args:
          data: dataframe containing one column per node in the Bayesian Network.
          method: how to fit probabilities. One of:
                  - "MaximumLikelihoodEstimator": fit probabilities using Maximum Likelihood Estimation;
                  - "BayesianEstimator": fit probabilities using Bayesian Parameter Estimation. Use bayes_prior.
          bayes_prior: how to construct the Bayesian prior used by method="BayesianEstimator". One of:
                       - "K2": shorthand for dirichlet where all pseudo_counts are 1
                               regardless of variable cardinality;
                       - "BDeu": equivalent of using dirichlet and using uniform 'pseudo_counts' of
                                 `equivalent_sample_size / (node_cardinality * np.prod(parents_cardinalities))`
                                 for each node. Use equivelant_sample_size.
          equivalent_sample_size: used by BDeu bayes_prior to compute pseudo_counts.
  
      Returns:
          self
      """
      return self.fit_node_states(data).fit_cpds(
          data, method, bayes_prior, equivalent_sample_size
      )
  
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 12 for calling the BayesianNetwork obj's underlying member method code with example usage: obj.fit_node_states_and_cpds(data, method, bayes_prior, equivalent_sample_size) and returns: self