class_docstr: Initializes a Bayesian Network.
  A models stores nodes and edges with conditional probability
  distribution (cpd) and other attributes.
  
  models hold directed edges.  Self loops are not allowed neither
  multiple (parallel) edges.
  
  Nodes can be any hashable python object.
  
  Edges are represented as links between nodes.
  
  Parameters
  ----------
  ebunch: input graph
      Data to initialize graph.  If ebunch=None (default) an empty
      graph is created.  The ebunch can be an edge list, or any
      NetworkX graph object.
  
  latents: list, array-like
      List of variables which are latent (i.e. unobserved) in the model.
  
  Examples
  --------
  Create an empty Bayesian Network with no nodes and no edges.
  
  >>> from pgmpy.models import BayesianNetwork
  >>> G = BayesianNetwork()
  
  G can be grown in several ways.
  
  **Nodes:**
  
  Add one node at a time:
  
  >>> G.add_node('a')
  
  Add the nodes from any container (a list, set or tuple or the nodes
  from another graph).
  
  >>> G.add_nodes_from(['a', 'b'])
  
  **Edges:**
  
  G can also be grown by adding edges.
  
  Add one edge,
  
  >>> G.add_edge('a', 'b')
  
  a list of edges,
  
  >>> G.add_edges_from([('a', 'b'), ('b', 'c')])
  
  If some edges connect nodes not yet in the model, the nodes
  are added automatically.  There are no errors when adding
  nodes or edges that already exist.
  
  **Shortcuts:**
  
  Many common graph features allow python syntax for speed reporting.
  
  >>> 'a' in G     # check if node in graph
  True
  >>> len(G)  # number of nodes in graph
  3
class_docstr_tok: ["Initializes","a","Bayesian","Network",".","A","models","stores","nodes","and","edges","with","conditional","probability","distribution","(","cpd",")","and","other","attributes",".","models","hold","directed","edges",".","Self","loops","are","not","allowed","neither","multiple","(","parallel",")","edges",".","Nodes","can","be","any","hashable","python","object",".","Edges","are","represented","as","links","between","nodes",".","Parameters","--","--","--","--","--","ebunch",":","input","graph","Data","to","initialize","graph",".","If","ebunch=None","(","default",")","an","empty","graph","is","created",".","The","ebunch","can","be","an","edge","list",",","or","any","NetworkX","graph","object",".","latents",":","list",",","array-like","List","of","variables","which","are","latent","(","i.e",".","unobserved",")","in","the","model",".","Examples","--","--","--","--","Create","an","empty","Bayesian","Network","with","no","nodes","and","no","edges",".",">",">",">","from","pgmpy.models","import","BayesianNetwork",">",">",">","G","=","BayesianNetwork","(",")","G","can","be","grown","in","several","ways",".","*","*","Nodes",":","*","*","Add","one","node","at","a","time",":",">",">",">","G.add_node","(","'","a","'",")","Add","the","nodes","from","any","container","(","a","list",",","set","or","tuple","or","the","nodes","from","another","graph",")",".",">",">",">","G.add_nodes_from","(","[","'","a","'",",","'","b","'","]",")","*","*","Edges",":","*","*","G","can","also","be","grown","by","adding","edges",".","Add","one","edge",",",">",">",">","G.add_edge","(","'","a","'",",","'","b","'",")","a","list","of","edges",",",">",">",">","G.add_edges_from","(","[","(","'","a","'",",","'","b","'",")",",","(","'","b","'",",","'","c","'",")","]",")","If","some","edges","connect","nodes","not","yet","in","the","model",",","the","nodes","are","added","automatically",".","There","are","no","errors","when","adding","nodes","or","edges","that","already","exist",".","*","*","Shortcuts",":","*","*","Many","common","graph","features","allow","python","syntax","for","speed","reporting",".",">",">",">","'","a","'","in","G","#","check","if","node","in","graph","True",">",">",">","len","(","G",")","#","number","of","nodes","in","graph","3"]
docstr: Applies the do operation. The do operation removes all incoming edges
  to variables in `nodes` and marginalizes their CPDs to only contain the
  variable itself.
  
  Parameters
  ----------
  nodes : list, array-like
      The names of the nodes to apply the do-operator for.
  
  inplace: boolean (default: False)
      If inplace=True, makes the changes to the current object,
      otherwise returns a new instance.
  
  Returns
  -------
  Modified network: pgmpy.models.BayesianNetwork or None
      If inplace=True, modifies the object itself else returns an instance of
      BayesianNetwork modified by the do operation.
  
  Examples
  --------
  >>> from pgmpy.utils import get_example_model
  >>> asia = get_example_model('asia')
  >>> asia.edges()
  OutEdgeView([('asia', 'tub'), ('tub', 'either'), ('smoke', 'lung'), ('smoke', 'bronc'),
               ('lung', 'either'), ('bronc', 'dysp'), ('either', 'xray'), ('either', 'dysp')])
  >>> do_bronc = asia.do(['bronc'])
  OutEdgeView([('asia', 'tub'), ('tub', 'either'), ('smoke', 'lung'), ('lung', 'either'),
               ('bronc', 'dysp'), ('either', 'xray'), ('either', 'dysp')])
code: def do(self, nodes, inplace=False):
      """
      Applies the do operation. The do operation removes all incoming edges
      to variables in `nodes` and marginalizes their CPDs to only contain the
      variable itself.
  
      Parameters
      ----------
      nodes : list, array-like
          The names of the nodes to apply the do-operator for.
  
      inplace: boolean (default: False)
          If inplace=True, makes the changes to the current object,
          otherwise returns a new instance.
  
      Returns
      -------
      Modified network: pgmpy.models.BayesianNetwork or None
          If inplace=True, modifies the object itself else returns an instance of
          BayesianNetwork modified by the do operation.
  
      Examples
      --------
      >>> from pgmpy.utils import get_example_model
      >>> asia = get_example_model('asia')
      >>> asia.edges()
      OutEdgeView([('asia', 'tub'), ('tub', 'either'), ('smoke', 'lung'), ('smoke', 'bronc'),
                   ('lung', 'either'), ('bronc', 'dysp'), ('either', 'xray'), ('either', 'dysp')])
      >>> do_bronc = asia.do(['bronc'])
      OutEdgeView([('asia', 'tub'), ('tub', 'either'), ('smoke', 'lung'), ('lung', 'either'),
                   ('bronc', 'dysp'), ('either', 'xray'), ('either', 'dysp')])
      """
      if isinstance(nodes, (str, int)):
          nodes = [nodes]
      else:
          nodes = list(nodes)
  
      if not set(nodes).issubset(set(self.nodes())):
          raise ValueError(
              f"Nodes not found in the model: {set(nodes) - set(self.nodes)}"
          )
  
      model = self if inplace else self.copy()
      adj_model = DAG.do(model, nodes, inplace=inplace)
  
      if adj_model.cpds:
          for node in nodes:
              cpd = adj_model.get_cpds(node=node)
              cpd.marginalize(cpd.variables[1:], inplace=True)
      return adj_model
  
imports: import itertools
  from collections import defaultdict
  from functools import reduce
  from operator import mul
  import networkx
  import numpy
  import pandas
  from joblib import Parallel, delayed
  from tqdm.auto import tqdm
  from pgmpy.base import DAG
  from pgmpy.factors.continuous import ContinuousFactor
  from pgmpy.factors.discrete import DiscreteFactor, JointProbabilityDistribution, TabularCPD
  from pgmpy.global_vars import logger
  from pgmpy.models.MarkovNetwork import MarkovNetwork
  from pgmpy.utils import compat_fns
label_desc: Use image node_id 24 for calling the BayesianNetwork obj's underlying member method code with example usage: obj.do(nodes, inplace) and returns: adj_model