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: Likelihood of node and parents, initialized with zeros (or prior values) and then increased from data.
  The likelihood is not a conditional expression (i.e. X|U), but a joint expression (i.e. X,U).
  However, we use the same structure as the CPT to store that likelihood. That structure is:
  a pandas table with the index being X values and the the columns being U values as a MultiIndex
  
  Args:
      node: Node key
  
  Returns:
      Dataframe containing the likelihood of the node's parents
docstr_tok: ["Likelihood","of","node","and","parents",",","initialized","with","zeros","(","or","prior","values",")","and","then","increased","from","data",".","The","likelihood","is","not","a","conditional","expression","(","i.e",".","X|U",")",",","but","a","joint","expression","(","i.e",".","X",",","U",")",".","However",",","we","use","the","same","structure","as","the","CPT","to","store","that","likelihood",".","That","structure","is",":","a","pandas","table","with","the","index","being","X","values","and","the","the","columns","being","U","values","as","a","MultiIndex","Args",":","node",":","Node","key","Returns",":","Dataframe","containing","the","likelihood","of","the","node","'s","parents"]
code: def _initialize_sufficient_stats(self, node: str) -> pd.DataFrame:
      """
      Likelihood of node and parents, initialized with zeros (or prior values) and then increased from data.
      The likelihood is not a conditional expression (i.e. X|U), but a joint expression (i.e. X,U).
      However, we use the same structure as the CPT to store that likelihood. That structure is:
      a pandas table with the index being X values and the the columns being U values as a MultiIndex
  
      Args:
          node: Node key
  
      Returns:
          Dataframe containing the likelihood of the node's parents
      """
      if self.priors is None:
          sufficient_stats_df = self.cpds[node].copy()
          sufficient_stats_df.loc[:] = 0
      else:
          sufficient_stats_df = self.priors[node].copy()
  
      return sufficient_stats_df
  
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 12 for calling the EMSingleLatentVariable obj's underlying member method code with example usage: obj._initialize_sufficient_stats(node) and returns: sufficient_stats_df