polymorphic-method-dispatch-on-plot-kind
Summary
A class hierarchy and dispatch mechanism that routes plot kind arguments (spectrum, chromatogram, mobilogram, peakmap) to concrete plot subclasses, each implementing backend-specific rendering for mass spectrometry data visualization. This enables consistent Pandas-style plotting API across matplotlib (static) and bokeh/plotly (interactive) backends.
When to use
When building a plotting library that must support multiple visualization types (1D spectra, chromatograms, mobilograms, 2D peak maps) across heterogeneous rendering backends, and you want users to specify plot type via a single kind parameter rather than importing backend-specific classes directly. Specifically useful when integrating with Pandas DataFrame.plot() accessor pattern and mass spectrometry data requires MS-specific validation and axis semantics.
When NOT to use
- When the user has already selected and imported a specific backend plotting class directly (e.g., instantiated BOKEHSpectrumPlot directly) — dispatch adds no value in this case.
- When input data does not have the required x, y (or x, y, z for peakmap) columns, or when columns are not mass spectrometry-specific — validation should fail before dispatch.
- When plot kind is not one of the four supported types (spectrum, chromatogram, mobilogram, peakmap) — kind dispatcher should raise ValueError rather than attempting polymorphic dispatch.
Inputs
- Pandas DataFrame with mass spectrometry columns (m/z, intensity, retention time, mobility)
- kind parameter (string: 'spectrum', 'chromatogram', 'mobilogram', 'peakmap')
- backend parameter (string: 'matplotlib', 'bokeh', 'plotly')
- x, y, z column names (strings)
- configuration object (ChromatogramConfig, SpectrumConfig, PeakMapConfig, MobilogramConfig)
Outputs
- Plot object (matplotlib Figure, bokeh Figure, or plotly Figure)
- Rendered visualization displayed in notebook or saved to file
- Backend-specific plot instance with generate() method called
How to apply
Establish a two-tier abstract class hierarchy: (1) BasePlot with common configuration and initialization; (2) BaseMSPlot inheriting from BasePlot to add MS-specific validation for plot kinds and axis requirements. Implement concrete plot subclasses (ChromatogramPlot, SpectrumPlot, PeakMapPlot, MobilogramPlot) inheriting from BaseMSPlot, each with a generate() method accepting x, y, z axes and a kind-specific config object (ChromatogramConfig, SpectrumConfig, etc.). Create backend-specific abstract bases (BOKEHPlot, PLOTLYPlot, MATPLOTLIBPlot) and concrete implementations (BOKEHSpectrumPlot, PLOTLYSpectrumPlot) that override generate() to call backend-specific rendering. Implement a kind-dispatch mechanism in the Pandas plotting backend accessor that maps kind='spectrum'|'chromatogram'|'mobilogram'|'peakmap' to the appropriate concrete plot class constructor, with backend selection passed separately. The dispatcher should validate that required axes (x, y for 1D plots; x, y, z for peakmap 2D) are present before instantiation.
Related tools
- Pandas (DataFrame accessor integration point for kind dispatch and column selection)
- matplotlib (Static rendering backend; concrete MATPLOTLIBPlot subclasses call matplotlib rendering methods)
- bokeh (Interactive rendering backend; concrete BOKEHPlot subclasses call bokeh rendering methods)
- plotly (Interactive rendering backend; concrete PLOTLYPlot subclasses call plotly rendering methods)
- pyOpenMS-viz (Reference implementation of polymorphic dispatch on kind argument across backends) — https://github.com/OpenMS/pyopenms_viz
Examples
ms_data.plot(x='m/z', y='intensity', kind='spectrum', backend='bokeh')
Evaluation signals
- Verify that calling df.plot(x='m/z', y='intensity', kind='spectrum', backend='bokeh') returns a bokeh Figure (not matplotlib), confirming backend dispatch worked.
- Verify that calling df.plot(..., kind='peakmap') with only x and y columns (no z) raises ValueError with clear MS-specific validation message before instantiation.
- Verify that identical kind and column arguments produce visually equivalent plots across backends (matplotlib, bokeh, plotly) by comparing axis ranges, title, and data point counts.
- Verify that the appropriate concrete subclass (ChromatogramPlot, SpectrumPlot, etc.) is instantiated by inspecting type(plot_obj).name or using isinstance() checks.
- Verify that configuration objects are passed to and respected by the chosen backend class (e.g., SpectrumConfig.mz_range constrains x-axis in all three backends).
Limitations
- PeakMap 3D visualization is only supported on matplotlib and plotly; bokeh does not support 3D rendering, so kind='peakmap' with plot3d=True will raise NotImplementedError for bokeh backend.
- Column selection is versatile but requires exact string matching; if user specifies x='m/z_ppm' but DataFrame column is 'm/z', dispatch will fail silently or raise KeyError.
- Dispatch mechanism requires backend parameter to be passed explicitly or inferred from environment; if neither is available, the dispatcher has no deterministic way to select a backend.
- MS-specific validation (e.g., m/z ranges, intensity positivity) is enforced at BaseMSPlot level, so non-MS data may pass dispatch but fail at render time with cryptic backend-specific errors.
Evidence
- [other] Define BaseMSPlot as an intermediate abstract class inheriting from BasePlot to add mass-spectrometry-specific methods and validation for chromatogram, mobilogram, spectrum, and peakmap plot kinds.: "Define BaseMSPlot as an intermediate abstract class inheriting from BasePlot to add mass-spectrometry-specific methods and validation for chromatogram, mobilogram, spectrum, and peakmap plot kinds."
- [other] Implement a kind-dispatch mechanism in the pandas plotting backend accessor that routes kind='spectrum', 'chromatogram', 'mobilogram', 'peakmap' to the appropriate concrete plot class constructor based on backend selection.: "Implement a kind-dispatch mechanism in the pandas plotting backend accessor that routes kind='spectrum', 'chromatogram', 'mobilogram', 'peakmap' to the appropriate concrete plot class constructor"
- [other] Implement backend-specific plot classes (BOKEHPlot, PLOTLYPlot, MATPLOTLIBPlot as abstract bases; BOKEHLinePlot, BOKEHSpectrumPlot, etc. as concrete implementations) that override generate() to call backend-specific rendering methods.: "Implement backend-specific plot classes (BOKEHPlot, PLOTLYPlot, MATPLOTLIBPlot as abstract bases; BOKEHLinePlot, BOKEHSpectrumPlot, etc. as concrete implementations) that override generate() to call"
- [readme] Support for multiple plotting backends: matplotlib (static), bokeh and plotly (interactive): "Support for multiple plotting backends: matplotlib (static), bokeh and plotly (interactive)"
- [readme] Flexible plotting API that interfaces directly with Pandas DataFrames: "Flexible plotting API that interfaces directly with Pandas DataFrames"
- [readme] Consistent API across different plotting backends for easy switching between static and interactive plots: "Consistent API across different plotting backends for easy switching between static and interactive plots"
1---2name: polymorphic-method-dispatch-on-plot-kind3description: Use when when building a plotting library that must support multiple visualization types (1D spectra, chromatograms, mobilograms, 2D peak maps) across heterogeneous rendering backends, and you want users to specify plot type via a single kind parameter rather than importing backend-specific classes.4license: CC-BY-4.05---67# polymorphic-method-dispatch-on-plot-kind89## Summary1011A class hierarchy and dispatch mechanism that routes plot kind arguments (spectrum, chromatogram, mobilogram, peakmap) to concrete plot subclasses, each implementing backend-specific rendering for mass spectrometry data visualization. This enables consistent Pandas-style plotting API across matplotlib (static) and bokeh/plotly (interactive) backends.1213## When to use1415When building a plotting library that must support multiple visualization types (1D spectra, chromatograms, mobilograms, 2D peak maps) across heterogeneous rendering backends, and you want users to specify plot type via a single kind parameter rather than importing backend-specific classes directly. Specifically useful when integrating with Pandas DataFrame.plot() accessor pattern and mass spectrometry data requires MS-specific validation and axis semantics.1617## When NOT to use1819- When the user has already selected and imported a specific backend plotting class directly (e.g., instantiated BOKEHSpectrumPlot directly) — dispatch adds no value in this case.20- When input data does not have the required x, y (or x, y, z for peakmap) columns, or when columns are not mass spectrometry-specific — validation should fail before dispatch.21- When plot kind is not one of the four supported types (spectrum, chromatogram, mobilogram, peakmap) — kind dispatcher should raise ValueError rather than attempting polymorphic dispatch.2223## Inputs2425- Pandas DataFrame with mass spectrometry columns (m/z, intensity, retention time, mobility)26- kind parameter (string: 'spectrum', 'chromatogram', 'mobilogram', 'peakmap')27- backend parameter (string: 'matplotlib', 'bokeh', 'plotly')28- x, y, z column names (strings)29- configuration object (ChromatogramConfig, SpectrumConfig, PeakMapConfig, MobilogramConfig)3031## Outputs3233- Plot object (matplotlib Figure, bokeh Figure, or plotly Figure)34- Rendered visualization displayed in notebook or saved to file35- Backend-specific plot instance with generate() method called3637## How to apply3839Establish a two-tier abstract class hierarchy: (1) BasePlot with common configuration and initialization; (2) BaseMSPlot inheriting from BasePlot to add MS-specific validation for plot kinds and axis requirements. Implement concrete plot subclasses (ChromatogramPlot, SpectrumPlot, PeakMapPlot, MobilogramPlot) inheriting from BaseMSPlot, each with a generate() method accepting x, y, z axes and a kind-specific config object (ChromatogramConfig, SpectrumConfig, etc.). Create backend-specific abstract bases (BOKEHPlot, PLOTLYPlot, MATPLOTLIBPlot) and concrete implementations (BOKEHSpectrumPlot, PLOTLYSpectrumPlot) that override generate() to call backend-specific rendering. Implement a kind-dispatch mechanism in the Pandas plotting backend accessor that maps kind='spectrum'|'chromatogram'|'mobilogram'|'peakmap' to the appropriate concrete plot class constructor, with backend selection passed separately. The dispatcher should validate that required axes (x, y for 1D plots; x, y, z for peakmap 2D) are present before instantiation.4041## Related tools4243- **Pandas** (DataFrame accessor integration point for kind dispatch and column selection)44- **matplotlib** (Static rendering backend; concrete MATPLOTLIBPlot subclasses call matplotlib rendering methods)45- **bokeh** (Interactive rendering backend; concrete BOKEHPlot subclasses call bokeh rendering methods)46- **plotly** (Interactive rendering backend; concrete PLOTLYPlot subclasses call plotly rendering methods)47- **pyOpenMS-viz** (Reference implementation of polymorphic dispatch on kind argument across backends) — https://github.com/OpenMS/pyopenms_viz4849## Examples5051```52ms_data.plot(x='m/z', y='intensity', kind='spectrum', backend='bokeh')53```5455## Evaluation signals5657- Verify that calling df.plot(x='m/z', y='intensity', kind='spectrum', backend='bokeh') returns a bokeh Figure (not matplotlib), confirming backend dispatch worked.58- Verify that calling df.plot(..., kind='peakmap') with only x and y columns (no z) raises ValueError with clear MS-specific validation message before instantiation.59- Verify that identical kind and column arguments produce visually equivalent plots across backends (matplotlib, bokeh, plotly) by comparing axis ranges, title, and data point counts.60- Verify that the appropriate concrete subclass (ChromatogramPlot, SpectrumPlot, etc.) is instantiated by inspecting type(plot_obj).__name__ or using isinstance() checks.61- Verify that configuration objects are passed to and respected by the chosen backend class (e.g., SpectrumConfig.mz_range constrains x-axis in all three backends).6263## Limitations6465- PeakMap 3D visualization is only supported on matplotlib and plotly; bokeh does not support 3D rendering, so kind='peakmap' with plot3d=True will raise NotImplementedError for bokeh backend.66- Column selection is versatile but requires exact string matching; if user specifies x='m/z_ppm' but DataFrame column is 'm/z', dispatch will fail silently or raise KeyError.67- Dispatch mechanism requires backend parameter to be passed explicitly or inferred from environment; if neither is available, the dispatcher has no deterministic way to select a backend.68- MS-specific validation (e.g., m/z ranges, intensity positivity) is enforced at BaseMSPlot level, so non-MS data may pass dispatch but fail at render time with cryptic backend-specific errors.6970## Evidence7172- [other] Define BaseMSPlot as an intermediate abstract class inheriting from BasePlot to add mass-spectrometry-specific methods and validation for chromatogram, mobilogram, spectrum, and peakmap plot kinds.: "Define BaseMSPlot as an intermediate abstract class inheriting from BasePlot to add mass-spectrometry-specific methods and validation for chromatogram, mobilogram, spectrum, and peakmap plot kinds."73- [other] Implement a kind-dispatch mechanism in the pandas plotting backend accessor that routes kind='spectrum', 'chromatogram', 'mobilogram', 'peakmap' to the appropriate concrete plot class constructor based on backend selection.: "Implement a kind-dispatch mechanism in the pandas plotting backend accessor that routes kind='spectrum', 'chromatogram', 'mobilogram', 'peakmap' to the appropriate concrete plot class constructor"74- [other] Implement backend-specific plot classes (BOKEHPlot, PLOTLYPlot, MATPLOTLIBPlot as abstract bases; BOKEHLinePlot, BOKEHSpectrumPlot, etc. as concrete implementations) that override generate() to call backend-specific rendering methods.: "Implement backend-specific plot classes (BOKEHPlot, PLOTLYPlot, MATPLOTLIBPlot as abstract bases; BOKEHLinePlot, BOKEHSpectrumPlot, etc. as concrete implementations) that override generate() to call"75- [readme] Support for multiple plotting backends: matplotlib (static), bokeh and plotly (interactive): "Support for multiple plotting backends: matplotlib (static), bokeh and plotly (interactive)"76- [readme] Flexible plotting API that interfaces directly with Pandas DataFrames: "Flexible plotting API that interfaces directly with Pandas DataFrames"77- [readme] Consistent API across different plotting backends for easy switching between static and interactive plots: "Consistent API across different plotting backends for easy switching between static and interactive plots"