code: def estimate_distribution_change_scores(
      causal_model: ProbabilisticCausalModel,
      original_data: pd.DataFrame,
      new_data: pd.DataFrame,
      difference_estimation_func: Callable[
          [np.ndarray, np.ndarray], Union[np.ndarray, float]
      ] = auto_estimate_kl_divergence,
      max_num_evaluation_samples: int = 1000,
      num_joint_samples: int = 500,
      early_stopping_percentage: float = 0.01,
      independence_test: Callable[
          [np.ndarray, np.ndarray], float
      ] = kernel_based,
      conditional_independence_test: Callable[
          [np.ndarray, np.ndarray, np.ndarray], float
      ] = kernel_based,
      mechanism_change_test_significance_level: float = 0.05,
      mechanism_change_test_fdr_control_method: Optional[
          str
      ] = "fdr_bh",
  ) -> Dict[Any, float]:
      """Given newly observed and original samples from the joint distribution of the given causal graphical model, this
      method estimates a score for each node that quantifies how much the distribution of the node has changed. For this,
      it first checks whether the underlying causal mechanism has changed at all and, if this is the case, it estimates
      the difference between the new and original distributions. The score is based on the quantity measured by the
      provided difference_estimation_func or 0 if no mechanism change has been detected.
  
      Note that for each parent sample, num_joint_samples conditional samples are generated based on the original and new
      causal mechanism and evaluated by the given difference_estimation_func function. These results are then averaged
      over multiple different parent samples.
  
      :param causal_model: The underlying causal model based on the original data.
      :param original_data: Samples from the original data.
      :param new_data: Samples from the new data.
      :param difference_estimation_func: Function for quantifying the distribution change. This function should expect
                                         two inputs which represent samples from two different distributions. An example
                                         could be the KL divergence.
      :param max_num_evaluation_samples: Maximum number of (parent) samples for evaluating the difference in
                                         distributions.
      :param num_joint_samples: Number of samples generated in a node per parent sample.
      :param early_stopping_percentage: If the change in percentage between multiple consecutive runs is below this
                                        threshold, the evaluation stops before evaluating all max_num_evaluation_samples.
      :param independence_test: Unconditional independence test. This is used to identify mechanism changes in root nodes.
      :param conditional_independence_test: Conditional independence test. This is used to identify mechanism changes in
                                            non-root nodes.
      :param mechanism_change_test_significance_level: A significance level for rejecting the null hypothesis that the
                                                       causal mechanism of a node has not changed.
      :param mechanism_change_test_fdr_control_method: The false discovery rate control method for mechanism change
                                                       tests. For more options, checkout `statsmodels manual
                                                       <https://www.statsmodels.org/dev/generated/statsmodels.stats.multitest.multipletests.html>`_.
      :return: A dictionary assining a score to each node in the causal graph.
      """
      validate_causal_dag(causal_model.graph)
  
      mechanism_changed_for_node = _check_significant_mechanism_change(
          causal_model.graph,
          original_data,
          new_data,
          independence_test,
          conditional_independence_test,
          mechanism_change_test_significance_level,
          mechanism_change_test_fdr_control_method,
      )
  
      results = {}
      for node in tqdm(
          causal_model.graph.nodes,
          desc="Estimating mechanism change anomaly scores",
          position=0,
          leave=True,
      ):
          if mechanism_changed_for_node[node]:
              if is_root_node(causal_model.graph, node):
                  results[node] = difference_estimation_func(
                      original_data[node].to_numpy(),
                      new_data[node].to_numpy(),
                  )
              else:
                  parent_nodes = get_ordered_predecessors(
                      causal_model.graph, node
                  )
                  results[node] = _estimate_distribution_change_score(
                      original_data[parent_nodes].to_numpy(),
                      new_data[parent_nodes].to_numpy(),
                      new_data[node].to_numpy(),
                      causal_model.causal_mechanism(node),
                      difference_estimation_func,
                      max_num_evaluation_samples,
                      num_joint_samples,
                      early_stopping_percentage,
                  )
          else:
              results[node] = 0
  
      return results
  
imports: import logging
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
  import networkx
  import numpy
  import pandas
  from numpy.matlib import repmat
  from statsmodels.stats.multitest import multipletests
  from tqdm import tqdm
  from dowhy.gcm.auto import AssignmentQuality, assign_causal_mechanism_node, assign_causal_mechanisms
  from dowhy.gcm.causal_mechanisms import ConditionalStochasticModel
  from dowhy.gcm.causal_models import PARENTS_DURING_FIT, ProbabilisticCausalModel, clone_causal_models, validate_causal_dag
  from dowhy.gcm.divergence import auto_estimate_kl_divergence
  from dowhy.gcm.fitting_sampling import draw_samples, fit_causal_model_of_target
  from dowhy.gcm.independence_test.kernel import kernel_based
  from dowhy.gcm.shapley import ShapleyConfig, estimate_shapley_values
  from dowhy.gcm.util.general import shape_into_2d
  from dowhy.graph import DirectedGraph, get_ordered_predecessors, is_root_node, node_connected_subgraph_view
label_desc: Use image node_id 7 for calling a global function with example usage: estimate_distribution_change_scores(causal_model, original_data, new_data, difference_estimation_func, max_num_evaluation_samples, num_joint_samples, early_stopping_percentage, independence_test, conditional_independence_test, mechanism_change_test_significance_level, mechanism_change_test_fdr_control_method) and returns: results