code: def _extract_paired_histogram_specification(
      histogram_data: List[List[float]],
      num_bins: int,
      density: bool,
      x_pad_fraction: float,
      y_pad_fraction: float,
  ) -> Tuple[
      List[float], List[List[float]], List[float], Tuple[float, float]
  ]:
      """Extracts all information from the data needed to plot a paired histogram.
  
      Args:
          histogram_data: Two data vectors
          num_bins: Number of bins to be used for the histogram
          density: If true, generate information for a probability density histogram
          x_pad_fraction: Percentage of extra space in the horizontal direction
          y_pad_fraction: Percentage of extra space in the vertical direction
  
      Returns:
          The bins, values, ranges of either x-axis, and the range of the y-axis
  
      Raises:
          ValueError: If histogram_data does not contain values.
      """
      if not histogram_data or not np.concatenate(histogram_data).size:
          rasa.shared.utils.io.raise_warning(
              "No data to plot paired histogram."
          )
          raise ValueError("No data to plot paired histogram.")
      min_data_value: float = np.min(np.concatenate(histogram_data))
      max_data_value: float = np.max(np.concatenate(histogram_data))
      bin_width = (max_data_value - min_data_value) / num_bins
      bins = [
          min_data_value + i * bin_width
          # `bins` describes the _boundaries_ of the bins, so we need
          # 2 extra - one at the beginning and one at the end
          for i in range(num_bins + 2)
      ]
      histograms = [
          # A list of counts - how often a value in `data` falls into a particular bin
          list(np.histogram(data, bins=bins, density=density)[0])
          for data in histogram_data
      ]
  
      y_padding = 0.5 * bin_width + y_pad_fraction * bin_width
  
      if density:
          # Get the maximum count across both histograms, and scale it
          # with `x_pad_fraction`
          v = max(
              [
                  (1.0 + x_pad_fraction) * max(histogram)
                  for histogram in histograms
              ]
          )
          # When we plot the PDF, let both x-axes run to the same value
          # so it's easier to compare visually
          x_ranges = [v, v]
      else:
          # For the left and right histograms, get the largest counts and scale them
          # by `x_pad_fraction` to get the maximum x-values displayed
          x_ranges = [
              (1.0 + x_pad_fraction) * max(histogram)
              for histogram in histograms
          ]
  
      try:
          bin_of_first_non_zero_tally = min(
              [
                  [bool(v) for v in histogram].index(True)
                  for histogram in histograms
              ]
          )
      except ValueError:
          bin_of_first_non_zero_tally = 0
  
      y_range = (
          # Start plotting where the data starts (ignore empty bins at the low end)
          bins[bin_of_first_non_zero_tally] - y_padding,
          # The y_padding adds half a bin width, as we want the bars to be
          # _centered_ on the bins. We take the next-to-last element of `bins`,
          # because that is the beginning of the last bin.
          bins[-2] + y_padding,
      )
  
      return bins, histograms, x_ranges, y_range
  
file_path: rasa/rasa/utils/plotting.py
imports: import logging
  import itertools
  import os
  from functools import wraps
  import numpy
  from typing import Any, Callable, List, Optional, Text, TypeVar, Union, Tuple
  import matplotlib
  from matplotlib.ticker import FormatStrFormatter
  import rasa.shared.utils.io
  from rasa.constants import RESULTS_FILE
label_desc: Use image node_id 4 for calling a global function with example usage: _extract_paired_histogram_specification(histogram_data, num_bins, density, x_pad_fraction, y_pad_fraction) and returns: bins,  histograms,  x_ranges,  y_range