code: def assign_causal_mechanisms(
      causal_model: ProbabilisticCausalModel,
      based_on: pd.DataFrame,
      quality: AssignmentQuality = AssignmentQuality.GOOD,
      override_models: bool = False,
  ) -> AutoAssignmentSummary:
      """Automatically assigns appropriate causal mechanisms to nodes. If causal mechanisms are already assigned to nodes
      and override_models is set to False, this function only validates the assignments with respect to the graph
      structure. This is, the validation checks whether root nodes have StochasticModels and non-root
      ConditionalStochasticModels assigned.
  
      The following types of causal mechanisms are considered for the automatic selection:
  
      If root node:
      An empirical distribution, i.e., the distribution is represented by randomly sampling from the provided data.
      This provides a flexible and non-parametric way to model the marginal distribution and is valid for all types of
      data modalities.
  
      If non-root node and the data is continuous:
      Additive Noise Models (ANM) of the form X_i = f(PA_i) + N_i, where PA_i are the parents of X_i and the unobserved
      noise N_i is assumed to be independent of PA_i. To select the best model for f, different regression models are
      evaluated and the model with the smallest mean squared error is selected. Note that minimizing the mean squared
      error here is equivalent to selecting the best choice of an ANM. See the following paper for more details:
          Hoyer, P., Janzing, D., Mooij, J. M., Peters, J., & Schölkopf, B. (2008).
          Nonlinear causal discovery with additive noise models.
          Advances in neural information processing systems, 21
  
      If non-root node and the data is discrete:
      Discrete Additive Noise Models have almost the same definition as non-discrete ANMs, but come with an additional
      constraint to return discrete values. Note that 'discrete' here refers to numerical values with an order. If the
      data is categorical, consider representing them as strings to ensure proper model selection. See the following
      paper for more details:
          Peters, J., Janzing, D., & Scholkopf, B. (2011).
          Causal inference on discrete data using additive noise models.
          IEEE Transactions on Pattern Analysis and Machine Intelligence, 33(12), 2436-2450.
  
      If non-root node and the data is categorical:
      A functional causal model based on a classifier, i.e., X_i = f(PA_i, N_i).
      Here, N_i follows a uniform distribution on [0, 1] and is used to randomly sample a class (category) using the
      conditional probability distribution produced by a classification model. Here, different model classes are evaluated
      using the (negative) F1 score and the best performing model class is selected.
  
      The current model zoo is:
  
      With "GOOD" quality:
          Numerical:
          - Linear Regressor
          - Linear Regressor with polynomial features
          - Histogram Gradient Boost Regressor
  
          Categorical:
          - Logistic Regressor
          - Logistic Regressor with polynomial features
          - Histogram Gradient Boost Classifier
  
      With "BETTER" quality:
          Numerical:
          - Linear Regressor
          - Linear Regressor with polynomial features
          - Gradient Boost Regressor
          - Ridge Regressor
          - Lasso Regressor
          - Random Forest Regressor
          - Support Vector Regressor
          - Extra Trees Regressor
          - KNN Regressor
          - Ada Boost Regressor
  
          Categorical:
          - Logistic Regressor
          - Logistic Regressor with polynomial features
          - Histogram Gradient Boost Classifier
          - Random Forest Classifier
          - Extra Trees Classifier
          - Support Vector Classifier
          - KNN Classifier
          - Gaussian Naive Bayes Classifier
          - Ada Boost Classifier
  
      With "BEST" quality:
      An auto ML model based on AutoGluon (optional dependency, needs to be installed).
  
      :param causal_model: The causal model to whose nodes to assign causal models.
      :param based_on: Jointly sampled data corresponding to the nodes of the given graph.
      :param quality: AssignmentQuality for the automatic model selection and model accuracy. This changes the type of
                      prediction model and time spent on the selection. See the docstring for a list of potential models.
                      The options for the quality are:
          - AssignmentQuality.GOOD: Only a small set of models are evaluated.
              Model selection speed: Fast
              Model training speed: Fast
              Model inference speed: Fast
              Model accuracy: Medium
          - AssignmentQuality.BETTER: A larger set of models are evaluated.
              Model selection speed: Medium
              Model training speed: Fast
              Model inference speed: Fast
              Model accuracy: Good
          - AssignmentQuality.BEST: Uses an AutoGluon (auto ML) model with default settings defined by the AutoGluon
              wrapper. While the model selection itself is fast, the training and inference speed can be significantly
              slower than in the other options. NOTE: This requires the optional autogluon.tabular dependency.
              Model selection speed: Instant
              Model training speed: Slow
              Model inference speed: Slow-Medium
              Model accuracy: Best
      :param override_models: If set to True, existing mechanism assignments are replaced with automatically selected
                              ones. If set to False, the assigned mechanisms are only validated with respect to the graph
                              structure.
      :return: A summary object containing details about the model selection process.
      """
      auto_assignment_summary = AutoAssignmentSummary()
  
      for node in nx.topological_sort(causal_model.graph):
          if (
              not override_models
              and CAUSAL_MECHANISM in causal_model.graph.nodes[node]
          ):
              auto_assignment_summary.add_node_log_message(
                  node,
                  "Node %s already has a causal mechanism assigned and the override parameter is False. Skipping this "
                  "node." % node,
              )
              validate_causal_model_assignment(causal_model.graph, node)
              continue
  
          model_performances = assign_causal_mechanism_node(
              causal_model, node, based_on, quality
          )
  
          if is_root_node(causal_model.graph, node):
              auto_assignment_summary.add_node_log_message(
                  node,
                  "Node %s is a root node. Therefore, assigning '%s' to the node representing the marginal distribution."
                  % (node, causal_model.causal_mechanism(node)),
              )
          else:
              data_type = "continuous"
              if isinstance(
                  causal_model.causal_mechanism(node), ClassifierFCM
              ):
                  data_type = "categorical"
              elif isinstance(
                  causal_model.causal_mechanism(node),
                  DiscreteAdditiveNoiseModel,
              ):
                  data_type = "discrete"
  
              auto_assignment_summary.add_node_log_message(
                  node,
                  "Node %s is a non-root node with %s data. Assigning '%s' to the node."
                  % (
                      node,
                      data_type,
                      causal_model.causal_mechanism(node),
                  ),
              )
  
          if isinstance(
              causal_model.causal_mechanism(node),
              DiscreteAdditiveNoiseModel,
          ):
              auto_assignment_summary.add_node_log_message(
                  node,
                  "This represents the discrete causal relationship as "
                  + str(node)
                  + " := f("
                  + ",".join(
                      [
                          str(parent)
                          for parent in get_ordered_predecessors(
                              causal_model.graph, node
                          )
                      ]
                  )
                  + ") + N.",
              )
          elif isinstance(
              causal_model.causal_mechanism(node), AdditiveNoiseModel
          ):
              auto_assignment_summary.add_node_log_message(
                  node,
                  "This represents the causal relationship as "
                  + str(node)
                  + " := f("
                  + ",".join(
                      [
                          str(parent)
                          for parent in get_ordered_predecessors(
                              causal_model.graph, node
                          )
                      ]
                  )
                  + ") + N.",
              )
          elif isinstance(
              causal_model.causal_mechanism(node), ClassifierFCM
          ):
              auto_assignment_summary.add_node_log_message(
                  node,
                  "This represents the causal relationship as "
                  + str(node)
                  + " := f("
                  + ",".join(
                      [
                          str(parent)
                          for parent in get_ordered_predecessors(
                              causal_model.graph, node
                          )
                      ]
                  )
                  + ",N).",
              )
  
          for model, performance, metric_name in model_performances:
              auto_assignment_summary.add_model_performance(
                  node, model, performance, metric_name
              )
  
      return auto_assignment_summary
  
imports: import warnings
  from enum import Enum, auto
  from functools import partial
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
  import networkx
  import numpy
  import pandas
  from joblib import Parallel, delayed
  from sklearn import metrics
  from sklearn.exceptions import ConvergenceWarning
  from sklearn.linear_model import LinearRegression, LogisticRegression
  from sklearn.model_selection import KFold, StratifiedKFold, train_test_split
  from sklearn.preprocessing import MultiLabelBinarizer
  from dowhy.gcm import config
  from dowhy.gcm.causal_mechanisms import AdditiveNoiseModel, ClassifierFCM, DiscreteAdditiveNoiseModel
  from dowhy.gcm.causal_models import CAUSAL_MECHANISM, ProbabilisticCausalModel, validate_causal_model_assignment
  from dowhy.gcm.ml import ClassificationModel, PredictionModel, create_hist_gradient_boost_classifier, create_hist_gradient_boost_regressor, create_lasso_regressor, create_linear_regressor, create_logistic_regression_classifier, create_random_forest_regressor, create_ridge_regressor, create_support_vector_regressor
  from dowhy.gcm.ml.classification import create_ada_boost_classifier, create_extra_trees_classifier, create_gaussian_nb_classifier, create_knn_classifier, create_polynom_logistic_regression_classifier, create_random_forest_classifier, create_support_vector_classifier
  from dowhy.gcm.ml.regression import create_ada_boost_regressor, create_extra_trees_regressor, create_knn_regressor, create_polynom_regressor
  from dowhy.gcm.stochastic_models import EmpiricalDistribution
  from dowhy.gcm.util.general import auto_apply_encoders, auto_fit_encoders, is_categorical, is_discrete, set_random_seed, shape_into_2d
  from dowhy.graph import get_ordered_predecessors, is_root_node
label_desc: Use image node_id 1 for calling a global function with example usage: assign_causal_mechanisms(causal_model, based_on, quality, override_models) and returns: auto_assignment_summary