Implementing your own plot functions#

my_plot:
  creator: some_creator
  # ... plot configuration parameters here ...

my_other_plot:
  creator: another_creator
  # ... plot configuration parameters for this plot ...

This leads to the PlotManager instantiating a plot creator some_creator, which is instructed to create a plot called my_plot. The additional parameters are passed to the plot creator, which then uses these for its own purposes. The same happens for the my_other_plot plot, which uses another_creator. For more information on the PlotManager, refer to the dantro documentation.

The PyPlotCreator#

In Utopia, the PyPlotCreator has a central role, as it forms the basis of several, more specialized plot creators. The “external” refers to is abiliy to invoke some plot function from an external module or file. Such a plot function can essentially be arbitrary. However, the PyPlotCreator has some specialized functionality for working with matplotlib which aims to make plotting more convenient: the style option and the PlotHelper framework. Furthermore, it has access to dantro’s data transformation framework.

In practice, the PyPlotCreator itself is hardly used in Utopia, but it is the base class of the UniversePlotCreator and the MultiversePlotCreator. Thus, the following information is valid for both these specializations and is important to understand before looking at the other creators. More detail on the specializations themselves is given later.

Specifying which plotting function to use#

Let’s assume we have a plotting function defined somewhere and want to communicate to the PyPlotCreator that this function should be used for some plot. For the moment, the exact definition of the function is irrelevant. You can read more about it in below.

Importing a plotting function from a module#

To import a plot function, the module and plot_func entries are required. The following example shows a plot that uses a plot function from utopya.plot_funcs and another plot that uses some (importable) package from which the module and the plot function are imported:

---
my_plot:
  # Import some module from utopya.plot_funcs (note the leading dot)
  module: .distribution

  # Use the function with the following name from that module
  plot_func: my_plot_func

  # ... all other arguments

my_other_plot:
  # Import a module from any installed package
  module: my_installed_plotting_package.some_module
  plot_func: my_plot_func

  # ... all other arguments

Importing a plotting function from a file#

There are plenty of plot function implementations provided both by utopya and the various Utopia models. However, you might also want to implement a plot function of your own design. This can be achieved by specifying the module_file key instead of the module key in the plot configuration. The python module is then loaded from file and the plot_func key is used to retrieve the plotting function:

---
my_plot:
  # Load the following file as a python module
  module_file: ~/path/to/my/python/script.py

  # Use the function with the following name from that module
  plot_func: my_plot_func

  # ... all other arguments (as usual)

The PlotHelper#

The aim of the PlotHelper framework is to let the plot functions focus on what cannot easily be automated: being the bridge between some selected data and its visualization. The plot function should not have to concern itself with plot aesthetics, as these can be easily automated. The PlotHelper framework can make your life significantly easier, as it already takes care of most of the plot aesthetics by making large parts of the matplotlib interface accessible via the plot configuration. That way, you don’t need to touch Python code for trivial tasks like changing the plot limits. It also takes care of setting up a figure and storing it in the appropriate location. Most importantly, it will make your plots future-proof and let them profit from upcoming features. For available plot helpers, have a look at the PlotHelper API reference.

As an example, the following plot configuration sets the title of the plot as well as the labels and limits of the axes:

my_plot:
  # ...

  # Configure the plot helpers
  helpers:
    set_title:
      title: This is My Fancy Plot
    set_labels:
      x: $A$
      y: Counts $N_A$
    set_limits:
      x: [0, max]
      y: [1.0, ~]

Furthermore, notice how you can combine the capabilities of the plot helper framework with the ability to set the plot style.

Implementing plot functions#

Below, you will learn how to implement a plot function that can be used with the plot creator.

The is_plot_func() decorator#

When defining a plot function, we recommend using this decorator. It takes care of providing essential information to the PyPlotCreator and makes it easy to configure those parameters relevant for the plot function. For example, to specify which creator should be used for the plot function, the creator argument can specify the name of a creator. To control usage of the data transformation framework, the use_dag flag can be used and the required_dag_tags argument can specify which data tags the plot function expects.

Other possible plot function signatures#

Warning

The examples below are for the PyPlotCreator and might need to be adapted for the specialized plot creators.

Examples for those creators are given in the dantro documentation and here.

Without DAG framework#

If you wish not to use the data transformation framework, simply omit the use_dag flag or set it to False in the decorator. When not using the transformation framework, the creator_type should be specified, thus binding the plot function to one type of creator.

from utopya import DataManager
from utopya.eval import is_plot_func, PlotHelper

@is_plot_func()
def my_plot(dm: DataManager, *, hlpr: PlotHelper, **additional_kwargs):
    """A plot function using the plot helper framework.

    Args:
        dm: The DataManager object that contains all loaded data.
        hlpr: The associated plot helper
        **additional_kwargs: Anything else from the plot config.
    """
    # Select some data ...
    data = dm['foo/bar']

    # Create a lineplot on the currently selected axis
    hlpr.ax.plot(data)

    # When exiting here, the plot helper saves the plot.

Note

The dm argument is only provided when not using the DAG framework.

Bare basics#

If you really want to do everything by yourself, you can also disable the plot helper framework by passing use_helper=False to the decorator. The hlpr argument is then not passed to the plot function.

There is an even more basic version of doing this, leaving out the is_plot_func() decorator:

from utopya import DataManager

def my_bare_basics_plot(dm: DataManager, *, out_path: str,
                        **additional_kwargs):
    """Bare-basics signature required by the ExternalPlotCreator.

    Args:
        dm: The DataManager object that contains all loaded data.
        out_path: The generated path at which this plot should be saved
        **additional_kwargs: Anything else from the plot config.
    """
    # Your code here ...

    # Save to the specified output path
    plt.savefig(out_path)

Note

When using the bare basics version, you need to set the creator argument in the plot configuration in order for the plot manager to find the desired creator.