Skip to content

Commit e1558ae

Browse files
authored
Enhance documentation and minor fixes in backend plotting (#658)
1 parent 7e61b97 commit e1558ae

9 files changed

Lines changed: 284 additions & 162 deletions

File tree

docs/source/how_to/how_to_change_plotting_backend.ipynb

Lines changed: 189 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -14,79 +14,14 @@
1414
"metadata": {},
1515
"source": [
1616
"optimagic supports various visualization libraries as plotting backends, which can be\n",
17-
"selected using the `backend` argument."
18-
]
19-
},
20-
{
21-
"cell_type": "markdown",
22-
"id": "2",
23-
"metadata": {},
24-
"source": [
25-
"::::{tab-set}\n",
26-
"\n",
27-
":::{tab-item} Plotly\n",
28-
"\n",
29-
"The default plotting library. To select the Plotly backend explicitly, set `backend=\"plotly\"`.\n",
30-
"\n",
31-
"The returned figure object is a [`plotly.graph_objects.Figure`](https://plotly.com/python-api-reference/generated/plotly.graph_objects.Figure.html).\n",
32-
"\n",
33-
":::\n",
34-
"\n",
35-
":::{tab-item} Matplotlib\n",
36-
"\n",
37-
"To select the Matplotlib backend, set `backend=\"matplotlib\"`.\n",
38-
"\n",
39-
"The returned figure object is a [`matplotlib.axes.Axes`](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html).\n",
40-
"\n",
41-
"```{note}\n",
42-
"In case of grid plots (such as `convergence_plot` or `slice_plot`), the returned object is a 2-dimensional numpy array of `Axes` objects: [`numpy.ndarray`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html)[[`matplotlib.axes.Axes`]](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html) of shape `(n_rows, n_cols)`.\n",
43-
"```\n",
44-
"\n",
45-
":::\n",
46-
"\n",
47-
":::{tab-item} Bokeh\n",
48-
"\n",
49-
"To select the Bokeh backend, set `backend=\"bokeh\"`.\n",
50-
"\n",
51-
"The returned figure object is a [`bokeh.plotting.figure`](https://docs.bokeh.org/en/latest/docs/reference/plotting/figure.html).\n",
52-
"\n",
53-
"```{note}\n",
54-
"In case of grid plots (such as `convergence_plot` or `slice_plot`), the returned object is a [`bokeh.models.GridPlot`](https://docs.bokeh.org/en/latest/docs/reference/models/plots.html#bokeh.models.GridPlot) object.\n",
55-
"```\n",
56-
"\n",
57-
"```{warning}\n",
58-
"Bokeh applies themes globally. Passing the `template` parameter to a plotting function updates the theme for all existing and future Bokeh plots. If you do not pass `template`, a default template is applied, which will also change the global theme.\n",
59-
"```\n",
60-
"\n",
61-
":::\n",
62-
"\n",
63-
":::{tab-item} Altair\n",
64-
"To select the Altair backend, set `backend=\"altair\"`.\n",
65-
"\n",
66-
"The returned figure object is an [`altair.Chart`](https://altair-viz.github.io/user_guide/generated/toplevel/altair.Chart.html).\n",
67-
"\n",
68-
"```{note}\n",
69-
"In case of grid plots (such as `convergence_plot` or `slice_plot`), the returned object is either an [`altair.Chart`](https://altair-viz.github.io/user_guide/generated/toplevel/altair.Chart.html) if there is only one subplot, an [`altair.HConcatChart`](https://altair-viz.github.io/user_guide/generated/toplevel/altair.HConcatChart.html) if there is only one row, or an [`altair.VConcatChart`](https://altair-viz.github.io/user_guide/generated/toplevel/altair.VConcatChart.html) otherwise.\n",
70-
"```\n",
71-
"\n",
72-
":::\n",
73-
"\n",
74-
"::::"
75-
]
76-
},
77-
{
78-
"cell_type": "markdown",
79-
"id": "3",
80-
"metadata": {},
81-
"source": [
82-
"In the following guide, we showcase the criterion plot visualized using different\n",
83-
"backends."
17+
"selected using the `backend` argument. In the following guide, we showcase the \n",
18+
"`criterion_plot` visualized using different backends."
8419
]
8520
},
8621
{
8722
"cell_type": "code",
8823
"execution_count": null,
89-
"id": "4",
24+
"id": "2",
9025
"metadata": {},
9126
"outputs": [],
9227
"source": [
@@ -106,33 +41,43 @@
10641
},
10742
{
10843
"cell_type": "markdown",
109-
"id": "5",
44+
"id": "3",
11045
"metadata": {},
11146
"source": [
112-
"## Plotly"
47+
"## Backends"
11348
]
11449
},
11550
{
11651
"cell_type": "markdown",
117-
"id": "6",
52+
"id": "4",
53+
"metadata": {},
54+
"source": [
55+
"### Plotly"
56+
]
57+
},
58+
{
59+
"cell_type": "markdown",
60+
"id": "5",
11861
"metadata": {},
11962
"source": [
120-
":::{note}\n",
63+
"The default plotting library. To select the Plotly backend explicitly, set `backend=\"plotly\"`.\n",
64+
"\n",
65+
"The returned figure object is a [`plotly.graph_objects.Figure`](https://plotly.com/python-api-reference/generated/plotly.graph_objects.Figure.html).\n",
12166
"\n",
67+
"```{note}\n",
12268
"**Choose the Plotly renderer according to your environment:**\n",
12369
"\n",
12470
"- Use `plotly.io.renderers.default = \"notebook_connected\"` in Jupyter notebooks for interactive plots.\n",
12571
"- Use `plotly.io.renderers.default = \"browser\"` to open plots in your default web browser when running as a script.\n",
12672
"\n",
12773
"Refer to the [Plotly documentation](https://plotly.com/python/renderers/) for more details.\n",
128-
"\n",
129-
":::"
74+
"```"
13075
]
13176
},
13277
{
13378
"cell_type": "code",
13479
"execution_count": null,
135-
"id": "7",
80+
"id": "6",
13681
"metadata": {},
13782
"outputs": [],
13883
"source": [
@@ -144,12 +89,24 @@
14489
"fig.show()"
14590
]
14691
},
92+
{
93+
"cell_type": "markdown",
94+
"id": "7",
95+
"metadata": {},
96+
"source": [
97+
"### Matplotlib"
98+
]
99+
},
147100
{
148101
"cell_type": "markdown",
149102
"id": "8",
150103
"metadata": {},
151104
"source": [
152-
"## Matplotlib"
105+
"To select the Matplotlib backend, set `backend=\"matplotlib\"`.\n",
106+
"\n",
107+
"The returned figure object is a [`matplotlib.axes.Axes`](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html).\n",
108+
"\n",
109+
"In case of grid plots (such as `convergence_plot` or `slice_plot`), the returned object is a 2-dimensional numpy array of `Axes` objects: [`numpy.ndarray`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html)[[`matplotlib.axes.Axes`]](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html) of shape `(n_rows, n_cols)`."
153110
]
154111
},
155112
{
@@ -167,13 +124,30 @@
167124
"id": "10",
168125
"metadata": {},
169126
"source": [
170-
"## Bokeh"
127+
"### Bokeh"
128+
]
129+
},
130+
{
131+
"cell_type": "markdown",
132+
"id": "11",
133+
"metadata": {},
134+
"source": [
135+
"To select the Bokeh backend, set `backend=\"bokeh\"`.\n",
136+
"\n",
137+
"The returned figure object is a [`bokeh.plotting.figure`](https://docs.bokeh.org/en/latest/docs/reference/plotting/figure.html).\n",
138+
"\n",
139+
"In case of grid plots (such as `convergence_plot` or `slice_plot`), the returned object is a [`bokeh.models.GridPlot`](https://docs.bokeh.org/en/latest/docs/reference/models/plots.html#bokeh.models.GridPlot) object.\n",
140+
"\n",
141+
"```{warning}\n",
142+
"- Bokeh applies themes globally. Passing the `template` parameter to a plotting function updates the theme for all existing and future Bokeh plots. If you do not pass `template`, a default template is applied, which will also change the global theme.\n",
143+
"- Bokeh doesn't support titles for grid plots. So, the `title` parameter in `slice_plot` is ignored when using the Bokeh backend.\n",
144+
"```\n"
171145
]
172146
},
173147
{
174148
"cell_type": "code",
175149
"execution_count": null,
176-
"id": "11",
150+
"id": "12",
177151
"metadata": {},
178152
"outputs": [],
179153
"source": [
@@ -187,32 +161,40 @@
187161
},
188162
{
189163
"cell_type": "markdown",
190-
"id": "12",
164+
"id": "13",
191165
"metadata": {},
192166
"source": [
193-
"## Altair"
167+
"### Altair"
194168
]
195169
},
196170
{
197171
"cell_type": "markdown",
198-
"id": "13",
172+
"id": "14",
199173
"metadata": {},
200174
"source": [
201-
":::{note}\n",
175+
"To select the Altair backend, set `backend=\"altair\"`.\n",
176+
"\n",
177+
"The returned figure object is an [`altair.Chart`](https://altair-viz.github.io/user_guide/generated/toplevel/altair.Chart.html).\n",
178+
"\n",
179+
"In case of grid plots (such as `convergence_plot` or `slice_plot`), the returned object is either an [`altair.Chart`](https://altair-viz.github.io/user_guide/generated/toplevel/altair.Chart.html) if there is only one subplot, an [`altair.HConcatChart`](https://altair-viz.github.io/user_guide/generated/toplevel/altair.HConcatChart.html) if there is only one row, or an [`altair.VConcatChart`](https://altair-viz.github.io/user_guide/generated/toplevel/altair.VConcatChart.html) otherwise.\n",
180+
"\n",
181+
"```{warning}\n",
182+
"Altair applies themes globally. Passing the `template` parameter to a plotting function updates the theme for all existing and future Altair plots. If you do not pass `template`, a default template is applied, which will also change the global theme.\n",
183+
"```\n",
202184
"\n",
185+
"```{note}\n",
203186
"It is mostly not required to set the renderer manually, as Altair automatically\n",
204187
"selects the appropriate renderer based on your environment. In this example,\n",
205188
"we explicitly set the renderer to ensure correct display within the documentation.\n",
206189
"\n",
207190
"Refer to the [Altair documentation](https://altair-viz.github.io/user_guide/display_frontends.html) for more details.\n",
208-
"\n",
209-
":::"
191+
"```\n"
210192
]
211193
},
212194
{
213195
"cell_type": "code",
214196
"execution_count": null,
215-
"id": "14",
197+
"id": "15",
216198
"metadata": {},
217199
"outputs": [],
218200
"source": [
@@ -224,6 +206,128 @@
224206
"chart = om.criterion_plot(results, backend=\"altair\")\n",
225207
"chart.show()"
226208
]
209+
},
210+
{
211+
"cell_type": "markdown",
212+
"id": "16",
213+
"metadata": {},
214+
"source": [
215+
"## Customizing plots"
216+
]
217+
},
218+
{
219+
"cell_type": "markdown",
220+
"id": "17",
221+
"metadata": {},
222+
"source": [
223+
"Here, we provide a simple example of how to customize plots created with different backends.\n",
224+
"\n",
225+
"::::{tab-set}\n",
226+
"\n",
227+
":::{tab-item} Plotly\n",
228+
"\n",
229+
"```python\n",
230+
"fig = om.criterion_plot(results, backend=\"plotly\")\n",
231+
"\n",
232+
"# Configure Axes\n",
233+
"fig.update_yaxes(title_text=\"Custom Y Label\", title_font_size=20)\n",
234+
"fig.update_xaxes(range=[0, 100])\n",
235+
"\n",
236+
"# Change legend position\n",
237+
"fig.update_layout(legend=dict(xanchor=\"left\", yanchor=\"top\", x=1, y=0.6))\n",
238+
"\n",
239+
"# Configure line properties\n",
240+
"# The index corresponding to a line, can be inferred from the legend\n",
241+
"# In case of criterion_plot, it is the order of optimizers in `results`\n",
242+
"fig.data[0].update(line=dict(width=4))\n",
243+
"fig.data[1].update(line=dict(dash=\"dashdot\"))\n",
244+
"\n",
245+
"fig.show()\n",
246+
"```\n",
247+
":::\n",
248+
"\n",
249+
":::{tab-item} Matplotlib\n",
250+
"\n",
251+
"```python\n",
252+
"ax = om.criterion_plot(results, backend=\"matplotlib\")\n",
253+
"\n",
254+
"# Configure Axis\n",
255+
"ax.set_ylabel(ylabel=\"Custom Y Label\", fontsize=20)\n",
256+
"ax.set_xlim(0, 100)\n",
257+
"\n",
258+
"# Change legend position\n",
259+
"ax.figure.legends[0].set_loc(\"outside center right\")\n",
260+
"\n",
261+
"# Configure line properties\n",
262+
"# The index corresponding to a line, can be inferred from the legend\n",
263+
"# In case of criterion_plot, it is the order of optimizers in `results`\n",
264+
"ax.lines[0].set_linewidth(4)\n",
265+
"ax.lines[1].set_linestyle(\"dashdot\")\n",
266+
"```\n",
267+
"\n",
268+
":::\n",
269+
"\n",
270+
":::{tab-item} Bokeh\n",
271+
"\n",
272+
"```python\n",
273+
"from bokeh.models import Range1d\n",
274+
"\n",
275+
"p = om.criterion_plot(results, backend=\"bokeh\")\n",
276+
"\n",
277+
"# Configure Axes\n",
278+
"p.yaxis.axis_label = \"Custom Y Label\"\n",
279+
"p.yaxis.axis_label_text_font_size = \"20pt\"\n",
280+
"p.x_range = Range1d(0, 100)\n",
281+
"\n",
282+
"# Change legend position\n",
283+
"p.add_layout(p.legend[0], \"right\")\n",
284+
"p.legend[0].location = \"center\"\n",
285+
"\n",
286+
"# Configure line properties\n",
287+
"# The index corresponding to a line, can be inferred from the legend\n",
288+
"# In case of criterion_plot, it is the order of optimizers in `results`\n",
289+
"p.renderers[0].glyph.line_width = 4\n",
290+
"p.renderers[1].glyph.line_dash = \"dashdot\"\n",
291+
"\n",
292+
"show(p)\n",
293+
"```\n",
294+
"\n",
295+
":::\n",
296+
"\n",
297+
":::{tab-item} Altair\n",
298+
"\n",
299+
"```{note}\n",
300+
"Due to the nature of Altair charts, top-level configuration may not work as expected. In these cases, it might be necessary to override the encoding.\n",
301+
"```\n",
302+
"\n",
303+
"```python\n",
304+
"import altair as alt\n",
305+
"\n",
306+
"chart = om.criterion_plot(results, backend=\"altair\")\n",
307+
"\n",
308+
"# Configure Axes\n",
309+
"chart = chart.encode(\n",
310+
" y=alt.Y(\"y\", axis=alt.Axis(title=\"Custom Y Label\", titleFontSize=20)),\n",
311+
" x=alt.X(\"x\", scale=alt.Scale(domain=(0, 100))),\n",
312+
")\n",
313+
"\n",
314+
"# Configure lines\n",
315+
"chart = chart.encode(\n",
316+
" strokeWidth=alt.condition(\n",
317+
" alt.datum.name == \"scipy_lbfgsb\", alt.value(4), alt.value(2)\n",
318+
" ),\n",
319+
" strokeDash=alt.condition(\n",
320+
" alt.datum.name == \"scipy_neldermead\", alt.value([8, 4, 2, 4]), alt.value([1, 0])\n",
321+
" ),\n",
322+
")\n",
323+
"\n",
324+
"chart.show()\n",
325+
"```\n",
326+
"\n",
327+
":::\n",
328+
"\n",
329+
"::::"
330+
]
227331
}
228332
],
229333
"metadata": {

0 commit comments

Comments
 (0)