Skip to main content

Maxplotlib

Maxplotlib

A clean, expressive wrapper around Matplotlib, Plotly, plotext, and tikzfigure for producing publication-quality figures with minimal boilerplate. Swap backends without rewriting your data — render the same canvas as a crisp PNG, an interactive Plotly chart, a terminal-native plotext figure, or camera-ready TikZ code for LaTeX.

Install

pip install maxplotlibx

Showcase

Quickstart

import numpy as np
from maxplotlib import Canvas

x = np.linspace(0, 2 * np.pi, 200)
y = np.sin(x)

canvas, ax = Canvas.subplots()
ax.plot(x, y)

Figure 1

Plot the figure with the default (matplotlib) backend:

canvas.show()

Use canvas.plot(x, y) to add data directly to a canvas. When you want to explicitly render an already-built canvas, use canvas.render(...). The older canvas.plot(backend=...) spelling is still supported for compatibility, but emits a FutureWarning.

Add several lines at once with shared styling:

canvas.plot_many(
    [(x, np.sin(x)), (x, np.cos(x))],
    labels=["sin(x)", "cos(x)"],
    linewidth=2,
)

Common figure and axis settings can be grouped with configure():

canvas.configure(
    title="Trigonometry",
    xlabel="Angle",
    ylabel="Value",
    grid=True,
    facecolor="whitesmoke",
)

For Matplotlib-specific customization, pass method calls declaratively. Figure methods run once and axes methods run for every subplot, providing access to any Matplotlib API without requiring a maxplotlib wrapper:

canvas.plot(matplotlib_customizations={
    "figure": {
        "suptitle": "My figure",
    },
    "axes": {
        "tick_params": {
            "axis": "both",
            "which": "major",
            "length": 6,
        },
    },
})

For dynamic customization, the same option also accepts a function:

def customize(fig, axes):
    fig.suptitle("My figure")
    for ax in axes.flat:
        ax.tick_params(axis="both", which="major", length=6)

canvas.plot(matplotlib_customizations=customize)

Axis Label and Tick Styling

Axis labels, titles, and tick appearance accept Matplotlib-style keyword arguments:

canvas.set_xlabel("Time", fontsize=12, fontweight="bold", labelpad=10)
canvas.set_ylabel("Duration", color="darkblue")
canvas.set_title("Runtime", fontsize=14, color="navy")
canvas.tick_params(
    axis="both",
    which="major",
    labelsize=10,
    colors="darkgreen",
    length=6,
)

Common axis controls and figure-level layout settings are also available:

canvas.set_facecolor("whitesmoke")
canvas.set_axisbelow(True)
canvas.margins(x=0.05, y=0.1)
canvas.minorticks_on()
canvas.invert_yaxis()
canvas.supxlabel("Shared x label")
canvas.supylabel("Shared y label")
canvas.subplots_adjust(left=0.15, bottom=0.15)
canvas.tight_layout()

Secondary Y-Axis

Use Canvas.twinx() to add a second y-axis that shares the primary x-axis:

twin_canvas, primary = Canvas.subplots()
secondary = twin_canvas.twinx()

primary.plot(x, np.sin(x), color="tab:blue")
secondary.plot(x, 100 * np.cos(x), color="tab:red")
primary.set_ylabel("sin(x)", color="tab:blue")
secondary.set_ylabel("100 cos(x)", color="tab:red")

twin_canvas.show()

Secondary y-axes are currently supported by the Matplotlib and Plotly backends.

Plotly field plots and tables

Several Matplotlib field and annotation APIs map directly to interactive Plotly traces, including pseudocolor plots, sparsity patterns, triangular grids, and tables:

plotly_canvas, plotly_ax = Canvas.subplots()
plotly_ax.pcolor(x, x, np.outer(np.sin(x), np.cos(x)))
plotly_ax.spy([[1, 0, 1], [0, 1, 0], [1, 0, 1]])
plotly_ax.table(cellText=[["A", "B"], ["1", "2"]])
plotly_canvas.show(backend="plotly")

Plotly raises NotImplementedError for primitives without a faithful equivalent instead of silently dropping them. To render the supported parts of a mixed canvas, explicitly opt into skipping unsupported primitives:

plotly_canvas.render(backend="plotly", allow_unsupported=True)

Render the same line graph directly in the terminal with the plotext backend:

terminal_fig = canvas.render(backend="plotext")
print(terminal_fig.build(keep_colors=False))
Trigonometry
                                       Runtime                                  
     ┌┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬┐
 1.00┼ ▞▞ sin(x) ──▗▄▞▀▀▀▀▀▙▄▖────────────┼─────────────────┼─────────────▄▄▀▀▀┤
     │ ▞▞ cos(x) ▄▀▘    │    ▝▀▄          │                 │          ▄▞▀    ││
     ││       ▜▄▘       │       ▀▄        │                 │        ▄▛       ││
 0.67┼┼──────▟▀─▀▄──────┼─────────▀▄──────┼─────────────────┼──────▄▀─────────┼┤
     ││    ▄▛    ▝▚▖    │           ▚▖    │                 │    ▗▞▘          ││
 0.33┼┼──▗▞────────▀▖───┼────────────▝▄───┼─────────────────┼───▗▀────────────┼┤
     ││ ▄▀          ▝▚  │              ▚▖ │                 │  ▞▘             ││
     │▗▞▘             ▀▖│               ▀▄│                 │▗▀               ││
 0.00┼▞────────────────▝▙────────────────▝▚▖────────────────▟▘────────────────▄┤
     ││                 │▜▖               │▀▄             ▗▛│               ▗▞▘│
     ││                 │ ▝▙              │ ▝▚           ▟▘ │              ▄▀ ││
-0.33┼┼─────────────────┼───▚▖────────────┼───▀▖───────▗▞───┼─────────────▞▘──┼┤
     ││                 │    ▝▄▖          │    ▝▚    ▗▄▘    │           ▟▀    ││
-0.67┼┼─────────────────┼──────▀▖─────────┼──────▀▄─▗▀──────┼─────────▄▛──────┼┤
     ││                 │       ▝▜▄       │       ▄▛▘       │       ▗▞▘       ││
     ││                 │          ▀▄▖    │    ▗▄▀   ▀▄▖    │    ▗▄▀▘         ││
-1.00┼┼─────────────────┼────────────▝▀▚▄▄▄▄▄▞▀▘───────▝▀▜▄▄▄▄▄▞▀▘────────────┼┤
     └┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼┘
     0.0               1.6               3.1               4.7              6.3 
Duration                                Time                                    

Or plot with the TikZ backend:

canvas.show(backend="tikzfigure")

Subplots and Meshes with the TikZ Backend

The tikzfigure backend draws the canvas with Matplotlib and converts the drawn figure into pgfplots axes, so every layout converts (rows, columns, grids, twin axes), with LaTeX text, legends and colorbars. Lines, markers, bars and text are pgfplots code; meshes and images are included as images:

x = np.linspace(0, 2 * np.pi, 200)
canvas, (ax1, ax2) = Canvas.subplots(ncols=2, width="12cm", ratio=0.45)

ax1.plot(x, np.sin(x), color="royalblue", label="$\\sin x$")
ax1.plot(x, np.cos(x), color="tomato", label="$\\cos x$")
ax1.set_title("Lines")
ax1.set_legend(True)

xx, yy = np.meshgrid(x, x)
ax2.pcolormesh(xx, yy, np.sin(xx) * np.cos(yy), cmap="RdBu_r")
ax2.add_colorbar(label="$\\sin x \\cos y$")
ax2.set_title("A mesh")

canvas.show(backend="tikzfigure")  # compiles with pdflatex

Figure 2

canvas.render(backend="tikzfigure").savefig("figure.tikz") writes the code for \\input in a LaTeX document, with the images next to it. Any Matplotlib figure converts the same way with maxplotlib.backends.tikzfigure.figure_to_tikz(fig). See the tutorials for more examples.

Terminal Backend with plotext

The plotext backend is designed for terminal-first workflows. It currently supports line plots, scatter plots, bars, filled regions, error bars, reference lines, text/annotations, labels/titles, log axes, layers, matrix-style imshow() rendering, common patches, and multi-subplot canvases.

x = np.linspace(1, 10, 40)

canvas, ax = Canvas.subplots()
ax.plot(x, np.sqrt(x), color="cyan", label="sqrt(x)")
ax.errorbar(x[::8], np.sqrt(x[::8]), yerr=0.15, color="yellow", label="samples")
ax.set_title("Terminal plot")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_xscale("log")
ax.set_legend(True)

canvas.show(backend="plotext")
                                    Terminal plot                               
    ┌──────────────────────────────────────────────────────────────────────────┐
3.16┤ ▞▞ sqrt(x)                                                             ▄▞│
    │                                                                   │▗▄▞▀  │
    │                                                                  ▄┼▘     │
2.79┤                                                               ▄▀▀        │
    │                                                            ┼▀▀           │
2.42┤                                                        ▗▞▀▀│             │
    │                                                    ▗▞▀▀▘                 │
    │                                               ▗▄┼▄▀▘                     │
2.04┤                                            ▗▄▀▘ │                        │
    │                                        ▗▄▞▀▘                             │
    │                                 │  ▄▄▀▀▘                                 │
1.67┤                              ▗▄▄┼▀▀                                      │
    │                        ▗▄▄▞▀▀▘                                           │
1.30┤                 ▄▄▄▄▀▀▀▘                                                 │
    │            ▄▄▞▀▀                                                         │
    │┼  ▗▄▄▞▀▀▀▀▀                                                              │
0.93┤│▀▀▘                                                                      │
    └┬─────────────────┬──────────────────┬─────────────────┬─────────────────┬┘
    1.0               1.8                3.2               5.6             10.0 
y                                         x                                     

<maxplotlib.backends.plotext.figure.PlotextFigure at 0x1102a0690>

Layers

x = np.linspace(0, 2 * np.pi, 200)

canvas, ax = Canvas.subplots(width="10cm", ratio=0.55)

ax.plot(x, np.sin(x), color="steelblue", label=r"$\sin(x)$", layer=0)
ax.plot(x, np.cos(x), color="tomato", label=r"$\cos(x)$", layer=1)
ax.plot(
    x,
    np.sin(x) * np.cos(x),
    color="seagreen",
    label=r"$\sin(x)\cos(x)$",
    linestyle="dashed",
    layer=2,
)

ax.set_xlabel("x")
ax.set_legend(True)

Figure 3

Show layer 0 only, then layers 0 and 1, then everything:

canvas.show(layers=[0])

(<Figure size 590.551x324.803 with 1 Axes>,
 array([[<Axes: xlabel='x'>]], dtype=object))

Show all layers:

canvas.show()

(<Figure size 590.551x324.803 with 1 Axes>,
 array([[<Axes: xlabel='x'>]], dtype=object))

xarray data

Plot labelled xarray data directly (pip install maxplotlibx[xarray]). Axes come from the coordinates, labels from the long_name and units attributes, and titles from the coordinates you selected. import maxplotlib.xarray adds a .maxplot accessor that mirrors xarray’s own .plot API and returns an ordinary Canvas, so the backend is still chosen when rendering:

import xarray as xr

import maxplotlib.xarray  # registers da.maxplot and ds.maxplot

t = np.linspace(0, 1.5, 6)
xs = np.linspace(0, 2 * np.pi, 80)
ys = np.linspace(-1, 1, 50)
wave = xr.DataArray(
    np.sin(xs - 2 * t[:, None, None]) * np.exp(-3 * ys[None, :, None] ** 2),
    dims=("t", "y", "x"),
    coords={"t": ("t", t, {"units": "s"}), "y": ys, "x": ("x", xs, {"units": "m"})},
    name="phi",
    attrs={"long_name": "Potential", "units": "V"},
)

wave.maxplot.pcolormesh(col="t", col_wrap=3, canvas_kwargs={"width": "16cm", "ratio": 0.6}).show()

(<Figure size 944.882x566.929 with 7 Axes>,
 array([[<Axes: title={'center': 't = 0 s'}, ylabel='y'>,
         <Axes: title={'center': 't = 0.3 s'}>,
         <Axes: title={'center': 't = 0.6 s'}>],
        [<Axes: title={'center': 't = 0.9 s'}, xlabel='x [m]', ylabel='y'>,
         <Axes: title={'center': 't = 1.2 s'}, xlabel='x [m]'>,
         <Axes: title={'center': 't = 1.5 s'}, xlabel='x [m]'>]],
       dtype=object))

The same works through Canvas methods, e.g. canvas.plot(da, hue="species"), ax.pcolormesh(da, xcoord="R", ycoord="Z") for curvilinear grids, or Canvas.facet(da, col="t"). ds.maxplot.scatter(x=..., y=..., hue=...) plots one Dataset variable against another. See the xarray tutorial for more.

Metadata

Release files for maxplotlibx 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for maxplotlibx 0.2.0
File Size Uploaded
maxplotlibx-0.2.0.tar.gz 140.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for maxplotlibx 0.2.0
File Interpreter ABI Platform
maxplotlibx-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 288.1 kB

Release files / maxplotlibx-0.2.0.tar.gz

Download URL maxplotlibx-0.2.0.tar.gz
Size 140.7 kB
Tags Source
SHA-256 checksum
How to use checksums
72abed8d5854208a8d88dc34059596b7b02edd08b2be0a01c210ba9fe0354aee
BLAKE2b-256 checksum
How to use checksums
bbb78d99c5e109a50b0f4604ad6eddfb3975b33c9a98d3242f9807b544d2a082
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.21

Release files / maxplotlibx-0.2.0-py3-none-any.whl

Download URL maxplotlibx-0.2.0-py3-none-any.whl
Size 147.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ab51ed3c0a2786a76f3b61a3f181dc9156095a98d70dd7cb3ef7342f37f17308
BLAKE2b-256 checksum
How to use checksums
6736e1d1a36db8b8fc1c5963cc3f32abab0eae50b14e42818c8e6fa26819660e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.21

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page