From 864fb5c66310d2e97ed8d50a2b0ed5959a83993e Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Fri, 13 Mar 2026 15:07:05 +0530 Subject: [PATCH 1/3] feat: register ms_plot namespace for pandas DataFrames --- pyopenms_viz/__init__.py | 1 + pyopenms_viz/_pandas_accessor.py | 15 + .../test_pandas_ms_plot_snapshot.json | 1090 +++++++++++++++++ test/test_pandas_accessor.py | 42 + 4 files changed, 1148 insertions(+) create mode 100644 pyopenms_viz/_pandas_accessor.py create mode 100644 test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot.json create mode 100644 test/test_pandas_accessor.py diff --git a/pyopenms_viz/__init__.py b/pyopenms_viz/__init__.py index 7d24a4725..0a097a770 100644 --- a/pyopenms_viz/__init__.py +++ b/pyopenms_viz/__init__.py @@ -10,6 +10,7 @@ import types from pathlib import Path from ._core import plot_chromatogram, plot_spectrum, plot_mobilogram, plot_peakmap +from . import _pandas_accessor # Import for side effects: registers Pandas 'ms_plot' namespace. noqa: F401 __version__ = "1.0.1" diff --git a/pyopenms_viz/_pandas_accessor.py b/pyopenms_viz/_pandas_accessor.py new file mode 100644 index 000000000..5316c4a30 --- /dev/null +++ b/pyopenms_viz/_pandas_accessor.py @@ -0,0 +1,15 @@ +import pandas as pd + +@pd.api.extensions.register_dataframe_accessor("ms_plot") +class PandasPlotAccessor: + """ + Registers a custom '.ms_plot' namespace for Pandas DataFrames. + This provides a unified API syntax alongside the Polars integration. + """ + def __init__(self, pandas_obj): + self._df = pandas_obj + + def __call__(self, *args, **kwargs): + # Delegate directly to the native pandas plot method. + # This ensures all existing ms_* backends and entry points work perfectly. + return self._df.plot(*args, **kwargs) \ No newline at end of file diff --git a/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot.json b/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot.json new file mode 100644 index 000000000..5408197e5 --- /dev/null +++ b/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot.json @@ -0,0 +1,1090 @@ +{ + "data": [ + { + "line": { + "color": "#4575B4" + }, + "mode": "lines", + "x": [ + 100.1, + 100.1, + 100.1, + 100.2, + 100.2, + 100.2, + 105.0, + 105.0, + 105.0, + 105.1, + 105.1, + 105.1 + ], + "y": [ + 0.0, + 10.0, + 0.0, + 0.0, + 50.0, + 0.0, + 0.0, + 100.0, + 0.0, + 0.0, + 90.0, + 0.0 + ], + "type": "scatter", + "customdata": [ + [ + 100.1, + 10.0 + ], + [ + 100.1, + 10.0 + ], + [ + 100.1, + 10.0 + ], + [ + 100.2, + 50.0 + ], + [ + 100.2, + 50.0 + ], + [ + 100.2, + 50.0 + ], + [ + 105.0, + 100.0 + ], + [ + 105.0, + 100.0 + ], + [ + 105.0, + 100.0 + ], + [ + 105.1, + 90.0 + ], + [ + 105.1, + 90.0 + ], + [ + 105.1, + 90.0 + ] + ], + "hovertemplate": "m\u002fz: %{customdata[0]}\u003cbr\u003eintensity: %{customdata[1]}" + } + ], + "layout": { + "template": { + "data": { + "barpolar": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "bar": [ + { + "error_x": { + "color": "rgb(36,36,36)" + }, + "error_y": { + "color": "rgb(36,36,36)" + }, + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "rgb(36,36,36)", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "rgb(36,36,36)" + }, + "baxis": { + "endlinecolor": "rgb(36,36,36)", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "rgb(36,36,36)" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "type": "choropleth" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "type": "contourcarpet" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "contour" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "heatmapgl" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "heatmap" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "histogram2dcontour" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "histogram2d" + } + ], + "histogram": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.6 + } + }, + "type": "histogram" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scatterpolargl" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scatterpolar" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "rgb(237,237,237)" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "rgb(217,217,217)" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "colorscale": { + "diverging": [ + [ + 0.0, + "rgb(103,0,31)" + ], + [ + 0.1, + "rgb(178,24,43)" + ], + [ + 0.2, + "rgb(214,96,77)" + ], + [ + 0.3, + "rgb(244,165,130)" + ], + [ + 0.4, + "rgb(253,219,199)" + ], + [ + 0.5, + "rgb(247,247,247)" + ], + [ + 0.6, + "rgb(209,229,240)" + ], + [ + 0.7, + "rgb(146,197,222)" + ], + [ + 0.8, + "rgb(67,147,195)" + ], + [ + 0.9, + "rgb(33,102,172)" + ], + [ + 1.0, + "rgb(5,48,97)" + ] + ], + "sequential": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ] + }, + "colorway": [ + "#1F77B4", + "#FF7F0E", + "#2CA02C", + "#D62728", + "#9467BD", + "#8C564B", + "#E377C2", + "#7F7F7F", + "#BCBD22", + "#17BECF" + ], + "font": { + "color": "rgb(36,36,36)" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "white", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "polar": { + "angularaxis": { + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside" + }, + "bgcolor": "white", + "radialaxis": { + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "white", + "gridcolor": "rgb(232,232,232)", + "gridwidth": 2, + "linecolor": "rgb(36,36,36)", + "showbackground": true, + "showgrid": false, + "showline": true, + "ticks": "outside", + "zeroline": false, + "zerolinecolor": "rgb(36,36,36)" + }, + "yaxis": { + "backgroundcolor": "white", + "gridcolor": "rgb(232,232,232)", + "gridwidth": 2, + "linecolor": "rgb(36,36,36)", + "showbackground": true, + "showgrid": false, + "showline": true, + "ticks": "outside", + "zeroline": false, + "zerolinecolor": "rgb(36,36,36)" + }, + "zaxis": { + "backgroundcolor": "white", + "gridcolor": "rgb(232,232,232)", + "gridwidth": 2, + "linecolor": "rgb(36,36,36)", + "showbackground": true, + "showgrid": false, + "showline": true, + "ticks": "outside", + "zeroline": false, + "zerolinecolor": "rgb(36,36,36)" + } + }, + "shapedefaults": { + "fillcolor": "black", + "line": { + "width": 0 + }, + "opacity": 0.3 + }, + "ternary": { + "aaxis": { + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside" + }, + "baxis": { + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside" + }, + "bgcolor": "white", + "caxis": { + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside", + "title": { + "standoff": 15 + }, + "zeroline": false, + "zerolinecolor": "rgb(36,36,36)" + }, + "yaxis": { + "automargin": true, + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside", + "title": { + "standoff": 15 + }, + "zeroline": false, + "zerolinecolor": "rgb(36,36,36)" + } + } + }, + "title": { + "text": "Mass Spectrum", + "font": { + "family": "Helvetica", + "size": 18 + } + }, + "xaxis": { + "title": { + "text": "mass-to-charge", + "font": { + "family": "Helvetica", + "size": 16 + } + }, + "tickfont": { + "size": 14, + "family": "Helvetica" + }, + "gridcolor": "#CCCCCC", + "showgrid": true, + "showline": true, + "linewidth": 1, + "linecolor": "black", + "ticks": "outside", + "tickwidth": 1, + "tickcolor": "black", + "range": [ + 80.08, + 126.11999999999999 + ] + }, + "yaxis": { + "title": { + "text": "Intensity", + "font": { + "family": "Helvetica", + "size": 16 + } + }, + "tickfont": { + "size": 14, + "family": "Helvetica" + }, + "gridcolor": "#CCCCCC", + "showgrid": true, + "showline": true, + "linewidth": 1, + "linecolor": "black", + "tickwidth": 1, + "tickcolor": "black", + "range": [ + 0, + 115.0 + ] + }, + "width": 500, + "height": 500, + "dragmode": "select", + "legend": { + "font": { + "size": 10, + "family": "Helvetica" + }, + "title": { + "text": "Trace" + } + }, + "showlegend": true, + "font": { + "family": "Helvetica" + }, + "plot_bgcolor": "#FFFFFF", + "annotations": [ + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "105.0", + "x": 105.0, + "xanchor": "left", + "y": 100.0 + }, + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "105.1", + "x": 105.1, + "xanchor": "left", + "y": 90.0 + }, + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "100.2", + "x": 100.2, + "xanchor": "left", + "y": 50.0 + }, + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "100.1", + "x": 100.1, + "xanchor": "left", + "y": 10.0 + } + ], + "shapes": [ + { + "line": { + "color": "#EEEEEE", + "width": 2 + }, + "opacity": 1, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 0, + "y1": 0, + "yref": "y" + } + ] + } +} \ No newline at end of file diff --git a/test/test_pandas_accessor.py b/test/test_pandas_accessor.py new file mode 100644 index 000000000..376140683 --- /dev/null +++ b/test/test_pandas_accessor.py @@ -0,0 +1,42 @@ +import pandas as pd +import pytest +import pyopenms_viz + +@pytest.fixture(autouse=True) +def load_backend(): + """ + Safely override the global backend so the syrupy snapshot fixture + in conftest.py knows which extension to load, then reset it. + """ + original_backend = pd.options.plotting.backend + pd.options.plotting.backend = "ms_plotly" + + yield + + # Teardown: restore the original state + pd.options.plotting.backend = original_backend + + +def test_pandas_ms_plot_snapshot(snapshot): + """ + Test that the new Pandas ms_plot() accessor properly delegates + to the plotting backend and generates a stable Plotly figure. + """ + data = { + "m/z": [100.1, 100.2, 105.0, 105.1], + "intensity": [10.0, 50.0, 100.0, 90.0] + } + + df_pd = pd.DataFrame(data) + + # Generate the figure using the NEW Pandas ms_plot accessor! + out = df_pd.ms_plot( + kind="spectrum", + x="m/z", + y="intensity", + backend="ms_plotly", + show_plot=False + ) + + # Let syrupy handle the serialization and comparison + assert snapshot == out \ No newline at end of file From 6bc26d46fd8aa2a8b0ae2abf299d3015f3dce88c Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Fri, 13 Mar 2026 15:25:00 +0530 Subject: [PATCH 2/3] test: address bot feedback regarding fixture shadowing and linting --- ...est_pandas_ms_plot_snapshot[ms_bokeh].html | 61 + ...pandas_ms_plot_snapshot[ms_matplotlib].png | Bin 0 -> 20815 bytes ...st_pandas_ms_plot_snapshot[ms_plotly].json | 1090 +++++++++++++++++ test/test_pandas_accessor.py | 24 +- 4 files changed, 1158 insertions(+), 17 deletions(-) create mode 100644 test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_bokeh].html create mode 100644 test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_matplotlib].png create mode 100644 test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_plotly].json diff --git a/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_bokeh].html b/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_bokeh].html new file mode 100644 index 000000000..d53f94d71 --- /dev/null +++ b/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_bokeh].html @@ -0,0 +1,61 @@ + + + + + Bokeh Application + + + + + +
+ + + + + \ No newline at end of file diff --git a/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_matplotlib].png b/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_matplotlib].png new file mode 100644 index 0000000000000000000000000000000000000000..8add60309d908ab749453dd17828ab5b28251822 GIT binary patch literal 20815 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 Date: Sun, 15 Mar 2026 20:48:51 +0530 Subject: [PATCH 3/3] fix: address CodeRabbit linting and Plotly snapshot resilience --- pyopenms_viz/__init__.py | 2 +- ...st_pandas_ms_plot_snapshot[ms_plotly].json | 1245 ++++++++--------- test/test_pandas_accessor.py | 18 +- 3 files changed, 615 insertions(+), 650 deletions(-) diff --git a/pyopenms_viz/__init__.py b/pyopenms_viz/__init__.py index 0a097a770..d11a5931d 100644 --- a/pyopenms_viz/__init__.py +++ b/pyopenms_viz/__init__.py @@ -10,7 +10,7 @@ import types from pathlib import Path from ._core import plot_chromatogram, plot_spectrum, plot_mobilogram, plot_peakmap -from . import _pandas_accessor # Import for side effects: registers Pandas 'ms_plot' namespace. noqa: F401 +from . import _pandas_accessor # noqa: F401 __version__ = "1.0.1" diff --git a/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_plotly].json b/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_plotly].json index 5408197e5..3dbe1e2e9 100644 --- a/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_plotly].json +++ b/test/__snapshots__/test_pandas_accessor/test_pandas_ms_plot_snapshot[ms_plotly].json @@ -1,39 +1,6 @@ { "data": [ { - "line": { - "color": "#4575B4" - }, - "mode": "lines", - "x": [ - 100.1, - 100.1, - 100.1, - 100.2, - 100.2, - 100.2, - 105.0, - 105.0, - 105.0, - 105.1, - 105.1, - 105.1 - ], - "y": [ - 0.0, - 10.0, - 0.0, - 0.0, - 50.0, - 0.0, - 0.0, - 100.0, - 0.0, - 0.0, - 90.0, - 0.0 - ], - "type": "scatter", "customdata": [ [ 100.1, @@ -84,558 +51,690 @@ 90.0 ] ], - "hovertemplate": "m\u002fz: %{customdata[0]}\u003cbr\u003eintensity: %{customdata[1]}" + "hovertemplate": "m\u002fz: %{customdata[0]}\u003cbr\u003eintensity: %{customdata[1]}", + "line": { + "color": "#4575B4" + }, + "mode": "lines", + "x": [ + 100.1, + 100.1, + 100.1, + 100.2, + 100.2, + 100.2, + 105.0, + 105.0, + 105.0, + 105.1, + 105.1, + 105.1 + ], + "y": [ + 0.0, + 10.0, + 0.0, + 0.0, + 50.0, + 0.0, + 0.0, + 100.0, + 0.0, + 0.0, + 90.0, + 0.0 + ], + "type": "scatter" } ], "layout": { + "annotations": [ + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "105.0", + "x": 105.0, + "xanchor": "left", + "y": 100.0 + }, + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "105.1", + "x": 105.1, + "xanchor": "left", + "y": 90.0 + }, + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "100.2", + "x": 100.2, + "xanchor": "left", + "y": 50.0 + }, + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "100.1", + "x": 100.1, + "xanchor": "left", + "y": 10.0 + } + ], + "dragmode": "select", + "font": { + "family": "Helvetica" + }, + "height": 500, + "legend": { + "font": { + "family": "Helvetica", + "size": 10 + }, + "title": { + "text": "Trace" + } + }, + "plot_bgcolor": "#FFFFFF", + "shapes": [ + { + "line": { + "color": "#EEEEEE", + "width": 2 + }, + "opacity": 1, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 0, + "y1": 0, + "yref": "y" + } + ], + "showlegend": true, + "title": { + "font": { + "family": "Helvetica", + "size": 18 + }, + "text": "Mass Spectrum" + }, + "width": 500, + "xaxis": { + "gridcolor": "#CCCCCC", + "linecolor": "black", + "linewidth": 1, + "range": [ + 80.08, + 126.11999999999999 + ], + "showgrid": true, + "showline": true, + "tickcolor": "black", + "tickfont": { + "family": "Helvetica", + "size": 14 + }, + "ticks": "outside", + "tickwidth": 1, + "title": { + "font": { + "family": "Helvetica", + "size": 16 + }, + "text": "mass-to-charge" + } + }, + "yaxis": { + "gridcolor": "#CCCCCC", + "linecolor": "black", + "linewidth": 1, + "range": [ + 0, + 115.0 + ], + "showgrid": true, + "showline": true, + "tickcolor": "black", + "tickfont": { + "family": "Helvetica", + "size": 14 + }, + "tickwidth": 1, + "title": { + "font": { + "family": "Helvetica", + "size": 16 + }, + "text": "Intensity" + } + }, "template": { "data": { - "barpolar": [ - { - "marker": { - "line": { - "color": "white", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "bar": [ - { - "error_x": { - "color": "rgb(36,36,36)" - }, - "error_y": { - "color": "rgb(36,36,36)" - }, - "marker": { - "line": { - "color": "white", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "rgb(36,36,36)", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "rgb(36,36,36)" - }, - "baxis": { - "endlinecolor": "rgb(36,36,36)", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "rgb(36,36,36)" - }, - "type": "carpet" - } - ], - "choropleth": [ + "histogram2dcontour": [ { + "type": "histogram2dcontour", "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" }, - "type": "choropleth" + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] } ], - "contourcarpet": [ + "choropleth": [ { + "type": "choropleth", "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" - }, - "type": "contourcarpet" + "outlinewidth": 0, + "ticks": "" + } } ], - "contour": [ + "histogram2d": [ { + "type": "histogram2d", "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" }, "colorscale": [ [ 0.0, - "#440154" + "#0d0887" ], [ 0.1111111111111111, - "#482878" + "#46039f" ], [ 0.2222222222222222, - "#3e4989" + "#7201a8" ], [ 0.3333333333333333, - "#31688e" + "#9c179e" ], [ 0.4444444444444444, - "#26828e" + "#bd3786" ], [ 0.5555555555555556, - "#1f9e89" + "#d8576b" ], [ 0.6666666666666666, - "#35b779" + "#ed7953" ], [ 0.7777777777777778, - "#6ece58" + "#fb9f3a" ], [ 0.8888888888888888, - "#b5de2b" + "#fdca26" ], [ 1.0, - "#fde725" + "#f0f921" ] - ], - "type": "contour" + ] } ], - "heatmapgl": [ + "heatmap": [ { + "type": "heatmap", "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" }, "colorscale": [ [ 0.0, - "#440154" + "#0d0887" ], [ 0.1111111111111111, - "#482878" + "#46039f" ], [ 0.2222222222222222, - "#3e4989" + "#7201a8" ], [ 0.3333333333333333, - "#31688e" + "#9c179e" ], [ 0.4444444444444444, - "#26828e" + "#bd3786" ], [ 0.5555555555555556, - "#1f9e89" + "#d8576b" ], [ 0.6666666666666666, - "#35b779" + "#ed7953" ], [ 0.7777777777777778, - "#6ece58" + "#fb9f3a" ], [ 0.8888888888888888, - "#b5de2b" + "#fdca26" ], [ 1.0, - "#fde725" + "#f0f921" ] - ], - "type": "heatmapgl" + ] } ], - "heatmap": [ + "heatmapgl": [ { + "type": "heatmapgl", "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" }, "colorscale": [ [ 0.0, - "#440154" + "#0d0887" ], [ 0.1111111111111111, - "#482878" + "#46039f" ], [ 0.2222222222222222, - "#3e4989" + "#7201a8" ], [ 0.3333333333333333, - "#31688e" + "#9c179e" ], [ 0.4444444444444444, - "#26828e" + "#bd3786" ], [ 0.5555555555555556, - "#1f9e89" + "#d8576b" ], [ 0.6666666666666666, - "#35b779" + "#ed7953" ], [ 0.7777777777777778, - "#6ece58" + "#fb9f3a" ], [ 0.8888888888888888, - "#b5de2b" + "#fdca26" ], [ 1.0, - "#fde725" + "#f0f921" ] - ], - "type": "heatmap" + ] } ], - "histogram2dcontour": [ + "contourcarpet": [ { + "type": "contourcarpet", "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" + } + } + ], + "contour": [ + { + "type": "contour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" }, "colorscale": [ [ 0.0, - "#440154" + "#0d0887" ], [ 0.1111111111111111, - "#482878" + "#46039f" ], [ 0.2222222222222222, - "#3e4989" + "#7201a8" ], [ 0.3333333333333333, - "#31688e" + "#9c179e" ], [ 0.4444444444444444, - "#26828e" + "#bd3786" ], [ 0.5555555555555556, - "#1f9e89" + "#d8576b" ], [ 0.6666666666666666, - "#35b779" + "#ed7953" ], [ 0.7777777777777778, - "#6ece58" + "#fb9f3a" ], [ 0.8888888888888888, - "#b5de2b" + "#fdca26" ], [ 1.0, - "#fde725" + "#f0f921" ] - ], - "type": "histogram2dcontour" + ] } ], - "histogram2d": [ + "surface": [ { + "type": "surface", "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" }, "colorscale": [ [ 0.0, - "#440154" + "#0d0887" ], [ 0.1111111111111111, - "#482878" + "#46039f" ], [ 0.2222222222222222, - "#3e4989" + "#7201a8" ], [ 0.3333333333333333, - "#31688e" + "#9c179e" ], [ 0.4444444444444444, - "#26828e" + "#bd3786" ], [ 0.5555555555555556, - "#1f9e89" + "#d8576b" ], [ 0.6666666666666666, - "#35b779" + "#ed7953" ], [ 0.7777777777777778, - "#6ece58" + "#fb9f3a" ], [ 0.8888888888888888, - "#b5de2b" + "#fdca26" ], [ 1.0, - "#fde725" + "#f0f921" ] - ], - "type": "histogram2d" + ] } ], - "histogram": [ + "mesh3d": [ { - "marker": { - "line": { - "color": "white", - "width": 0.6 - } - }, - "type": "histogram" + "type": "mesh3d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } } ], - "mesh3d": [ + "scatter": [ { - "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 }, - "type": "mesh3d" + "type": "scatter" } ], "parcoords": [ { + "type": "parcoords", "line": { "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" } - }, - "type": "parcoords" + } } ], - "pie": [ + "scatterpolargl": [ { - "automargin": true, - "type": "pie" + "type": "scatterpolargl", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } } ], - "scatter3d": [ + "bar": [ { - "line": { - "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" - } + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" }, "marker": { - "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 } }, - "type": "scatter3d" + "type": "bar" } ], - "scattercarpet": [ + "scattergeo": [ { + "type": "scattergeo", "marker": { "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" } - }, - "type": "scattercarpet" + } } ], - "scattergeo": [ + "scatterpolar": [ { + "type": "scatterpolar", "marker": { "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" } - }, - "type": "scattergeo" + } } ], - "scattergl": [ + "histogram": [ { "marker": { - "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 } }, - "type": "scattergl" + "type": "histogram" } ], - "scattermapbox": [ + "scattergl": [ { + "type": "scattergl", "marker": { "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" } - }, - "type": "scattermapbox" + } } ], - "scatterpolargl": [ + "scatter3d": [ { - "marker": { + "type": "scatter3d", + "line": { "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" } }, - "type": "scatterpolargl" + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } } ], - "scatterpolar": [ + "scattermapbox": [ { + "type": "scattermapbox", "marker": { "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" } - }, - "type": "scatterpolar" + } } ], - "scatter": [ + "scatterternary": [ { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" + "type": "scatterternary", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } } ], - "scatterternary": [ + "scattercarpet": [ { + "type": "scattercarpet", "marker": { "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" } - }, - "type": "scatterternary" + } } ], - "surface": [ + "carpet": [ { - "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" }, - "colorscale": [ - [ - 0.0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1.0, - "#fde725" - ] - ], - "type": "surface" + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" } ], "table": [ { "cells": { "fill": { - "color": "rgb(237,237,237)" + "color": "#EBF0F8" }, "line": { "color": "white" @@ -643,7 +742,7 @@ }, "header": { "fill": { - "color": "rgb(217,217,217)" + "color": "#C8D4E3" }, "line": { "color": "white" @@ -651,440 +750,298 @@ }, "type": "table" } - ] - }, - "layout": { - "annotationdefaults": { - "arrowhead": 0, - "arrowwidth": 1 + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ] + }, + "layout": { + "autotypenumbers": "strict", + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "hovermode": "closest", + "hoverlabel": { + "align": "left" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "bgcolor": "#E5ECF6", + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "ternary": { + "bgcolor": "#E5ECF6", + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } }, - "autotypenumbers": "strict", "coloraxis": { "colorbar": { - "outlinewidth": 1, - "tickcolor": "rgb(36,36,36)", - "ticks": "outside" + "outlinewidth": 0, + "ticks": "" } }, "colorscale": { - "diverging": [ + "sequential": [ [ 0.0, - "rgb(103,0,31)" + "#0d0887" ], [ - 0.1, - "rgb(178,24,43)" - ], - [ - 0.2, - "rgb(214,96,77)" + 0.1111111111111111, + "#46039f" ], [ - 0.3, - "rgb(244,165,130)" + 0.2222222222222222, + "#7201a8" ], [ - 0.4, - "rgb(253,219,199)" + 0.3333333333333333, + "#9c179e" ], [ - 0.5, - "rgb(247,247,247)" + 0.4444444444444444, + "#bd3786" ], [ - 0.6, - "rgb(209,229,240)" + 0.5555555555555556, + "#d8576b" ], [ - 0.7, - "rgb(146,197,222)" + 0.6666666666666666, + "#ed7953" ], [ - 0.8, - "rgb(67,147,195)" + 0.7777777777777778, + "#fb9f3a" ], [ - 0.9, - "rgb(33,102,172)" + 0.8888888888888888, + "#fdca26" ], [ 1.0, - "rgb(5,48,97)" + "#f0f921" ] ], - "sequential": [ + "sequentialminus": [ [ 0.0, - "#440154" + "#0d0887" ], [ 0.1111111111111111, - "#482878" + "#46039f" ], [ 0.2222222222222222, - "#3e4989" + "#7201a8" ], [ 0.3333333333333333, - "#31688e" + "#9c179e" ], [ 0.4444444444444444, - "#26828e" + "#bd3786" ], [ 0.5555555555555556, - "#1f9e89" + "#d8576b" ], [ 0.6666666666666666, - "#35b779" + "#ed7953" ], [ 0.7777777777777778, - "#6ece58" + "#fb9f3a" ], [ 0.8888888888888888, - "#b5de2b" + "#fdca26" ], [ 1.0, - "#fde725" + "#f0f921" ] ], - "sequentialminus": [ + "diverging": [ [ - 0.0, - "#440154" + 0, + "#8e0152" ], [ - 0.1111111111111111, - "#482878" + 0.1, + "#c51b7d" ], [ - 0.2222222222222222, - "#3e4989" + 0.2, + "#de77ae" ], [ - 0.3333333333333333, - "#31688e" + 0.3, + "#f1b6da" ], [ - 0.4444444444444444, - "#26828e" + 0.4, + "#fde0ef" ], [ - 0.5555555555555556, - "#1f9e89" + 0.5, + "#f7f7f7" ], [ - 0.6666666666666666, - "#35b779" + 0.6, + "#e6f5d0" ], [ - 0.7777777777777778, - "#6ece58" + 0.7, + "#b8e186" ], [ - 0.8888888888888888, - "#b5de2b" + 0.8, + "#7fbc41" ], [ - 1.0, - "#fde725" + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" ] ] }, - "colorway": [ - "#1F77B4", - "#FF7F0E", - "#2CA02C", - "#D62728", - "#9467BD", - "#8C564B", - "#E377C2", - "#7F7F7F", - "#BCBD22", - "#17BECF" - ], - "font": { - "color": "rgb(36,36,36)" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "white", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" + "xaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "automargin": true, + "zerolinewidth": 2 }, - "paper_bgcolor": "white", - "plot_bgcolor": "white", - "polar": { - "angularaxis": { - "gridcolor": "rgb(232,232,232)", - "linecolor": "rgb(36,36,36)", - "showgrid": false, - "showline": true, - "ticks": "outside" + "yaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 }, - "bgcolor": "white", - "radialaxis": { - "gridcolor": "rgb(232,232,232)", - "linecolor": "rgb(36,36,36)", - "showgrid": false, - "showline": true, - "ticks": "outside" - } + "zerolinecolor": "white", + "automargin": true, + "zerolinewidth": 2 }, "scene": { "xaxis": { - "backgroundcolor": "white", - "gridcolor": "rgb(232,232,232)", - "gridwidth": 2, - "linecolor": "rgb(36,36,36)", + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "linecolor": "white", "showbackground": true, - "showgrid": false, - "showline": true, - "ticks": "outside", - "zeroline": false, - "zerolinecolor": "rgb(36,36,36)" + "ticks": "", + "zerolinecolor": "white", + "gridwidth": 2 }, "yaxis": { - "backgroundcolor": "white", - "gridcolor": "rgb(232,232,232)", - "gridwidth": 2, - "linecolor": "rgb(36,36,36)", + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "linecolor": "white", "showbackground": true, - "showgrid": false, - "showline": true, - "ticks": "outside", - "zeroline": false, - "zerolinecolor": "rgb(36,36,36)" + "ticks": "", + "zerolinecolor": "white", + "gridwidth": 2 }, "zaxis": { - "backgroundcolor": "white", - "gridcolor": "rgb(232,232,232)", - "gridwidth": 2, - "linecolor": "rgb(36,36,36)", + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "linecolor": "white", "showbackground": true, - "showgrid": false, - "showline": true, - "ticks": "outside", - "zeroline": false, - "zerolinecolor": "rgb(36,36,36)" + "ticks": "", + "zerolinecolor": "white", + "gridwidth": 2 } }, "shapedefaults": { - "fillcolor": "black", "line": { - "width": 0 - }, - "opacity": 0.3 + "color": "#2a3f5f" + } }, - "ternary": { - "aaxis": { - "gridcolor": "rgb(232,232,232)", - "linecolor": "rgb(36,36,36)", - "showgrid": false, - "showline": true, - "ticks": "outside" - }, - "baxis": { - "gridcolor": "rgb(232,232,232)", - "linecolor": "rgb(36,36,36)", - "showgrid": false, - "showline": true, - "ticks": "outside" - }, + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "geo": { "bgcolor": "white", - "caxis": { - "gridcolor": "rgb(232,232,232)", - "linecolor": "rgb(36,36,36)", - "showgrid": false, - "showline": true, - "ticks": "outside" - } + "landcolor": "#E5ECF6", + "subunitcolor": "white", + "showland": true, + "showlakes": true, + "lakecolor": "white" }, "title": { "x": 0.05 }, - "xaxis": { - "automargin": true, - "gridcolor": "rgb(232,232,232)", - "linecolor": "rgb(36,36,36)", - "showgrid": false, - "showline": true, - "ticks": "outside", - "title": { - "standoff": 15 - }, - "zeroline": false, - "zerolinecolor": "rgb(36,36,36)" - }, - "yaxis": { - "automargin": true, - "gridcolor": "rgb(232,232,232)", - "linecolor": "rgb(36,36,36)", - "showgrid": false, - "showline": true, - "ticks": "outside", - "title": { - "standoff": 15 - }, - "zeroline": false, - "zerolinecolor": "rgb(36,36,36)" - } - } - }, - "title": { - "text": "Mass Spectrum", - "font": { - "family": "Helvetica", - "size": 18 - } - }, - "xaxis": { - "title": { - "text": "mass-to-charge", - "font": { - "family": "Helvetica", - "size": 16 - } - }, - "tickfont": { - "size": 14, - "family": "Helvetica" - }, - "gridcolor": "#CCCCCC", - "showgrid": true, - "showline": true, - "linewidth": 1, - "linecolor": "black", - "ticks": "outside", - "tickwidth": 1, - "tickcolor": "black", - "range": [ - 80.08, - 126.11999999999999 - ] - }, - "yaxis": { - "title": { - "text": "Intensity", - "font": { - "family": "Helvetica", - "size": 16 + "mapbox": { + "style": "light" } - }, - "tickfont": { - "size": 14, - "family": "Helvetica" - }, - "gridcolor": "#CCCCCC", - "showgrid": true, - "showline": true, - "linewidth": 1, - "linecolor": "black", - "tickwidth": 1, - "tickcolor": "black", - "range": [ - 0, - 115.0 - ] - }, - "width": 500, - "height": 500, - "dragmode": "select", - "legend": { - "font": { - "size": 10, - "family": "Helvetica" - }, - "title": { - "text": "Trace" - } - }, - "showlegend": true, - "font": { - "family": "Helvetica" - }, - "plot_bgcolor": "#FFFFFF", - "annotations": [ - { - "font": { - "color": "black", - "family": "Open Sans Mono, monospace", - "size": 12 - }, - "showarrow": false, - "text": "105.0", - "x": 105.0, - "xanchor": "left", - "y": 100.0 - }, - { - "font": { - "color": "black", - "family": "Open Sans Mono, monospace", - "size": 12 - }, - "showarrow": false, - "text": "105.1", - "x": 105.1, - "xanchor": "left", - "y": 90.0 - }, - { - "font": { - "color": "black", - "family": "Open Sans Mono, monospace", - "size": 12 - }, - "showarrow": false, - "text": "100.2", - "x": 100.2, - "xanchor": "left", - "y": 50.0 - }, - { - "font": { - "color": "black", - "family": "Open Sans Mono, monospace", - "size": 12 - }, - "showarrow": false, - "text": "100.1", - "x": 100.1, - "xanchor": "left", - "y": 10.0 - } - ], - "shapes": [ - { - "line": { - "color": "#EEEEEE", - "width": 2 - }, - "opacity": 1, - "type": "line", - "x0": 0, - "x1": 1, - "xref": "x domain", - "y0": 0, - "y1": 0, - "yref": "y" } - ] + } } } \ No newline at end of file diff --git a/test/test_pandas_accessor.py b/test/test_pandas_accessor.py index 11eee6dae..c2e58ddf8 100644 --- a/test/test_pandas_accessor.py +++ b/test/test_pandas_accessor.py @@ -1,6 +1,4 @@ import pandas as pd -import pytest -import pyopenms_viz def test_pandas_ms_plot_snapshot(snapshot): """ @@ -23,10 +21,20 @@ def test_pandas_ms_plot_snapshot(snapshot): show_plot=False ) - # Apply tight layout to matplotlib to ensure it is not cut off (matches repo standards) + # 1. Matplotlib handling if pd.options.plotting.backend == "ms_matplotlib": fig = out.get_figure() fig.tight_layout() + assert snapshot == out - # Let syrupy handle the serialization and comparison - assert snapshot == out \ No newline at end of file + # 2. Plotly handling (CodeRabbit Fix) + elif pd.options.plotting.backend == "ms_plotly": + # Convert to dictionary and strip version-sensitive template data + fig_dict = out.to_dict() + if "layout" in fig_dict and "template" in fig_dict["layout"]: + del fig_dict["layout"]["template"] + assert snapshot == fig_dict + + # 3. Default handling (Bokeh, etc.) + else: + assert snapshot == out \ No newline at end of file