Plotting Architectureο
This note describes the plotting architecture that exists today after the recent cleanup work. Its purpose is to explain the current responsibility boundaries so that future plotting changes remain focused and consistent.
It is not a feature guide and it is not an RFC.
Motivationο
The plotting cleanup was driven by accumulated maintenance problems:
public plotting regressions caused by duplicated dispatch and wrapper logic;
inconsistent handling of method aliases and plotting kwargs;
normalization rules spread across several modules;
wrappers partially re-implementing backend behavior.
The resulting architecture intentionally separates:
plotting vocabulary;
kwargs normalization;
style interpretation;
artist creation;
specialized orchestration.
Current Architectureο
The main public plotting surface includes:
NDDataset.plot()for dimension-aware default dispatch;explicit helpers such as
plot_pen(),plot_scatter(),plot_bar(),plot_lines(),plot_contour(),plot_contourf(),plot_image(),plot_surface(), andplot_waterfall();orchestration helpers such as
plot_multiple()andmultiplot();composite plotters and plugin plotters, which build on the shared plotting layer but may own additional specialized behavior.
The normal dataset plotting path is:
NDDataset.plot()
-> plotting.dispatcher.plot_dataset()
-> plotting.backends.matplotlib_backend.plot_dataset_impl()
-> plotting._methods
-> plotting._kwargs
-> plotting._style
-> plot1d.py / plot2d.py / plot3d.py
-> Matplotlib artists
Read that flow as a sequence of responsibilities, not as a promise that every function literally calls each helper in exactly that textual order for every plot family.
A simplified architecture sketch is:
User API
|
v
Dispatcher
|
v
Backend
|
+-- _methods.py (semantic normalization)
+-- _kwargs.py (parameter normalization)
+-- _style.py (style interpretation)
|
v
plot1d / plot2d / plot3d
|
v
Matplotlib artists
Responsibility Boundariesο
plotting._methodsο
This module owns plotting vocabulary normalization:
canonical method names;
compatibility aliases such as
lines <-> penwhere appropriate;dimensional aliases such as
stack -> linesandmap -> contour;validation of supported method names for the relevant plotting family.
This module does not decide styling or create artists.
plotting._kwargsο
This module owns plotting kwargs normalization:
alias renaming such as
lw -> linewidthandls -> linestyle;marker-related aliases such as
ms -> markersizeandmew -> markeredgewidth;color aliases such as
c -> colorandcolormap -> cmap;production of canonical kwargs for downstream plotting code.
This module does not interpret what those values should mean visually.
plotting._styleο
This module owns style interpretation after normalization:
geometry-dependent marker defaults;
line defaults and palette decisions;
colormap and alpha interpretation;
conversion of normalized plotting inputs into concrete style choices.
This is the layer that answers questions such as βwhat marker should a scatter plot use by default?β rather than βwhat is the canonical spelling of this kwarg?β.
plot1d.py / plot2d.py / plot3d.pyο
These plotter modules own rendering work:
creation of Matplotlib artists;
geometry-specific axis policy;
application of normalized methods, kwargs, and interpreted style to concrete artists.
This is where rendering happens. These modules should not duplicate
normalization logic that belongs in _methods.py or _kwargs.py.
Dispatcher and Backendο
The dispatcher selects the active plotting backend. The Matplotlib backend
owns the standard dataset plotting dispatch for that backend and the top-level
show handling for that path.
This layer is responsible for sending an already-understood plotting request to the correct rendering family. It is not the place to redefine plotting vocabulary or kwargs aliases.
Specialized Orchestrationο
Some plotting entry points are intentionally more specialized:
plot_multiple()overlays several datasets on one axes;multiplot()builds grids of axes and delegates panel rendering;composite plotters may combine several visuals with their own layout logic;
plugin plotting entry points may adapt plugin-specific objects or workflows before delegating to the shared plotting layer.
These helpers are orchestration code. They should reuse the shared normalization and rendering layers rather than re-implement them.
Design Principlesο
The cleanup adopted a small set of explicit design principles:
Normalize firstο
Method aliases and kwargs aliases should be normalized before downstream code interprets or renders them.
Interpret laterο
Style decisions belong after normalization, when the plotting geometry and canonical parameters are already known.
Render lastο
Artist creation should be the final step. Rendering code should receive already-normalized, already-interpreted inputs whenever possible.
Avoid duplicated normalizationο
When a method alias or kwargs alias needs to change, contributors should update the central helper module rather than patching several wrappers independently.
Keep semantics separate from renderingο
Plotting semantics, normalization, style interpretation, and artist creation are related, but they are not the same responsibility. The current structure exists to keep those concerns legible.
Architecture Rulesο
The following rules are intended to help future contributors preserve the current structure:
Public plotting methods must not duplicate method normalization. Use
_methods.py.Public plotting methods must not duplicate kwargs alias handling. Use
_kwargs.py.Style decisions belong in
_style.py.Plotter modules create artists; they do not normalize public API aliases.
Specialized plotters such as
multiplot(), composite plotters, and plugin plotters may orchestrate figures, but they should reuse the common normalization layers.New plotting methods should fit into the existing responsibility split rather than introducing new local normalization logic.
What Was Intentionally Not Centralizedο
Not every plotting responsibility should live in one helper layer.
The following remain intentionally local or specialized:
subplot layout and panel orchestration in
multiplot();overlay-specific behavior in
plot_multiple();composite visualizations that combine several plot types or analysis results;
plugin-specific plotting entry points and adapters;
parts of the figure/axes lifecycle that are inherently tied to specialized orchestration.
Centralizing these behaviors prematurely would blur the distinction between shared plotting semantics and higher-level composition logic.
Figure and Axes Lifecycleο
Figure and axes ownership remains partly centralized and partly specialized:
the main dataset plotting path uses
NDDataset._figure_setup()to decide whether to create a figure, reuse the current figure, or use an explicitax;ax,clear, andshowremain the main lifecycle controls for ordinary dataset plots;orchestration helpers such as
plot_multiple()andmultiplot()manage figure composition more directly because they coordinate several plot calls;composite plots may own their lifecycle outright.
This boundary is documented here because it affects contributor expectations, but it was intentionally not folded into the normalization helpers.
Practical Contributor Guidanceο
When changing plotting code:
put method vocabulary changes in
_methods.py;put kwargs alias changes in
_kwargs.py;keep style interpretation in
_style.py;keep artist creation in the plotter modules;
avoid re-implementing normalization in wrappers or composite helpers;
treat
plot_multiple()andmultiplot()as orchestration layers, not as places to invent alternate plotting semantics.
Future Evolutionο
The current architecture is stable, but a few areas may deserve future attention:
a clearer figure/axes lifecycle contract across ordinary and composite plots;
possible convergence of plugin-side helper patterns where duplication reappears;
future rendering abstractions only if they solve a demonstrated maintenance problem.
Any future evolution should preserve the current responsibility split unless a clear replacement architecture is intentionally adopted.