fitk.fisher_plotter API documentation
Description: Package for plotting of Fisher objects. See here for documentation of `FisherPlotter`, `FisherFigure1D`, and `FisherFigure2D`.

# Module `fitk.fisher_plotter`

Package for plotting of Fisher objects. See here for documentation of `FisherPlotter`, `FisherFigure1D`, and `FisherFigure2D`.

Expand source code

    """
    Package for plotting of Fisher objects.
    See here for documentation of `FisherPlotter`, `FisherFigure1D`, and `FisherFigure2D`.
    """

    from __future__ import annotations

    # standard library imports
    from abc import ABC, abstractmethod
    from typing import \
        Collection, \
        Optional, \
        Tuple, \
        Union

    # third party imports
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.figure import Figure
    from matplotlib.transforms import Bbox
    from matplotlib.axes import Axes
    from matplotlib.patches import Ellipse
    from scipy.stats import chi2, norm

    # first party imports
    from fitk.fisher_utils import \
        get_default_rcparams, \
        MismatchingSizeError, \
        ParameterNotFoundError, \
        is_iterable, \
        get_index_of_other_array, \
        float_to_latex
    from fitk.fisher_matrix import FisherMatrix

    class FisherBaseFigure(ABC):
        def __init__(
            self,
            figure : Figure,
            axes : Collection[Axes],
            names : Collection[str],
        ):
            """
            Constructor.

            Parameters
            ----------
            figure : Figure
                the figure plotted by `FisherPlotter`

            axes : Collection[Axes]
                the axes of the above figure

            names : Collection[str]
                the names of the parameters that are plotted
            """
            self._figure = figure
            self._axes = axes
            self._names = names

        @abstractmethod
        def __getitem__(self, key):
            """
            Implements element access.
            """
            pass

        @property
        def figure(self):
            """
            Returns the underlying figure, an instance of `matplotlib.figure.Figure`.
            """
            return self._figure

        @property
        def axes(self):
            """
            Returns the axes of the figure as a numpy array.
            """
            return self._axes

        @property
        def names(self):
            """
            Returns the names of the parameters plotted.
            """
            return self._names

        def savefig(
            self,
            path : str,
            dpi : float = 300,
            bbox_inches : Union[str, Bbox] = 'tight',
            **kwargs,
        ):
            """
            Convenience wrapper for `figure.savefig`.

            Parameters
            ----------
            path : str
                the path where to save the figure

            dpi : float, default = 300
                the resolution of the saved figure

            bbox_inches : Union[str, Bbox], default = 'tight'
                what is the bounding box for the figure

            kwargs
                any other keyword arguments that should be passed to `figure.savefig`
            """
            return self.figure.savefig(path, dpi=dpi, bbox_inches=bbox_inches, **kwargs)

    class FisherFigure1D(FisherBaseFigure):
        """
        Container for easy access to elements in the 1D plot.
        """
        def __getitem__(
            self,
            key : str,
        ):
            """
            Returns the axis associated to the name `key`.
            """
            if key not in self.names:
                raise ParameterNotFoundError(key, self.names)

            return self.axes.flat[np.where(self.names == key)][0]

    class FisherFigure2D(FisherBaseFigure):
        """
        Container for easy access to elements in the 2D plot.
        """
        def __getitem__(
            self,
            key : Tuple[str],
        ):
            pass

    class FisherPlotter:
        """
        Class for plotting FisherMatrix objects.
        """
        def __init__(
            self,
            *args : FisherMatrix,
            labels : Optional[Collection[str]] = None,
        ):
            """
            Constructor.

            Parameters
            ----------
            args : FisherMatrix
                `FisherMatrix` objects which we want to plot.
                Must have the same names, otherwise throws an error.
                Can have different fiducial values.
                The order of plotting of the parameters and the LaTeX labels to use
                are determined by the first argument.

            labels : array of strings, default None
                the list of labels to put in the legend of the plots.
                If not set, defaults to `0, ..., len(args) - 1`
            """
            # make sure all of the Fisher objects have the same sizes
            if not all(len(args[0]) == len(arg) for arg in args):
                raise MismatchingSizeError(*args)

            # check names
            if not all(set(args[0].names) == set(arg.names) for arg in args):
                raise ValueError(
                    'The names of the inputs do not match'
                )

            if labels is not None:
                if len(labels) != len(args):
                    raise MismatchingSizeError(labels, args)
            else:
                labels = list(map(str, np.arange(len(args))))

            # in case the names are shuffled, we sort them according to the FIRST
            # input
            indices = np.array(
                [get_index_of_other_array(args[0].names, arg.names) for arg in args],
                dtype=object,
            )

            self._values = [arg.sort(key=index) for index, arg in zip(indices, args)]

            self._labels = np.array(labels, dtype=object)

        @property
        def values(self):
            """
            Returns the input array of Fisher objects.
            """
            return self._values

        @property
        def labels(self):
            """
            Returns the labels of the Fisher objects.
            """
            return self._labels

        def find_limits_1d(
            self,
            name : str,
            sigma : float = 3,
        ):
            """
            Finds "nice" 1D limits for a given parameter taking into account fiducials
            and constraints.

            Parameters
            ----------
            name : str
                the name of the parameter

            sigma : float, default = 3
                how many sigmas away to plot

            Returns
            -------
            `tuple` with lower and upper limits
            """
            sigmas = np.array(
                [_.constraints(name, marginalized=True, sigma=sigma) for _ in self.values]
            )
            fiducial = np.array(
                [_.fiducial[np.where(_.names == name)] for _ in self.values]
            )

            xleft, xright = np.min(fiducial - sigmas), np.max(fiducial + sigmas)

            return xleft, xright

        def plot_1d(
            self,
            max_cols : Optional[int] = None,
            rc : dict = get_default_rcparams(),
            **kwargs,
        ):
            """
            Makes a 1D plot (Gaussians) of the Fisher objects and returns a
            `FisherFigure1D`.

            Parameters
            ----------
            max_cols : Optional[int], default = None
                the maximum number of columns to force the plot into.
                By default, the parameters are always plotted horizontally; if you
                need to spread it over `max_cols`, pass a non-negative integer
                here.

            rc : dict = get_default_rcparams()
                any parameters meant for `matplotlib.rcParams`.
                See [Matplotlib documentation](https://matplotlib.org/stable/tutorials/introductory/customizing.html)
                for more information.

            Returns
            -------
            An instance of `FisherFigure1D`.
            """
            size = len(self.values[0])

            if max_cols is not None and max_cols <= size:
                full = size % max_cols == 0
                layout = size // max_cols if full else size // max_cols + 1, max_cols
            else:
                layout = 1, size
                full = True

            with plt.rc_context(rc):
                # general figure setup
                fig = plt.figure(clear=True, figsize=(2 * layout[1], 2 * layout[0]))
                gs = fig.add_gridspec(
                    nrows=layout[0], ncols=layout[1],
                    hspace=0.5, wspace=0.1,
                )
                axes = gs.subplots()
                if size == 1:
                    axes = np.array([axes])

                names = self.values[0].names
                latex_names = self.values[0].latex_names

                ylabel1d = r'$p (\theta)$'

                handles = []

                for (index, name), name_latex in zip(enumerate(names), latex_names):
                    ax = axes.flat[index]
                    title_list = [
                        '{0} = ${1}^{{+{2}}}_{{-{2}}}$'.format(
                        name_latex,
                        float_to_latex(float(_.fiducial[np.where(_.names == name)])),
                        float_to_latex(float(_.constraints(name, marginalized=True))),
                    ) for _ in self.values
                    ]

                    # the scaling factor here is so that we don't cutoff the peak
                    ymax = np.max(
                        [gaussian(0, 0, _.constraints(name, marginalized=True)) for _ in self.values]
                    ) * 1.03

                    for fm in self.values:
                        handle, = add_plot_1d(
                            fm.fiducial[np.where(fm.names == name)],
                            fm.constraints(name, marginalized=True),
                            ax,
                        )

                        if index == 0:
                            handles.append(handle)

                    ax.set_xlabel(name_latex)
                    ax.set_xlim(*self.find_limits_1d(name))
                    ax.set_ylim(0, ymax)

                    if kwargs.get('title') is True:
                        ax.set_title('\n'.join(title_list))

                    if index == 0:
                        ax.set_ylabel(ylabel1d)

                    ax.set_yticks([])

                if kwargs.get('legend') is True:
                    fig.legend(
                        np.array(handles, dtype=object),
                        self.labels,
                        frameon=False,
                        loc='upper center',
                        bbox_to_anchor=(0.5, -0.15),
                        ncol=len(self.values),
                    )

                if isinstance(kwargs.get('title'), str):
                    fig.suptitle(kwargs.get('title'))

                # remove any axes which are not drawn
                if not full:
                    for index in range(
                        (layout[0] - 1) * layout[1] + 1,
                        layout[0] * layout[1]
                    ):
                        axes.flat[index].remove()

            return FisherFigure1D(fig, axes, names)

        def plot_2d(
            self,
            rc : dict = get_default_rcparams(),
            **kwargs,
        ):
            """
            Plots the 2D ellipses (and optionally 1D Gaussians) of the Fisher
            objects, and returns an instance of `FisherFigure2D`.
            """
            pass

        def plot_triangle(
            self,
            **kwargs,
        ):
            """
            Plots the 2D ellipses and 1D Gaussians of the Fisher objects, and
            returns an instance of `FisherFigure2D`.
            """
            pass

    def gaussian(
        x : float,
        mu : float = 0,
        sigma : float = 1,
    ):
        """
        Returns a normalized Gaussian.
        """
        if sigma <= 0:
            raise ValueError(
                f'Invalid parameter: sigma = {sigma}'
            )

        return np.exp(-(x - mu)**2 / 2 / sigma**2) / sigma / np.sqrt(2 * np.pi)

    def get_chisq(
        sigma : float = 1,
        df : int = 2,
    ):
        r"""
        Returns \(\Delta \chi^2\).
        To obtain the scaling coefficient \(\alpha\), just take the square root of the output.

        Parameters
        ----------
        sigma : float, default = 1
            the error on the parameter

        df : int, default = 2
            the number of degrees of freedom

        Returns
        -------
        float
        """
        return chi2.ppf(norm.cdf(sigma) - norm.cdf(-sigma), df=df)

    def add_plot_1d(
        fiducial : float,
        sigma : float,
        ax : Axes,
        **kwargs,
    ):
        """
        Adds a 1D Gaussian with marginalized constraints `sigma` close to fiducial
        value `fiducial` to axis `ax`.
        """
        x = np.linspace(
            fiducial - 4 * sigma,
            fiducial + 4 * sigma,
            100,
        )

        temp = ax.plot(
            x,
            [gaussian(_, mu=fiducial, sigma=sigma) for _ in x],
            **kwargs,
        )

        return temp

## Functions

`def add_plot_1d(fiducial: float, sigma: float, ax: Axes, **kwargs)`

  Adds a 1D Gaussian with marginalized constraints `sigma` close to fiducial value `fiducial` to axis `ax`.

  Expand source code

      def add_plot_1d(
          fiducial : float,
          sigma : float,
          ax : Axes,
          **kwargs,
      ):
          """
          Adds a 1D Gaussian with marginalized constraints `sigma` close to fiducial
          value `fiducial` to axis `ax`.
          """
          x = np.linspace(
              fiducial - 4 * sigma,
              fiducial + 4 * sigma,
              100,
          )

          temp = ax.plot(
              x,
              [gaussian(_, mu=fiducial, sigma=sigma) for _ in x],
              **kwargs,
          )

          return temp

`def gaussian(x: float, mu: float = 0, sigma: float = 1)`

  Returns a normalized Gaussian.

  Expand source code

      def gaussian(
          x : float,
          mu : float = 0,
          sigma : float = 1,
      ):
          """
          Returns a normalized Gaussian.
          """
          if sigma <= 0:
              raise ValueError(
                  f'Invalid parameter: sigma = {sigma}'
              )

          return np.exp(-(x - mu)**2 / 2 / sigma**2) / sigma / np.sqrt(2 * np.pi)

`def get_chisq(sigma: float = 1, df: int = 2)`

  Returns \Delta \chi^2
  . To obtain the scaling coefficient \alpha
  , just take the square root of the output.

  ## Parameters

  `sigma` :`float`, default `= 1`
    the error on the parameter
  `df` :`int`, default `= 2`
    the number of degrees of freedom

  ## Returns

  `float`
  Expand source code

      def get_chisq(
          sigma : float = 1,
          df : int = 2,
      ):
          r"""
          Returns \(\Delta \chi^2\).
          To obtain the scaling coefficient \(\alpha\), just take the square root of the output.

          Parameters
          ----------
          sigma : float, default = 1
              the error on the parameter

          df : int, default = 2
              the number of degrees of freedom

          Returns
          -------
          float
          """
          return chi2.ppf(norm.cdf(sigma) - norm.cdf(-sigma), df=df)

## Classes

`class FisherBaseFigure (figure: Figure, axes: Collection[Axes], names: Collection[str])`

  Helper class that provides a standard way to create an ABC using inheritance.

  Constructor.

  ## Parameters

  `figure` :`Figure`
    the figure plotted by `FisherPlotter`
  `axes` :`Collection[Axes]`
    the axes of the above figure
  `names` :`Collection[str]`
    the names of the parameters that are plotted
  Expand source code

      class FisherBaseFigure(ABC):
          def __init__(
              self,
              figure : Figure,
              axes : Collection[Axes],
              names : Collection[str],
          ):
              """
              Constructor.

              Parameters
              ----------
              figure : Figure
                  the figure plotted by `FisherPlotter`

              axes : Collection[Axes]
                  the axes of the above figure

              names : Collection[str]
                  the names of the parameters that are plotted
              """
              self._figure = figure
              self._axes = axes
              self._names = names

          @abstractmethod
          def __getitem__(self, key):
              """
              Implements element access.
              """
              pass

          @property
          def figure(self):
              """
              Returns the underlying figure, an instance of `matplotlib.figure.Figure`.
              """
              return self._figure

          @property
          def axes(self):
              """
              Returns the axes of the figure as a numpy array.
              """
              return self._axes

          @property
          def names(self):
              """
              Returns the names of the parameters plotted.
              """
              return self._names

          def savefig(
              self,
              path : str,
              dpi : float = 300,
              bbox_inches : Union[str, Bbox] = 'tight',
              **kwargs,
          ):
              """
              Convenience wrapper for `figure.savefig`.

              Parameters
              ----------
              path : str
                  the path where to save the figure

              dpi : float, default = 300
                  the resolution of the saved figure

              bbox_inches : Union[str, Bbox], default = 'tight'
                  what is the bounding box for the figure

              kwargs
                  any other keyword arguments that should be passed to `figure.savefig`
              """
              return self.figure.savefig(path, dpi=dpi, bbox_inches=bbox_inches, **kwargs)

  ### Ancestors

  - abc.ABC

  ### Subclasses

  - FisherFigure1D
  - FisherFigure2D

  ### Instance variables

  `var axes`

    Returns the axes of the figure as a numpy array.

    Expand source code

        @property
        def axes(self):
            """
            Returns the axes of the figure as a numpy array.
            """
            return self._axes

  `var figure`

    Returns the underlying figure, an instance of `matplotlib.figure.Figure`.

    Expand source code

        @property
        def figure(self):
            """
            Returns the underlying figure, an instance of `matplotlib.figure.Figure`.
            """
            return self._figure

  `var names`

    Returns the names of the parameters plotted.

    Expand source code

        @property
        def names(self):
            """
            Returns the names of the parameters plotted.
            """
            return self._names

  ### Methods

  `def savefig(self, path: str, dpi: float = 300, bbox_inches: Union[str, Bbox] = 'tight', **kwargs)`

    Convenience wrapper for `figure.savefig`.

    ## Parameters

    `path` :`str`
      the path where to save the figure
    `dpi` :`float`, default `= 300`
      the resolution of the saved figure
    `bbox_inches` :`Union[str, Bbox]`, default `= 'tight'`
      what is the bounding box for the figure
    `kwargs`
      any other keyword arguments that should be passed to `figure.savefig`
    Expand source code

        def savefig(
            self,
            path : str,
            dpi : float = 300,
            bbox_inches : Union[str, Bbox] = 'tight',
            **kwargs,
        ):
            """
            Convenience wrapper for `figure.savefig`.

            Parameters
            ----------
            path : str
                the path where to save the figure

            dpi : float, default = 300
                the resolution of the saved figure

            bbox_inches : Union[str, Bbox], default = 'tight'
                what is the bounding box for the figure

            kwargs
                any other keyword arguments that should be passed to `figure.savefig`
            """
            return self.figure.savefig(path, dpi=dpi, bbox_inches=bbox_inches, **kwargs)

`class FisherFigure1D (figure: Figure, axes: Collection[Axes], names: Collection[str])`

  Container for easy access to elements in the 1D plot.

  Constructor.

  ## Parameters

  `figure` :`Figure`
    the figure plotted by `FisherPlotter`
  `axes` :`Collection[Axes]`
    the axes of the above figure
  `names` :`Collection[str]`
    the names of the parameters that are plotted
  Expand source code

      class FisherFigure1D(FisherBaseFigure):
          """
          Container for easy access to elements in the 1D plot.
          """
          def __getitem__(
              self,
              key : str,
          ):
              """
              Returns the axis associated to the name `key`.
              """
              if key not in self.names:
                  raise ParameterNotFoundError(key, self.names)

              return self.axes.flat[np.where(self.names == key)][0]

  ### Ancestors

  - FisherBaseFigure
  - abc.ABC

  ### Inherited members

  - `FisherBaseFigure`:
    - `axes`
    - `figure`
    - `names`
    - `savefig`
`class FisherFigure2D (figure: Figure, axes: Collection[Axes], names: Collection[str])`

  Container for easy access to elements in the 2D plot.

  Constructor.

  ## Parameters

  `figure` :`Figure`
    the figure plotted by `FisherPlotter`
  `axes` :`Collection[Axes]`
    the axes of the above figure
  `names` :`Collection[str]`
    the names of the parameters that are plotted
  Expand source code

      class FisherFigure2D(FisherBaseFigure):
          """
          Container for easy access to elements in the 2D plot.
          """
          def __getitem__(
              self,
              key : Tuple[str],
          ):
              pass

  ### Ancestors

  - FisherBaseFigure
  - abc.ABC

  ### Inherited members

  - `FisherBaseFigure`:
    - `axes`
    - `figure`
    - `names`
    - `savefig`
`class FisherPlotter (*args: FisherMatrix, labels: Optional[Collection[str]] = None)`

  Class for plotting FisherMatrix objects.

  Constructor.

  ## Parameters

  `args` :`FisherMatrix`
    `FisherMatrix` objects which we want to plot. Must have the same names, otherwise throws an error. Can have different fiducial values. The order of plotting of the parameters and the LaTeX labels to use are determined by the first argument.
  `labels` :`array` of `strings`, default `None`
    the list of labels to put in the legend of the plots. If not set, defaults to `0, ..., len(args) - 1`
  Expand source code

      class FisherPlotter:
          """
          Class for plotting FisherMatrix objects.
          """
          def __init__(
              self,
              *args : FisherMatrix,
              labels : Optional[Collection[str]] = None,
          ):
              """
              Constructor.

              Parameters
              ----------
              args : FisherMatrix
                  `FisherMatrix` objects which we want to plot.
                  Must have the same names, otherwise throws an error.
                  Can have different fiducial values.
                  The order of plotting of the parameters and the LaTeX labels to use
                  are determined by the first argument.

              labels : array of strings, default None
                  the list of labels to put in the legend of the plots.
                  If not set, defaults to `0, ..., len(args) - 1`
              """
              # make sure all of the Fisher objects have the same sizes
              if not all(len(args[0]) == len(arg) for arg in args):
                  raise MismatchingSizeError(*args)

              # check names
              if not all(set(args[0].names) == set(arg.names) for arg in args):
                  raise ValueError(
                      'The names of the inputs do not match'
                  )

              if labels is not None:
                  if len(labels) != len(args):
                      raise MismatchingSizeError(labels, args)
              else:
                  labels = list(map(str, np.arange(len(args))))

              # in case the names are shuffled, we sort them according to the FIRST
              # input
              indices = np.array(
                  [get_index_of_other_array(args[0].names, arg.names) for arg in args],
                  dtype=object,
              )

              self._values = [arg.sort(key=index) for index, arg in zip(indices, args)]

              self._labels = np.array(labels, dtype=object)

          @property
          def values(self):
              """
              Returns the input array of Fisher objects.
              """
              return self._values

          @property
          def labels(self):
              """
              Returns the labels of the Fisher objects.
              """
              return self._labels

          def find_limits_1d(
              self,
              name : str,
              sigma : float = 3,
          ):
              """
              Finds "nice" 1D limits for a given parameter taking into account fiducials
              and constraints.

              Parameters
              ----------
              name : str
                  the name of the parameter

              sigma : float, default = 3
                  how many sigmas away to plot

              Returns
              -------
              `tuple` with lower and upper limits
              """
              sigmas = np.array(
                  [_.constraints(name, marginalized=True, sigma=sigma) for _ in self.values]
              )
              fiducial = np.array(
                  [_.fiducial[np.where(_.names == name)] for _ in self.values]
              )

              xleft, xright = np.min(fiducial - sigmas), np.max(fiducial + sigmas)

              return xleft, xright

          def plot_1d(
              self,
              max_cols : Optional[int] = None,
              rc : dict = get_default_rcparams(),
              **kwargs,
          ):
              """
              Makes a 1D plot (Gaussians) of the Fisher objects and returns a
              `FisherFigure1D`.

              Parameters
              ----------
              max_cols : Optional[int], default = None
                  the maximum number of columns to force the plot into.
                  By default, the parameters are always plotted horizontally; if you
                  need to spread it over `max_cols`, pass a non-negative integer
                  here.

              rc : dict = get_default_rcparams()
                  any parameters meant for `matplotlib.rcParams`.
                  See [Matplotlib documentation](https://matplotlib.org/stable/tutorials/introductory/customizing.html)
                  for more information.

              Returns
              -------
              An instance of `FisherFigure1D`.
              """
              size = len(self.values[0])

              if max_cols is not None and max_cols <= size:
                  full = size % max_cols == 0
                  layout = size // max_cols if full else size // max_cols + 1, max_cols
              else:
                  layout = 1, size
                  full = True

              with plt.rc_context(rc):
                  # general figure setup
                  fig = plt.figure(clear=True, figsize=(2 * layout[1], 2 * layout[0]))
                  gs = fig.add_gridspec(
                      nrows=layout[0], ncols=layout[1],
                      hspace=0.5, wspace=0.1,
                  )
                  axes = gs.subplots()
                  if size == 1:
                      axes = np.array([axes])

                  names = self.values[0].names
                  latex_names = self.values[0].latex_names

                  ylabel1d = r'$p (\theta)$'

                  handles = []

                  for (index, name), name_latex in zip(enumerate(names), latex_names):
                      ax = axes.flat[index]
                      title_list = [
                          '{0} = ${1}^{{+{2}}}_{{-{2}}}$'.format(
                          name_latex,
                          float_to_latex(float(_.fiducial[np.where(_.names == name)])),
                          float_to_latex(float(_.constraints(name, marginalized=True))),
                      ) for _ in self.values
                      ]

                      # the scaling factor here is so that we don't cutoff the peak
                      ymax = np.max(
                          [gaussian(0, 0, _.constraints(name, marginalized=True)) for _ in self.values]
                      ) * 1.03

                      for fm in self.values:
                          handle, = add_plot_1d(
                              fm.fiducial[np.where(fm.names == name)],
                              fm.constraints(name, marginalized=True),
                              ax,
                          )

                          if index == 0:
                              handles.append(handle)

                      ax.set_xlabel(name_latex)
                      ax.set_xlim(*self.find_limits_1d(name))
                      ax.set_ylim(0, ymax)

                      if kwargs.get('title') is True:
                          ax.set_title('\n'.join(title_list))

                      if index == 0:
                          ax.set_ylabel(ylabel1d)

                      ax.set_yticks([])

                  if kwargs.get('legend') is True:
                      fig.legend(
                          np.array(handles, dtype=object),
                          self.labels,
                          frameon=False,
                          loc='upper center',
                          bbox_to_anchor=(0.5, -0.15),
                          ncol=len(self.values),
                      )

                  if isinstance(kwargs.get('title'), str):
                      fig.suptitle(kwargs.get('title'))

                  # remove any axes which are not drawn
                  if not full:
                      for index in range(
                          (layout[0] - 1) * layout[1] + 1,
                          layout[0] * layout[1]
                      ):
                          axes.flat[index].remove()

              return FisherFigure1D(fig, axes, names)

          def plot_2d(
              self,
              rc : dict = get_default_rcparams(),
              **kwargs,
          ):
              """
              Plots the 2D ellipses (and optionally 1D Gaussians) of the Fisher
              objects, and returns an instance of `FisherFigure2D`.
              """
              pass

          def plot_triangle(
              self,
              **kwargs,
          ):
              """
              Plots the 2D ellipses and 1D Gaussians of the Fisher objects, and
              returns an instance of `FisherFigure2D`.
              """
              pass

  ### Instance variables

  `var labels`

    Returns the labels of the Fisher objects.

    Expand source code

        @property
        def labels(self):
            """
            Returns the labels of the Fisher objects.
            """
            return self._labels

  `var values`

    Returns the input array of Fisher objects.

    Expand source code

        @property
        def values(self):
            """
            Returns the input array of Fisher objects.
            """
            return self._values

  ### Methods

  `def find_limits_1d(self, name: str, sigma: float = 3)`

    Finds "nice" 1D limits for a given parameter taking into account fiducials and constraints.

    ## Parameters

    `name` :`str`
      the name of the parameter
    `sigma` :`float`, default `= 3`
      how many sigmas away to plot

    ## Returns

    `tuple` with lower and upper limits

    Expand source code

        def find_limits_1d(
            self,
            name : str,
            sigma : float = 3,
        ):
            """
            Finds "nice" 1D limits for a given parameter taking into account fiducials
            and constraints.

            Parameters
            ----------
            name : str
                the name of the parameter

            sigma : float, default = 3
                how many sigmas away to plot

            Returns
            -------
            `tuple` with lower and upper limits
            """
            sigmas = np.array(
                [_.constraints(name, marginalized=True, sigma=sigma) for _ in self.values]
            )
            fiducial = np.array(
                [_.fiducial[np.where(_.names == name)] for _ in self.values]
            )

            xleft, xright = np.min(fiducial - sigmas), np.max(fiducial + sigmas)

            return xleft, xright

  `def plot_1d(self, max_cols: Optional[int] = None, rc: dict = {'mathtext.fontset': 'cm', 'font.family': 'serif'}, **kwargs)`

    Makes a 1D plot (Gaussians) of the Fisher objects and returns a `FisherFigure1D`.

    ## Parameters

    `max_cols` :`Optional[int]`, default `= None`
      the maximum number of columns to force the plot into. By default, the parameters are always plotted horizontally; if you need to spread it over `max_cols`, pass a non-negative integer here.
    `rc` :`dict = get_default_rcparams()`
      any parameters meant for `matplotlib.rcParams`. See Matplotlib documentation for more information.

    ## Returns

    An instance of `FisherFigure1D`.

    Expand source code

        def plot_1d(
            self,
            max_cols : Optional[int] = None,
            rc : dict = get_default_rcparams(),
            **kwargs,
        ):
            """
            Makes a 1D plot (Gaussians) of the Fisher objects and returns a
            `FisherFigure1D`.

            Parameters
            ----------
            max_cols : Optional[int], default = None
                the maximum number of columns to force the plot into.
                By default, the parameters are always plotted horizontally; if you
                need to spread it over `max_cols`, pass a non-negative integer
                here.

            rc : dict = get_default_rcparams()
                any parameters meant for `matplotlib.rcParams`.
                See [Matplotlib documentation](https://matplotlib.org/stable/tutorials/introductory/customizing.html)
                for more information.

            Returns
            -------
            An instance of `FisherFigure1D`.
            """
            size = len(self.values[0])

            if max_cols is not None and max_cols <= size:
                full = size % max_cols == 0
                layout = size // max_cols if full else size // max_cols + 1, max_cols
            else:
                layout = 1, size
                full = True

            with plt.rc_context(rc):
                # general figure setup
                fig = plt.figure(clear=True, figsize=(2 * layout[1], 2 * layout[0]))
                gs = fig.add_gridspec(
                    nrows=layout[0], ncols=layout[1],
                    hspace=0.5, wspace=0.1,
                )
                axes = gs.subplots()
                if size == 1:
                    axes = np.array([axes])

                names = self.values[0].names
                latex_names = self.values[0].latex_names

                ylabel1d = r'$p (\theta)$'

                handles = []

                for (index, name), name_latex in zip(enumerate(names), latex_names):
                    ax = axes.flat[index]
                    title_list = [
                        '{0} = ${1}^{{+{2}}}_{{-{2}}}$'.format(
                        name_latex,
                        float_to_latex(float(_.fiducial[np.where(_.names == name)])),
                        float_to_latex(float(_.constraints(name, marginalized=True))),
                    ) for _ in self.values
                    ]

                    # the scaling factor here is so that we don't cutoff the peak
                    ymax = np.max(
                        [gaussian(0, 0, _.constraints(name, marginalized=True)) for _ in self.values]
                    ) * 1.03

                    for fm in self.values:
                        handle, = add_plot_1d(
                            fm.fiducial[np.where(fm.names == name)],
                            fm.constraints(name, marginalized=True),
                            ax,
                        )

                        if index == 0:
                            handles.append(handle)

                    ax.set_xlabel(name_latex)
                    ax.set_xlim(*self.find_limits_1d(name))
                    ax.set_ylim(0, ymax)

                    if kwargs.get('title') is True:
                        ax.set_title('\n'.join(title_list))

                    if index == 0:
                        ax.set_ylabel(ylabel1d)

                    ax.set_yticks([])

                if kwargs.get('legend') is True:
                    fig.legend(
                        np.array(handles, dtype=object),
                        self.labels,
                        frameon=False,
                        loc='upper center',
                        bbox_to_anchor=(0.5, -0.15),
                        ncol=len(self.values),
                    )

                if isinstance(kwargs.get('title'), str):
                    fig.suptitle(kwargs.get('title'))

                # remove any axes which are not drawn
                if not full:
                    for index in range(
                        (layout[0] - 1) * layout[1] + 1,
                        layout[0] * layout[1]
                    ):
                        axes.flat[index].remove()

            return FisherFigure1D(fig, axes, names)

  `def plot_2d(self, rc: dict = {'mathtext.fontset': 'cm', 'font.family': 'serif'}, **kwargs)`

    Plots the 2D ellipses (and optionally 1D Gaussians) of the Fisher objects, and returns an instance of `FisherFigure2D`.

    Expand source code

        def plot_2d(
            self,
            rc : dict = get_default_rcparams(),
            **kwargs,
        ):
            """
            Plots the 2D ellipses (and optionally 1D Gaussians) of the Fisher
            objects, and returns an instance of `FisherFigure2D`.
            """
            pass

  `def plot_triangle(self, **kwargs)`

    Plots the 2D ellipses and 1D Gaussians of the Fisher objects, and returns an instance of `FisherFigure2D`.

    Expand source code

        def plot_triangle(
            self,
            **kwargs,
        ):
            """
            Plots the 2D ellipses and 1D Gaussians of the Fisher objects, and
            returns an instance of `FisherFigure2D`.
            """
            pass