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: Predicts states of all the missing variables.
  
  Parameters
  ----------
  data: pandas DataFrame object
      A DataFrame object with column names same as the variables in the model.
  
  stochastic: boolean
      If True, does prediction by sampling from the distribution of predicted variable(s).
      If False, returns the states with the highest probability value (i.e. MAP) for the
          predicted variable(s).
  
  n_jobs: int (default: -1)
      The number of CPU cores to use. If -1, uses all available cores.
  
  Examples
  --------
  >>> import numpy as np
  >>> import pandas as pd
  >>> from pgmpy.models import BayesianNetwork
  >>> values = pd.DataFrame(np.random.randint(low=0, high=2, size=(1000, 5)),
  ...                       columns=['A', 'B', 'C', 'D', 'E'])
  >>> train_data = values[:800]
  >>> predict_data = values[800:]
  >>> model = BayesianNetwork([('A', 'B'), ('C', 'B'), ('C', 'D'), ('B', 'E')])
  >>> model.fit(train_data)
  >>> predict_data = predict_data.copy()
  >>> predict_data.drop('E', axis=1, inplace=True)
  >>> y_pred = model.predict(predict_data)
  >>> y_pred
      E
  800 0
  801 1
  802 1
  803 1
  804 0
  ... ...
  993 0
  994 0
  995 1
  996 1
  997 0
  998 0
  999 0
code: def predict(self, data, stochastic=False, n_jobs=-1):
      """
      Predicts states of all the missing variables.
  
      Parameters
      ----------
      data: pandas DataFrame object
          A DataFrame object with column names same as the variables in the model.
  
      stochastic: boolean
          If True, does prediction by sampling from the distribution of predicted variable(s).
          If False, returns the states with the highest probability value (i.e. MAP) for the
              predicted variable(s).
  
      n_jobs: int (default: -1)
          The number of CPU cores to use. If -1, uses all available cores.
  
      Examples
      --------
      >>> import numpy as np
      >>> import pandas as pd
      >>> from pgmpy.models import BayesianNetwork
      >>> values = pd.DataFrame(np.random.randint(low=0, high=2, size=(1000, 5)),
      ...                       columns=['A', 'B', 'C', 'D', 'E'])
      >>> train_data = values[:800]
      >>> predict_data = values[800:]
      >>> model = BayesianNetwork([('A', 'B'), ('C', 'B'), ('C', 'D'), ('B', 'E')])
      >>> model.fit(train_data)
      >>> predict_data = predict_data.copy()
      >>> predict_data.drop('E', axis=1, inplace=True)
      >>> y_pred = model.predict(predict_data)
      >>> y_pred
          E
      800 0
      801 1
      802 1
      803 1
      804 0
      ... ...
      993 0
      994 0
      995 1
      996 1
      997 0
      998 0
      999 0
      """
      from pgmpy.inference import VariableElimination
  
      if set(data.columns) == set(self.nodes()):
          raise ValueError(
              "No variable missing in data. Nothing to predict"
          )
  
      elif set(data.columns) - set(self.nodes()):
          raise ValueError(
              "Data has variables which are not in the model"
          )
  
      missing_variables = set(self.nodes()) - set(data.columns)
      model_inference = VariableElimination(self)
  
      if stochastic:
          data_unique_indexes = data.groupby(list(data.columns)).apply(
              lambda t: t.index.tolist()
          )
          data_unique = data_unique_indexes.index.to_frame()
  
          pred_values = Parallel(n_jobs=n_jobs)(
              delayed(model_inference.query)(
                  variables=missing_variables,
                  evidence=data_point.to_dict(),
                  show_progress=False,
              )
              for index, data_point in tqdm(
                  data_unique.iterrows(), total=data_unique.shape[0]
              )
          )
          predictions = pd.DataFrame()
          for i, row in enumerate(data_unique_indexes):
              p = pred_values[i].sample(n=len(row))
              p.index = row
              predictions = pd.concat((predictions, p), copy=False)
  
          return predictions.reindex(data.index)
  
      else:
          data_unique = data.drop_duplicates()
          pred_values = []
  
          # Send state_names dict from one of the estimated CPDs to the inference class.
          pred_values = Parallel(n_jobs=n_jobs)(
              delayed(model_inference.map_query)(
                  variables=missing_variables,
                  evidence=data_point.to_dict(),
                  show_progress=False,
              )
              for index, data_point in tqdm(
                  data_unique.iterrows(), total=data_unique.shape[0]
              )
          )
  
          df_results = pd.DataFrame(
              pred_values, index=data_unique.index
          )
          data_with_results = pd.concat(
              [data_unique, df_results], axis=1
          )
          return data.merge(data_with_results, how="left").loc[
              :, list(missing_variables)
          ]
  
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 15 for calling the BayesianNetwork obj's underlying member method code with example usage: obj.predict(data, stochastic, n_jobs) and returns: predictions, data