class_docstr: This class uses Expectation-Maximization to learn parameters of a single latent variable in a bayesian network.
  We do so by also allowing the user to CONSTRAINT the optimisation
  These are elements that help the algorithm find a local optimal point closer to the point we think
  
  The setting is:
  Input:
      - a StructureModel representing the whole network or any sub-graph containing the Markov Blanket of the LV
      - data as a dataframe. The LV must be in the dataframe, with missing values represented by `np.nan`s
      - constraints:
          - Box - A hard constraint; forbids the solution to be outside of certain boundaries
          - Priors - establishes Dirichlet priors to every parameter
  run:
      - using the method `run` or manually alternating over E and M steps)
  Result:
      - CPTs involving the latent variable, learnt by EM, found in the attribute `cpds`
      - CPTs not involving the LV not learned (They must be learned separately by MLE.
      This is faster and the result is the same)
  
  Example:
  >>> em = EMSingleLatentVariable(sm=sm, data=data, lv_name=lv_name, node_states=node_states)
  >>> em.run() # run EM until convergence
  >>> # or run E and M steps separately
  >>> for i in range(10): # Run EM 10 times
  >>>     em.e_step()
  >>>     em.m_step()
docstr: Initialise the CPD of a specified node
  
  Args:
      node: Node name
      node_states: States of the node
      sm: Structure model
  
  Returns:
      CPD dataframe associated with the node
  
  Raises:
      ValueError: if node is not found in the network
docstr_tok: ["Initialise","the","CPD","of","a","specified","node","Args",":","node",":","Node","name","node_states",":","States","of","the","node","sm",":","Structure","model","Returns",":","CPD","dataframe","associated","with","the","node","Raises",":","ValueError",":","if","node","is","not","found","in","the","network"]
code: def _initialise_node_cpd(
      node: str,
      node_states: Dict[str, List],
      sm: StructureModel,
  ) -> pd.DataFrame:
      """
      Initialise the CPD of a specified node
  
      Args:
          node: Node name
          node_states: States of the node
          sm: Structure model
  
      Returns:
          CPD dataframe associated with the node
  
      Raises:
          ValueError: if node is not found in the network
      """
      parents = list(sorted(sm.predecessors(node)))
      columns = [""]
  
      if len(parents) > 0:
          columns = pd.MultiIndex.from_product(
              [sorted(node_states[p]) for p in parents],
              names=parents,
          )
  
      indices = pd.Index(data=sorted(node_states[node]), name=node)
      values = np.zeros(shape=(len(indices), len(columns)))
      return pd.DataFrame(index=indices, columns=columns, data=values)
  
imports: import logging
  import os
  from time import time
  from typing import Dict, List, Tuple, Union
  import numpy
  import pandas
  from joblib import Parallel, delayed
  from causalnex.structure import StructureModel
  from causalnex.utils.data_utils import chunk_data, count_unique_rows
  from causalnex.utils.pgmpy_utils import cpd_multiplication
label_desc: Use image node_id 14 for calling the EMSingleLatentVariable obj's underlying member method code with example usage: obj._initialise_node_cpd(node, node_states, sm) and returns: pd