Source code for ribs.visualize._archive_ecdf

"""Provides archive_ecdf."""

from __future__ import annotations

from matplotlib.axes import Axes
from pandas import DataFrame
from seaborn import ecdfplot

from ribs.archives import ArchiveBase, ArchiveDataFrame
from ribs.visualize._utils import validate_df


[docs] def archive_ecdf( archive: ArchiveBase, df: DataFrame | ArchiveDataFrame | None = None, **kwargs, ) -> Axes: r"""Plots an ECDF or ECCDF of the objective values in an archive. Generally, the `ECDF (empirical cumulative distribution function) <https://en.wikipedia.org/wiki/Empirical_distribution_function>`_ counts the number of observations that fall below each output value. In the case of archives, an ECDF counts the number of elites that perform worse than or equal to each objective value. Conversely, an ECCDF (empirical *complementary* cumulative distribution function) counts the number of elites that perform better than or equal to each objective value. This function is a thin wrapper around Seaborn's :func:`~seaborn.ecdfplot` that extracts the objective values from the archive and then passes them as data. All kwargs are also passed directly to ``ecdfplot``. Similar to other pyribs visualization functions, one can also pass in the archive and a ``df`` previously extracted from the archive's :meth:`~ribs.archives.ArchiveBase.data` method. Examples: ECDF of a GridArchive .. plot:: :context: close-figs import numpy as np import matplotlib.pyplot as plt from ribs.archives import GridArchive from ribs.visualize import archive_ecdf # Populate the archive with the negative sphere function. archive = GridArchive(solution_dim=2, dims=[10, 10], ranges=[(-1, 1), (-1, 1)]) x = np.random.uniform(-1, 1, 10000) y = np.random.uniform(-1, 1, 10000) archive.add(solution=np.stack((x, y), axis=1), objective=-(x**2 + y**2), measures=np.stack((x, y), axis=1)) # Plot ECDF of the archive. plt.figure(figsize=(8, 6)) archive_ecdf(archive, stat="count") plt.xlabel("Objective") plt.ylabel("Num. Elites") plt.show() ECCDF of a GridArchive .. plot:: :context: close-figs import numpy as np import matplotlib.pyplot as plt from ribs.archives import GridArchive from ribs.visualize import archive_ecdf # Populate the archive with the negative sphere function. archive = GridArchive(solution_dim=2, dims=[10, 10], ranges=[(-1, 1), (-1, 1)]) x = np.random.uniform(-1, 1, 10000) y = np.random.uniform(-1, 1, 10000) archive.add(solution=np.stack((x, y), axis=1), objective=-(x**2 + y**2), measures=np.stack((x, y), axis=1)) # Plot ECCDF of the archive. Note the use of `complementary`. plt.figure(figsize=(8, 6)) archive_ecdf(archive, complementary=True, stat="count") plt.xlabel("Objective") plt.ylabel("Num. Elites") plt.show() Args: archive: An archive that can provide its objective values via the :meth:`~ribs.archives.ArchiveBase.data` method. df: If provided, we will plot data from this argument instead of the data currently in the archive. This data can be obtained by, for instance, calling :meth:`ribs.archives.ArchiveBase.data` with ``return_type="pandas"`` and modifying the resulting :class:`~ribs.archives.ArchiveDataFrame`. Note that, at a minimum, the data must contain a column for "objective". kwargs: Kwargs for :func:`~seaborn.ecdfplot`. Returns: The Matplotlib axes containing the plot. Raises: AttributeError: The data() method is not implemented on the given archive, which prevents retrieving its objective values. """ # Retrieve archive data. if df is None: try: objectives = archive.data("objective") except NotImplementedError as e: raise AttributeError( "To use archive_ecdf, the archive must have the data() method." ) from e else: df = validate_df(df) objectives = df["objective"] return ecdfplot(objectives, **kwargs)