Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## Unreleased
* plotting: make the plotly backends work with Plotly 7: marker sizes are numbers instead of strings (`plotly_offline` latent, inducing and error-bar plots raised `ValueError`), and showing a figure with the `'plotly'` / `'plotly_online'` library now raises a clear error pointing at `'plotly_offline'` or `'matplotlib'` instead of failing on the removed `plotly.plotly` Chart Studio API (#1243)
* packaging: require Python >=3.11 (matches CI; 3.9/3.10 are EOL) so the lockfile can drop unpatched 3.9-only pins (Discussion #1207)

## v1.15.0 (2026-10-24)
Expand Down
6 changes: 3 additions & 3 deletions GPy/plotting/plotly_dep/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@
data_2d = dict(marker="o", cmap="Hot", marker_kwargs=dict(opacity=1.0, size=5, line=dict(width=0.5, color="black")))
inducing_1d = dict(color=Tango.colorsHex["darkRed"])
inducing_2d = dict(
marker_kwargs=dict(size="5", opacity=0.7, line=dict(width=0.5, color="black")),
marker_kwargs=dict(size=5, opacity=0.7, line=dict(width=0.5, color="black")),
opacity=0.7,
color="white",
marker="star-triangle-up",
)
inducing_3d = dict(
marker_kwargs=dict(symbol="diamond", size="5", opacity=0.7, line=dict(width=0.1, color="black")), color="#F5F5F5"
marker_kwargs=dict(symbol="diamond", size=5, opacity=0.7, line=dict(width=0.1, color="black")), color="#F5F5F5"
)
xerrorbar = dict(color="black", error_kwargs=dict(thickness=0.5), opacity=0.5)
yerrorbar = dict(color=Tango.colorsHex["darkRed"], error_kwargs=dict(thickness=0.5), opacity=0.5)
Expand Down Expand Up @@ -82,5 +82,5 @@
latent = dict(colorscale="Greys", reversescale=True, zsmooth="best")
gradient = dict(colorscale="RdBu", opacity=0.7)
magnification = dict(colorscale="Greys", zsmooth="best", reversescale=True)
latent_scatter = dict(marker_kwargs=dict(size="5", opacity=0.7))
latent_scatter = dict(marker_kwargs=dict(size=5, opacity=0.7))
# annotation = dict(fontdict=dict(family='sans-serif', weight='light', fontsize=9), zorder=.3, alpha=.7)
27 changes: 11 additions & 16 deletions GPy/plotting/plotly_dep/plot_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ def xerrorbar(
z=Z,
mode="markers",
error_x=ErrorX(color=color, **error_kwargs or {}),
marker=dict(size="0"),
marker=dict(size=0),
name=label,
showlegend=label is not None,
**kwargs,
Expand All @@ -293,7 +293,7 @@ def xerrorbar(
y=Y,
mode="markers",
error_x=ErrorX(color=color, **error_kwargs or {}),
marker=dict(size="0"),
marker=dict(size=0),
name=label,
showlegend=label is not None,
**kwargs,
Expand All @@ -316,7 +316,7 @@ def yerrorbar(
z=Z,
mode="markers",
error_y=ErrorY(color=color, **error_kwargs or {}),
marker=dict(size="0"),
marker=dict(size=0),
name=label,
showlegend=label is not None,
**kwargs,
Expand All @@ -326,7 +326,7 @@ def yerrorbar(
y=Y,
mode="markers",
error_y=ErrorY(color=color, **error_kwargs or {}),
marker=dict(size="0"),
marker=dict(size=0),
name=label,
showlegend=label is not None,
**kwargs,
Expand Down Expand Up @@ -499,18 +499,13 @@ def __init__(self):
super(PlotlyPlotsOnline, self).__init__()

def show_canvas(self, canvas, filename=None, **kwargs):
figure, _, _ = canvas
if len(figure.data) == 0:
# add mock data
figure.add_trace(Scatter(x=[], y=[], name="", showlegend=False), row=1, col=1)
from ..gpy_plot.plot_util import in_ipynb

if in_ipynb():
return plotly.plotly.iplot(figure, filename=filename, **kwargs)
else:
return plotly.plotly.plot(
figure, filename=filename, **kwargs
) # self.current_states[hex(id(figure))]['filename'])
# plotly.plotly (Chart Studio upload) is no longer part of plotly (#1243).
raise RuntimeError(
"The 'plotly' (online) plotting library relied on plotly.plotly (Chart Studio), "
"which is not part of current plotly releases. Use "
"GPy.plotting.change_plotting_library('plotly_offline') or "
"GPy.plotting.change_plotting_library('matplotlib') instead."
)


class PlotlyPlotsOffline(PlotlyPlotsBase):
Expand Down
46 changes: 46 additions & 0 deletions GPy/testing/test_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,52 @@ def test_plotly_offline_loads_without_deprecation(self):
self.teardown_method()


class TestPlotly7:
"""#1243: the plotly backends must work with current plotly (no Chart Studio, numeric marker sizes)."""

def setup_method(self):
pytest.importorskip("plotly")
np.random.seed(1243)

def teardown_method(self):
if matplotlib is not None:
change_plotting_library("matplotlib")
else:
change_plotting_library("none")

def test_online_show_names_offline_alternatives(self):
change_plotting_library("plotly")
lib = plotting_library()
canvas, _ = lib.new_canvas()
with pytest.raises(RuntimeError, match="plotly_offline") as err:
lib.show_canvas(canvas)
assert "matplotlib" in str(err.value)

@pytest.mark.parametrize("projection", ["2d", "3d"])
def test_offline_inducing_marker_size(self, projection):
change_plotting_library("plotly_offline")
m = GPy.models.SparseGPRegression(np.random.rand(20, 2), np.random.rand(20, 1), num_inducing=5)
figure, _, _ = m.plot_inducing(projection=projection)
assert figure.data[0].marker.size == 5

def test_offline_latent_scatter_marker_size(self):
change_plotting_library("plotly_offline")
m = GPy.models.GPLVM(np.random.rand(15, 4), 2)
figure, _, _ = m.plot_latent()
assert [t.marker.size for t in figure.data if t.type == "scatter"] == [5]

@pytest.mark.parametrize("which", ["xerrorbar", "yerrorbar"])
@pytest.mark.parametrize("projection", ["2d", "3d"])
def test_offline_errorbar_marker_size(self, which, projection):
change_plotting_library("plotly_offline")
lib = plotting_library()
canvas, _ = lib.new_canvas(projection=projection)
X, Y, error = np.random.rand(5, 1), np.random.rand(5, 1), np.random.rand(5)
Z = np.random.rand(5, 1) if projection == "3d" else None
trace = getattr(lib, which)(canvas, X, Y, error, Z=Z)
assert trace.marker.size == 0


@pytest.mark.skipif(matplotlib is None, reason="Matplotlib not installed")
class TestShowAcceptsPlotDict:
"""#920: model.plot() returns a plots dict; show() must accept it (and Axes)."""
Expand Down
Loading