{ "cells": [ { "cell_type": "markdown", "id": "0", "metadata": {}, "source": [ "# Tutorial 08 – Plotly Backend\n", "\n", "The **Plotly backend** renders your maxplotlib canvas as an interactive `plotly.graph_objects.Figure`. Unlike the default matplotlib output, Plotly figures:\n", "\n", "- Are **interactive** in Jupyter: zoom, pan, hover for values, toggle traces.\n", "- Can be **exported as standalone HTML** files that work in any browser—no Python or server required.\n", "- Support multi-subplot layouts.\n", "\n", "### When to use Plotly vs matplotlib\n", "\n", "| Use case | Backend |\n", "|---|---|\n", "| Quick static plot / publication PDF | `matplotlib` (default) |\n", "| Interactive exploration in Jupyter | `plotly` |\n", "| Share a self-contained interactive report | `plotly` → `fig.write_html(...)` |\n", "| LaTeX document figure | `tikzfigure` |" ] }, { "cell_type": "code", "execution_count": null, "id": "1", "metadata": {}, "outputs": [], "source": [ "%load_ext autoreload\n", "%autoreload 2\n", "%matplotlib inline\n", "\n", "from maxplotlib import Canvas\n", "import numpy as np" ] }, { "cell_type": "markdown", "id": "2", "metadata": {}, "source": [ "## Plotly in Jupyter\n", "\n", "In notebooks, you can either:\n", "\n", "- call `canvas.show(backend=\"plotly\")` (displays and returns a Plotly figure), or\n", "- call `fig = canvas.plot(backend=\"plotly\")` and put `fig` as the last line of a cell.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "3", "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, 2 * np.pi, 200)\n", "\n", "canvas = Canvas(width=\"10cm\", ratio=0.45)\n", "canvas.add_line(x, np.sin(x), color=\"steelblue\", label=\"sin(x)\")\n", "canvas.set_title(\"Displayed inline\")\n", "canvas.set_legend(True)\n", "\n", "fig = canvas.show(backend=\"plotly\")\n", "fig" ] }, { "cell_type": "markdown", "id": "4", "metadata": {}, "source": [ "## 1 · Basic line plot\n", "\n", "Switch to the Plotly backend by passing `backend='plotly'` to `canvas.plot()`. The returned object is a genuine `plotly.graph_objects.Figure`, so every Plotly method is available on it." ] }, { "cell_type": "code", "execution_count": null, "id": "5", "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, 2 * np.pi, 200)\n", "\n", "canvas = Canvas(width=\"10cm\", ratio=0.55)\n", "canvas.add_line(x, np.sin(x), color=\"steelblue\", label=\"sin(x)\")\n", "canvas.set_xlabel(\"x\")\n", "canvas.set_ylabel(\"y\")\n", "canvas.set_title(\"Basic line plot\")\n", "\n", "fig = canvas.show(backend=\"plotly\")\n", "fig" ] }, { "cell_type": "markdown", "id": "6", "metadata": {}, "source": [ "## 2 · Multiple lines\n", "\n", "Each `ax.plot()` call becomes a separate Plotly trace. Enable the legend with `ax.set_legend(True)` so trace labels appear." ] }, { "cell_type": "code", "execution_count": null, "id": "7", "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, 2 * np.pi, 200)\n", "\n", "canvas = Canvas(width=\"10cm\", ratio=0.55)\n", "canvas.add_line(x, np.sin(x), color=\"steelblue\", label=\"sin(x)\", linewidth=2)\n", "canvas.add_line(x, np.cos(x), color=\"tomato\", label=\"cos(x)\", linewidth=2)\n", "canvas.add_line(\n", " x,\n", " np.sin(2 * x),\n", " color=\"seagreen\",\n", " label=\"sin(2x)\",\n", " linewidth=2,\n", " linestyle=\"dashed\",\n", ")\n", "\n", "canvas.set_xlabel(\"x\")\n", "canvas.set_ylabel(\"y\")\n", "canvas.set_title(\"Multiple lines\")\n", "canvas.set_legend(True)\n", "\n", "fig = canvas.show(backend=\"plotly\")\n", "fig" ] }, { "cell_type": "markdown", "id": "8", "metadata": {}, "source": [ "## 3 · Scatter plot\n", "\n", "`ax.scatter()` maps to a Plotly scatter trace with markers only." ] }, { "cell_type": "code", "execution_count": null, "id": "9", "metadata": {}, "outputs": [], "source": [ "rng = np.random.default_rng(42)\n", "n = 120\n", "x_data = rng.uniform(0, 10, n)\n", "y_data = 0.5 * x_data + rng.normal(0, 1, n)\n", "\n", "canvas = Canvas(width=\"10cm\", ratio=0.6)\n", "canvas.scatter(\n", " x_data, y_data, color=\"steelblue\", marker=\"o\", s=20, label=\"observations\"\n", ")\n", "canvas.add_line(\n", " [0, 10],\n", " [0, 5],\n", " color=\"tomato\",\n", " linestyle=\"dashed\",\n", " linewidth=2,\n", " label=\"y = 0.5x\",\n", ")\n", "\n", "canvas.set_xlabel(\"x\")\n", "canvas.set_ylabel(\"y\")\n", "canvas.set_title(\"Scatter plot with trend line\")\n", "canvas.set_legend(True)\n", "\n", "fig = canvas.show(backend=\"plotly\")\n", "fig" ] }, { "cell_type": "markdown", "id": "10", "metadata": {}, "source": [ "## 4 · Bar chart\n", "\n", "`ax.bar()` maps to a Plotly bar trace." ] }, { "cell_type": "code", "execution_count": null, "id": "11", "metadata": {}, "outputs": [], "source": [ "categories = [\"Alpha\", \"Beta\", \"Gamma\", \"Delta\", \"Epsilon\"]\n", "values = [4.2, 7.1, 3.8, 5.9, 6.4]\n", "x_pos = np.arange(len(categories))\n", "\n", "canvas = Canvas(width=\"10cm\", ratio=0.6)\n", "canvas.bar(x_pos, values, color=\"steelblue\", label=\"metric\")\n", "canvas.set_xticks(x_pos, categories)\n", "\n", "canvas.set_xlabel(\"Category\")\n", "canvas.set_ylabel(\"Value\")\n", "canvas.set_title(\"Bar chart\")\n", "canvas.set_legend(True)\n", "\n", "fig = canvas.show(backend=\"plotly\")\n", "fig" ] }, { "cell_type": "markdown", "id": "12", "metadata": {}, "source": [ "## 5 · Mixing lines and bars\n", "\n", "You can place multiple trace types on the same axes — Plotly handles the overlay automatically." ] }, { "cell_type": "code", "execution_count": null, "id": "13", "metadata": {}, "outputs": [], "source": [ "months = np.arange(1, 13)\n", "rainfall = np.array([55, 48, 62, 70, 85, 40, 30, 35, 60, 90, 75, 65])\n", "cumulative = np.cumsum(rainfall)\n", "\n", "canvas = Canvas(width=\"10cm\", ratio=0.6)\n", "canvas.bar(\n", " months, rainfall, color=\"steelblue\", alpha=0.7, label=\"monthly rainfall (mm)\"\n", ")\n", "canvas.add_line(\n", " months,\n", " cumulative / 10,\n", " color=\"tomato\",\n", " linewidth=2.5,\n", " marker=\"o\",\n", " label=\"cumulative / 10\",\n", ")\n", "\n", "canvas.set_xlabel(\"Month\")\n", "canvas.set_ylabel(\"Rainfall (mm)\")\n", "canvas.set_title(\"Monthly rainfall + cumulative trend\")\n", "canvas.set_legend(True)\n", "\n", "fig = canvas.show(backend=\"plotly\")\n", "fig" ] }, { "cell_type": "markdown", "id": "14", "metadata": {}, "source": [ "## 6 · Multiple subplots\n", "\n", "Multi-subplot canvases are fully supported. Each panel gets its own axis labels and title; `canvas.suptitle(...)` sets the figure-level title." ] }, { "cell_type": "code", "execution_count": null, "id": "15", "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, 2 * np.pi, 200)\n", "rng = np.random.default_rng(0)\n", "\n", "canvas = Canvas(ncols=1) # , width=\"14cm\", ratio=0.35)\n", "\n", "# Left panel — line plot\n", "canvas.add_line(x, np.sin(x), color=\"steelblue\", label=\"sin(x)\", linewidth=2, col=0)\n", "canvas.add_line(x, np.cos(x), color=\"tomato\", label=\"cos(x)\", linewidth=2, col=0)\n", "canvas.set_xlabel(\"x\", col=0)\n", "canvas.set_ylabel(\"y\", col=0)\n", "canvas.set_title(\"Trigonometric functions\", col=0)\n", "canvas.set_legend(True, col=0)\n", "\n", "# Right panel — scatter\n", "x_s = rng.uniform(0, 6, 80)\n", "y_s = np.sin(x_s) + rng.normal(0, 0.15, 80)\n", "canvas.scatter(x_s, y_s, color=\"seagreen\", marker=\"o\", s=18, label=\"noisy sin\", col=1)\n", "canvas.add_line(\n", " x,\n", " np.sin(x),\n", " color=\"black\",\n", " linestyle=\"dashed\",\n", " linewidth=1.5,\n", " label=\"true sin\",\n", " col=1,\n", ")\n", "canvas.set_xlabel(\"x\", col=1)\n", "canvas.set_ylabel(\"y\", col=1)\n", "canvas.set_title(\"Noisy observations\", col=1)\n", "canvas.set_legend(True, col=1)\n", "\n", "canvas.suptitle(\"Multi-panel Plotly figure\")\n", "\n", "fig = canvas.show(backend=\"plotly\")" ] }, { "cell_type": "markdown", "id": "16", "metadata": {}, "source": [ "## 7 · Log scale\n", "\n", "`ax.set_yscale('log')` is passed through to Plotly's axis type." ] }, { "cell_type": "code", "execution_count": null, "id": "17", "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0.1, 5, 200)\n", "\n", "canvas = Canvas(width=\"10cm\", ratio=0.55)\n", "canvas.add_line(x, np.exp(x), color=\"steelblue\", label=\"exp(x)\", linewidth=2)\n", "canvas.add_line(x, np.exp(1.5 * x), color=\"tomato\", label=\"exp(1.5x)\", linewidth=2)\n", "canvas.add_line(x, x**2, color=\"seagreen\", label=\"x²\", linewidth=2)\n", "\n", "canvas.set_xlabel(\"x\")\n", "canvas.set_ylabel(\"y (log scale)\")\n", "canvas.set_title(\"Log-scale y axis\")\n", "canvas.set_yscale(\"log\")\n", "canvas.set_legend(True)\n", "\n", "fig = canvas.show(backend=\"plotly\")" ] }, { "cell_type": "markdown", "id": "18", "metadata": {}, "source": [ "## 8 · Saving to HTML\n", "\n", "`fig.write_html()` saves a fully self-contained HTML file. The file works in any browser without Python, Plotly, or a running server — ideal for sharing interactive figures with colleagues." ] }, { "cell_type": "code", "execution_count": null, "id": "19", "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, 2 * np.pi, 200)\n", "\n", "canvas = Canvas(width=\"10cm\", ratio=0.55)\n", "canvas.add_line(x, np.sin(x), color=\"steelblue\", label=\"sin(x)\", linewidth=2)\n", "canvas.add_line(x, np.cos(x), color=\"tomato\", label=\"cos(x)\", linewidth=2)\n", "canvas.set_xlabel(\"x\")\n", "canvas.set_ylabel(\"y\")\n", "canvas.set_title(\"Saved interactive figure\")\n", "canvas.set_legend(True)\n", "\n", "# Writes a standalone HTML file — open it in any browser\n", "canvas.savefig(\"output.html\", backend=\"plotly\")\n", "print(\"Saved to output.html\")" ] }, { "cell_type": "markdown", "id": "20", "metadata": {}, "source": [ "## Summary\n", "\n", "### Plotly backend feature table\n", "\n", "| Feature | Supported | Notes |\n", "|---|---|---|\n", "| `ax.plot()` — line trace | ✅ | `color`, `linestyle`, `linewidth`, `marker` all passed through |\n", "| `ax.scatter()` — markers | ✅ | `color`, `marker`, `s`, `alpha` |\n", "| `ax.bar()` — bar chart | ✅ | `color`, `alpha` |\n", "| `ax.fill_between()` | ❌ | Not supported by this backend |\n", "| `ax.errorbar()` | ❌ | Not supported by this backend |\n", "| `ax.axhline/axvline` | ❌ | Not supported by this backend |\n", "| Multi-subplot canvas | ✅ | `Canvas.subplots(ncols=...)` etc. |\n", "| `canvas.suptitle()` | ✅ | Maps to figure title |\n", "| `ax.set_yscale('log')` | ✅ | |\n", "| `ax.set_legend(True)` | ✅ | |\n", "| `fig.show()` | ✅ | Interactive in Jupyter |\n", "| `fig.write_html(path)` | ✅ | Standalone interactive HTML |\n", "\n", "### Typical workflow\n", "\n", "```python\n", "from maxplotlib import Canvas\n", "import numpy as np\n", "\n", "canvas, ax = Canvas.subplots()\n", "ax.plot(x, y, label='data')\n", "ax.set_legend(True)\n", "\n", "fig = canvas.plot(backend='plotly') # → plotly.graph_objects.Figure\n", "fig.show() # interactive in Jupyter\n", "fig.write_html('report.html') # share with anyone\n", "```" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }