abstracts
Abstract visualizer interfaces for the PySATL CPD visualization system.
This subpackage exports the abstract base classes that define the contracts for visualizers and composable drawing components. Concrete implementations subclass these interfaces to render time series data, detection traces, benchmark metrics, and annotation elements using either Matplotlib or Plotly backends.
Architecture
The visualization layer implements two complementary design patterns:
Strategy Pattern (
IVisualizerhierarchy): Visualizers own complete subplots and encapsulate the drawing logic for a specific data type. Backend selection determines which drawing strategy is invoked through the template method pattern.Component Pattern (
IVisualComponent): Components add discrete visual elements (change-point markers, period fills, annotation lines) to existing subplots. Multiple components can be composed on the same subplot.
Separation of concerns is strict: visualizers configure axes properties and draw primary data content; components draw supplementary annotations; a coordinator (outside this subpackage) creates the figure layout and orchestrates drawing order.
Public API
IVisualizer: Base interface for all visualizers that manage complete subplots. Declaresaxes,backend, and backend-specificdrawmethods.ITimeseriesVisualizer: Interface for time series visualizers. Generic overDataProvider; requires subclasses to implementset_data_provider.ITraceVisualizer: Interface for detection trace visualizers. Generic overDetectionTrace; requires subclasses to implementset_trace.IMetricVisualizer: Base class for benchmark metric visualizers. Manages benchmark tables and per-entry line styling.IVisualComponent: Interface for composable drawing components. Renders specific annotation elements onto a single subplot with optional legend entries.
See each class’s own docstring for full parameter and method details.
Backend Abstraction
All visualizers and components implement both _draw_matplotlib() and
_draw_plotly() methods. The public draw() method dispatches to the
appropriate backend-specific implementation based on the backend property.
Backends are specified via the DrawBackend enum (MATPLOTLIB or
PLOTLY).
Legend Management
Components store legend state in their shared visual specs (label,
legend, legend_group). When add_legend=True is passed to
draw(), the component may add its legend entry if the element spec also
enables legend. For Plotly, legend entries are grouped by effective legend
group, defaulting to the element label.
Examples
Creating a concrete visualizer by subclassing IVisualizer:
>>> import matplotlib.pyplot as plt
>>> from pysatl_cpd.analysis.visualization.abstracts import IVisualizer
>>> from pysatl_cpd.analysis.visualization.typedefs import (
... DrawBackend,
... GoAxMapping,
... GoFigure,
... PltAxMapping,
... PltFigure,
... )
>>> class SimpleScatterVisualizer(IVisualizer):
... def __init__(self, backend: DrawBackend = DrawBackend.MATPLOTLIB):
... super().__init__(backend)
... self._x: list[float] = []
... self._y: list[float] = []
... @property
... def axes(self) -> set[str]:
... return {"scatter"}
... def set_data(self, x: list[float], y: list[float]) -> None:
... self._x = x
... self._y = y
... def _draw_matplotlib(self, figure: PltFigure, axes: PltAxMapping) -> PltFigure:
... ax = axes["scatter"]
... ax.scatter(self._x, self._y)
... return figure
... def _draw_plotly(self, figure: GoFigure, axes: GoAxMapping) -> GoFigure:
... import plotly.graph_objects as go
... row, col = axes["scatter"]
... figure.add_trace(go.Scatter(x=self._x, y=self._y, mode="markers"), row=row, col=col)
... return figure
>>> viz = SimpleScatterVisualizer(DrawBackend.MATPLOTLIB)
>>> viz.set_data([1.0, 2.0, 3.0], [4.0, 5.0, 6.0])
>>> print(viz.axes)
{'scatter'}
Creating a concrete component by subclassing IVisualComponent:
>>> import matplotlib.pyplot as plt
>>> from pysatl_cpd.analysis.visualization.abstracts import IVisualComponent
>>> from pysatl_cpd.analysis.visualization.typedefs import (
... DrawBackend,
... GoAxes,
... GoFigure,
... PltAxes,
... PltFigure,
... )
>>> class HorizontalLineComponent(IVisualComponent):
... def __init__(self, backend: DrawBackend = DrawBackend.MATPLOTLIB):
... super().__init__(backend)
... self._y_value: float = 0.0
... def set_y(self, y: float) -> None:
... self._y_value = y
... def _draw_matplotlib(self, figure: PltFigure, axes: PltAxes, add_legend: bool = False) -> None:
... axes.axhline(self._y_value, color="red", linestyle="--")
... def _draw_plotly(self, figure: GoFigure, axes: GoAxes, add_legend: bool = False) -> None:
... row, col = axes
... figure.add_hline(y=self._y_value, line_color="red", line_dash="dash", row=row, col=col)
>>> comp = HorizontalLineComponent(DrawBackend.MATPLOTLIB)
>>> comp.set_y(0.5)
>>> fig, ax = plt.subplots()
>>> comp.draw(fig, ax)
>>> plt.close(fig)
Switching backends on an existing visualizer or component:
>>> from pysatl_cpd.analysis.visualization.abstracts import IVisualComponent
>>> from pysatl_cpd.analysis.visualization.typedefs import DrawBackend
>>> comp = IVisualComponent.__new__(IVisualComponent)
>>> # Concrete components expose a backend property that can be reassigned:
>>> # comp.backend = DrawBackend.PLOTLY
Notes
All classes in this subpackage are abstract. They cannot be instantiated directly; concrete subclasses must implement the required
_draw_matplotliband_draw_plotlymethods.Type parameters on
ITimeseriesVisualizerandITraceVisualizeruse Python 3.12+ PEP 695 syntax. The module requires Python 3.12 or later.Matplotlib is required for the
MATPLOTLIBbackend; plotly is required for thePLOTLYbackend. Both are listed as optional dependencies inpyproject.toml.Change-point indices are zero-based throughout the visualization system.