Skip to content

[ENH]: A friendlier interface for pyplot #30139

Closed as not planned
Closed as not planned
@alfonsoSR

Description

@alfonsoSR

Problem

I find the syntax of pyplot a bit difficult to remember, and I have been thinking for a while about a way of making it friendlier taking advantage of Python tools. After some years of on/off iteration, I converged to what I think is a really nice interface. In very brief, I use:

  • A dataclass for all the setup (figure size, labels, ticks, grid, whether to save/show the figure...)
  • A context manager to plot, and then do what I call "postprocessing" (color lines, create legend, adjust boundaries, show figure, save figure to a file...) automatically when the plot goes out of scope.

This is a (rather complete) example script, and the figure it generates:

import nastro.graphics as ng
import numpy as np

if __name__ == "__main__":

    # Generate data
    x = np.linspace(0, 2 * np.pi, 100)
    y = np.sin(x)
    z = np.cos(x)
    error = np.abs(np.random.normal(0.0, 0.1, x.shape))
    xbar = np.arange(8)
    ybar = np.random.normal(5, 2, 8)
    gen = np.random.default_rng(1234797)

    # Figure configuration
    figure_setup = ng.PlotSetup(
        canvas_size=(12, 7),
        title="A simple figure with different types of subplots",
        save=True,
        dir=".",
        name="plots.png",
        show=False,
    )
    a_setup = ng.PlotSetup(ylabel="left", rlabel="right", plabel="parasite")
    c_setup = ng.PlotSetup(grid=False)
    d_setup = ng.PlotSetup(ylabel="sin(x)", rlabel="cos(x)")

    with ng.Mosaic("ab;cd;ef", figure_setup) as fig:

        with fig.subplot(a_setup, ng.ParasiteAxis) as a:

            a.line(x, x, fmt=".-", markersize=2, label="x")
            a.line(x, x**2, fmt="--", axis="right", label="x^2")
            a.line(x, x**3, axis="parasite", label="x^3")

        with fig.subplot(generator=ng.SingleAxis) as b:

            b.line(x, y, label="sin")
            b.line(x, z, label="cos")
            b.boundary(0.5, reference="sin")
            b.boundary(0.25, reference="sin", alpha=0.3)

        with fig.subplot(c_setup) as c:

            c.bar(xbar, ybar)

        with fig.subplot(d_setup, ng.DoubleAxis) as d:

            d.errorbar(x, y, np.abs(gen.normal(0.0, 0.1, x.shape)))
            d.errorbar(
                x, z, np.abs(gen.normal(0.0, 0.1, x.shape)), axis="right"
            )
            d.line(x, 0.8 * y)

        with fig.subplot(generator=ng.DoubleAxis) as e:

            e.step(x, y, fmt=".-")
            e.step(x, z, axis="right")

        with fig.subplot() as f:

            f.barh(xbar[:4], ybar[:4])

Image

This interface was born as a submodule of a library I was writing for astrodynamics, but I have been using it for general plotting for about a year now, and it seems flexible and robust. I don't think I have the technical knowledge, or the time to add this to matplotlib, but maybe someone here does.

All my source code is open, and I'd be happy to discuss the details of my implementation, or to improve the description of this issue if you are interested :)

Proposed solution

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions