code: def sem_generator(
      graph: nx.DiGraph,
      schema: Optional[Dict] = None,
      default_type: str = "continuous",
      noise_std: float = 1.0,
      n_samples: int = 1000,
      distributions: Dict[str, Union[str, float]] = None,
      intercept: bool = True,
      seed: int = None,
  ) -> pd.DataFrame:
      """
      Generator for tabular data with mixed variable types from a DAG.
  
      NOTE: the root nodes of the DAG are sampled from a distribution with noise_std=1.0 always.
      This is so that increases in the noise_std are in relation to a fixed spread, and therefore
      actually have an impact on the fit. Not using this method causes the noise_std to only change
      the axis scaling.
  
      Supported variable types: `'binary', 'categorical', 'continuous'`. The number
      of categories can be determined using a colon, e.g. `'categorical:5'`
      specifies a categorical feature with 5 categories.
  
      Notation: For binary and continuous variables, a ``variable'' refers to a
      ``node'', a ``feature'' refers to the one-hot column for categorical
      variables and is equivalent to a binary or continuous variable.
  
      Args:
          graph: A DAG in form of a networkx or StructureModel.
          schema: Dictionary with schema for a node/variable, if a node is missing
              uses ``default_type``. Format, {node_name: variable type}.
          default_type: The default data type for a node/variable not listed
              in the schema, or when the schema is empty.
          noise_std: The standard deviation of the noise. The binary and
              categorical features are created using a latent variable approach.
              The noise standard deviation determines how much weight the "mean"
              estimate has on the feature value.
          n_samples: The number of rows/observations to sample.
          distributions:
              ``continuous'': The type of distribution to use for the noise
                  of a continuous variable. Options: 'gaussian'/'normal' (alias)
                  (default), 'student-t', 'exponential', 'gumbel'.
              ``binary'': The type of distribution to use for the noise
                  of the latent binary variable. Options: 'probit'/'normal' (alias),
                  'logit' (default).
              ``categorical'': The type of distribution to use for the noise
                  of a latent continuous feature. Options: 'probit'/'normal' (alias),
                  'logit'/'gumbel' (alias) (default).
              ``weight'': The type of distribution to use for the linear coefficients.
                  Options: 'gaussian'/'normal' (alias), 'uniform' (default).
              ``intercept'': The type of distribution to use for the intercept. For
                  binary/categorical: this is the mean in the latent space.
                  Options: 'gaussian'/'normal' (alias), 'uniform' (default).
              ``count``: The zero-inflation probability as a float.
          intercept: Whether to use an intercept for each feature. The intercept
              is sampled once and held constant for all rows. For binary or
              categorical the intercept determines the class imbalance.
          seed: Random State
  
      Returns:
          DataFrame with generated features, uses a one-hot coding for
          categorical features.
  
      Raises:
          ValueError: if the graph is not a DAG.
          ValueError: if schema variable type is not in `'binary', 'categorical',
              'continuous', 'continuous:X` (for variables with X categories).
          ValueError: if distributions['continuous'] is not 'gaussian', 'normal', 'student-t',
              'exponential', 'gumbel'.
          ValueError: if distributions['binary'] is not 'probit', 'normal', 'logit'.
          ValueError: if distributions['categorical'] is not 'probit', 'normal', 'logit', 'gumbel'.
          ValueError: if distributions['weight'] is not 'normal' / 'gaussian' (alias), 'uniform'.
          ValueError: if distributions['intercept'] is not 'normal' / 'gaussian' (alias), 'uniform'.
          ValueError: if distributions['count'], the zero-inflation factor is not a float in [0, 1].
  
      Example:
          sm = StructureModel()
  
          sm.add_edges_from([('A', 'C'), ('D', 'C'), ('E', 'D')])
  
          sm.add_nodes_from(['B', 'F'])
  
          schema = {'B': 'binary', 'C': 'categorical:5',
                    'E': 'binary', 'F': 'continuous'}
  
          df = sem_generator(sm, schema, noise_scale=1,
                            n_samples=10000,
                            intercept=True,
                            )
      """
      distributions, var_fte_mapper, x_mat = _init_sem_data_gen(
          graph=graph,
          schema=schema,
          n_samples=n_samples,
          default_type=default_type,
          distributions=distributions,
          seed=seed,
      )
  
      # get dependence based on edges in graph (not via adjacency matrix)
      w_mat = _create_weight_matrix(
          edges_w_weights=graph.edges(data="weight"),
          variable_to_indices_dict=var_fte_mapper.var_indices_dict,
          weight_distribution=distributions["weight"],
          intercept_distribution=distributions["intercept"],
          intercept=intercept,
      )
  
      # intercept, append ones to the feature matrix
      if intercept:
          x_mat = np.append(
              x_mat, np.ones(shape=(n_samples, 1)), axis=1
          )
          intercept_idx = [x_mat.shape[1] - 1]
  
      # if intercept is used, the root nodes have len = 1
      root_node_len = 1 if intercept else 0
  
      # loop over sorted features according to ancestry (no parents first)
      for j_node in nx.topological_sort(graph):
          # all feature indices corresponding to the node/variable
          j_idx_list = var_fte_mapper.get_indices(j_node)
  
          # get all parent feature indices for the variable/node
          parents_idx = var_fte_mapper.get_indices(
              list(graph.predecessors(j_node))
          )
          if intercept:
              parents_idx += intercept_idx
  
          # if the data is a root node, must initialise the axis separate from noise parameter
          root_node = len(parents_idx) <= root_node_len
  
          # continuous variable
          if var_fte_mapper.is_var_of_type(j_node, "continuous"):
              x_mat[:, j_idx_list[0]] = _add_continuous_noise(
                  mean=x_mat[:, parents_idx].dot(
                      w_mat[parents_idx, j_idx_list[0]]
                  ),
                  distribution=distributions["continuous"],
                  noise_std=noise_std,
                  root_node=root_node,
              )
  
          # binary variable
          elif var_fte_mapper.is_var_of_type(j_node, "binary"):
              x_mat[:, j_idx_list[0]] = _sample_binary_from_latent(
                  latent_mean=x_mat[:, parents_idx].dot(
                      w_mat[parents_idx, j_idx_list[0]]
                  ),
                  distribution=distributions["binary"],
                  noise_std=noise_std,
                  root_node=root_node,
              )
  
          # count variable
          elif var_fte_mapper.is_var_of_type(j_node, "count"):
              x_mat[:, j_idx_list[0]] = _sample_count_from_latent(
                  eta=x_mat[:, parents_idx].dot(
                      w_mat[parents_idx, j_idx_list[0]]
                  ),
                  zero_inflation_pct=distributions["count"],
                  root_node=root_node,
              )
  
          # categorical variable
          elif var_fte_mapper.is_var_of_type(j_node, "categorical"):
              x_mat[:, j_idx_list] = _sample_categories_from_latent(
                  latent_mean=x_mat[:, parents_idx].dot(
                      w_mat[np.ix_(parents_idx, j_idx_list)]
                  ),
                  distribution=distributions["categorical"],
                  noise_std=noise_std,
                  root_node=root_node,
              )
  
      return pd.DataFrame(
          x_mat[:, :-1] if intercept else x_mat,
          columns=var_fte_mapper.feature_list,
      )
  
imports: from typing import Dict, Hashable, List, Optional, Tuple, Union
  import networkx
  import numpy
  import pandas
  from sklearn.gaussian_process.kernels import RBF, Kernel
  from causalnex.structure import StructureModel
  from causalnex.structure.categorical_variable_mapper import VariableFeatureMapper, validate_schema
label_desc: Use image node_id 2 for calling a global function with example usage: sem_generator(graph, schema, default_type, noise_std, n_samples, distributions, intercept, seed) and returns: pd