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 all the CPDs according to the choice made in the constructor.
  It can be:
  - filling CPDs with random values,
  - filling CPDs with specific values given by user,
  - filling CPDs with uniform probabilities (Tends to have bad effects on convergence)
  
  Returns:
      Dictionary of CPD dataframes
  
  Raises:
      ValueError: if `initial_params` is neither a dictionary nor part of supported type strings
docstr_tok: ["Initialise","all","the","CPDs","according","to","the","choice","made","in","the","constructor",".","It","can","be",":","-","filling","CPDs","with","random","values",",","-","filling","CPDs","with","specific","values","given","by","user",",","-","filling","CPDs","with","uniform","probabilities","(","Tends","to","have","bad","effects","on","convergence",")","Returns",":","Dictionary","of","CPD","dataframes","Raises",":","ValueError",":","if","`","initial_params","`","is","neither","a","dictionary","nor","part","of","supported","type","strings"]
code: def _initialise_network_cpds(self) -> Dict[str, pd.DataFrame]:
      """
      Initialise all the CPDs according to the choice made in the constructor.
      It can be:
      - filling CPDs with random values,
      - filling CPDs with specific values given by user,
      - filling CPDs with uniform probabilities (Tends to have bad effects on convergence)
  
      Returns:
          Dictionary of CPD dataframes
  
      Raises:
          ValueError: if `initial_params` is neither a dictionary nor part of supported type strings
      """
      if isinstance(
          self.initial_params, str
      ) and self.initial_params in set(INITIAL_PARAMS):
          valid_node_set = set(self.valid_nodes)
          cpds = {}
  
          for node in self.sm.nodes:
              if node in valid_node_set:
                  cpd = self._initialise_node_cpd(
                      node, self.node_states, self.sm
                  )
                  cpd.loc[:] = 1
  
                  if self.initial_params == "random":
                      cpd.loc[:] = np.random.random(cpd.shape)
  
                  cpd = cpd / cpd.sum(axis=0)
                  cpds[node] = cpd  # Update dictionary
      elif isinstance(self.initial_params, dict):
          self._check_initial_params_dict()
          cpds = self.initial_params
      else:
          raise ValueError(
              f"`initial_params` must be a dictionary or one of {INITIAL_PARAMS}"
          )
  
      return cpds
  
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 13 for calling the EMSingleLatentVariable obj's underlying member method code with example usage: obj._initialise_network_cpds() and returns: cpds