{ "cells": [ { "cell_type": "markdown", "id": "0", "metadata": {}, "source": [ "# Tutorial 10 - Matplotlib NxM Subplots and Spacing\n", "\n", "This tutorial shows how to build **NxM subplot grids** with the matplotlib backend and control the distance between plots using `wspace=...` and `hspace=...`.\n", "\n", "It includes both:\n", "- line plots\n", "- color plots (`add_imshow`)\n", "\n", "and prints measured subplot gaps so you can verify spacing changes numerically." ] }, { "cell_type": "code", "execution_count": null, "id": "1", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "from maxplotlib import Canvas" ] }, { "cell_type": "code", "execution_count": null, "id": "2", "metadata": {}, "outputs": [], "source": [ "def measure_gaps(axes):\n", " \"\"\"Measure one representative horizontal and vertical subplot gap.\"\"\"\n", " horizontal_gap = axes[0, 1].get_position().x0 - axes[0, 0].get_position().x1\n", " vertical_gap = axes[0, 0].get_position().y0 - axes[1, 0].get_position().y1\n", " return horizontal_gap, vertical_gap" ] }, { "cell_type": "markdown", "id": "3", "metadata": {}, "source": [ "## 1 · NxM line plots with spacing control" ] }, { "cell_type": "code", "execution_count": null, "id": "4", "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, 2 * np.pi, 200)\n", "\n", "tight_canvas, tight_axes = Canvas.subplots(\n", " nrows=2,\n", " ncols=3,\n", " width=\"14cm\",\n", " ratio=0.65,\n", " wspace=0.05,\n", " hspace=0.08,\n", ")\n", "for i, row in enumerate(tight_axes):\n", " for j, ax in enumerate(row):\n", " tight_canvas.plot(\n", " x, np.sin((i + 1) * (j + 1) * x), label=f\"sin({(i + 1) * (j + 1)}x)\"\n", " )\n", " tight_canvas.set_title(f\"line {i},{j}\")\n", "\n", "tight_fig, tight_m_axes = tight_canvas.render(backend=\"matplotlib\")\n", "tight_fig.suptitle(\"Line plots - tight spacing\")\n", "tight_h, tight_v = measure_gaps(tight_m_axes)\n", "print(f\"tight line gaps: h={tight_h:.4f}, v={tight_v:.4f}\")\n", "\n", "loose_canvas, loose_axes = Canvas.subplots(\n", " nrows=2,\n", " ncols=3,\n", " width=\"14cm\",\n", " ratio=0.65,\n", " wspace=0.45,\n", " hspace=0.45,\n", ")\n", "for i, row in enumerate(loose_axes):\n", " for j, ax in enumerate(row):\n", " loose_canvas.plot(\n", " x, np.sin((i + 1) * (j + 1) * x), label=f\"sin({(i + 1) * (j + 1)}x)\"\n", " )\n", " loose_canvas.set_title(f\"line {i},{j}\")\n", "\n", "loose_fig, loose_m_axes = loose_canvas.render(backend=\"matplotlib\")\n", "loose_fig.suptitle(\"Line plots - loose spacing\")\n", "loose_h, loose_v = measure_gaps(loose_m_axes)\n", "print(f\"loose line gaps: h={loose_h:.4f}, v={loose_v:.4f}\")\n", "\n", "assert loose_h > tight_h\n", "assert loose_v > tight_v" ] }, { "cell_type": "markdown", "id": "5", "metadata": {}, "source": [ "## 2 · NxM color plots (`imshow`) with spacing control" ] }, { "cell_type": "code", "execution_count": null, "id": "6", "metadata": {}, "outputs": [], "source": [ "base = np.arange(100).reshape(10, 10)\n", "\n", "tight_canvas, tight_axes = Canvas.subplots(\n", " nrows=2,\n", " ncols=3,\n", " width=\"14cm\",\n", " ratio=0.75,\n", " wspace=0.05,\n", " hspace=0.08,\n", ")\n", "idx = 0\n", "for row in tight_axes:\n", " for ax in row:\n", " tight_canvas.imshow(base + idx, cmap=\"viridis\")\n", " tight_canvas.set_title(f\"heatmap {idx}\")\n", " idx += 1\n", "\n", "tight_fig, tight_m_axes = tight_canvas.render(backend=\"matplotlib\")\n", "tight_fig.suptitle(\"Color plots - tight spacing\")\n", "tight_h, tight_v = measure_gaps(tight_m_axes)\n", "print(f\"tight color gaps: h={tight_h:.4f}, v={tight_v:.4f}\")\n", "\n", "loose_canvas, loose_axes = Canvas.subplots(\n", " nrows=2,\n", " ncols=3,\n", " width=\"14cm\",\n", " ratio=0.75,\n", " wspace=0.45,\n", " hspace=0.45,\n", ")\n", "idx = 0\n", "for row in loose_axes:\n", " for ax in row:\n", " loose_canvas.imshow(base + idx, cmap=\"viridis\")\n", " loose_canvas.set_title(f\"heatmap {idx}\")\n", " idx += 1\n", "\n", "loose_fig, loose_m_axes = loose_canvas.render(backend=\"matplotlib\")\n", "loose_fig.suptitle(\"Color plots - loose spacing\")\n", "loose_h, loose_v = measure_gaps(loose_m_axes)\n", "print(f\"loose color gaps: h={loose_h:.4f}, v={loose_v:.4f}\")\n", "\n", "assert loose_h > tight_h\n", "assert loose_v > tight_v\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "7", "metadata": {}, "source": [ "## Plotly preview\n", "\n", "The spacing measurements above are Matplotlib-specific. You can still preview the same canvas with the Plotly backend (layout/spacing will differ):\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "8", "metadata": {}, "outputs": [], "source": [ "# Reuse the last canvas created above\n", "loose_canvas.show(backend=\"plotly\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.13.3" } }, "nbformat": 4, "nbformat_minor": 5 }