{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n\n.. redirect-from:: /gallery/subplots_axes_and_figures/colorbar_placement\n\n# Placing colorbars\n\nColorbars indicate the quantitative extent of image data. Placing in\na figure is non-trivial because room needs to be made for them.\n\n## Automatic placement of colorbars\n\nThe simplest case is just attaching a colorbar to each Axes. Note in this\nexample that the colorbars steal some space from the parent Axes.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\nimport numpy as np\n\n# Fixing random state for reproducibility\nnp.random.seed(19680801)\n\nfig, axs = plt.subplots(2, 2)\ncmaps = ['RdBu_r', 'viridis']\nfor col in range(2):\n for row in range(2):\n ax = axs[row, col]\n pcm = ax.pcolormesh(np.random.random((20, 20)) * (col + 1),\n cmap=cmaps[col])\n fig.colorbar(pcm, ax=ax)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first column has the same type of data in both rows, so it may be\ndesirable to have just one colorbar. We do this by passing `.Figure.colorbar`\na list of Axes with the *ax* kwarg.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, axs = plt.subplots(2, 2)\ncmaps = ['RdBu_r', 'viridis']\nfor col in range(2):\n for row in range(2):\n ax = axs[row, col]\n pcm = ax.pcolormesh(np.random.random((20, 20)) * (col + 1),\n cmap=cmaps[col])\n fig.colorbar(pcm, ax=axs[:, col], shrink=0.6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The stolen space can lead to Axes in the same subplot layout\nbeing different sizes, which is often undesired if the the\nx-axis on each plot is meant to be comparable as in the following:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, axs = plt.subplots(2, 1, figsize=(4, 5), sharex=True)\nX = np.random.randn(20, 20)\naxs[0].plot(np.sum(X, axis=0))\npcm = axs[1].pcolormesh(X)\nfig.colorbar(pcm, ax=axs[1], shrink=0.6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is usually undesired, and can be worked around in various ways, e.g.\nadding a colorbar to the other Axes and then removing it. However, the most\nstraightforward is to use `constrained layout `:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, axs = plt.subplots(2, 1, figsize=(4, 5), sharex=True, layout='constrained')\naxs[0].plot(np.sum(X, axis=0))\npcm = axs[1].pcolormesh(X)\nfig.colorbar(pcm, ax=axs[1], shrink=0.6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Relatively complicated colorbar layouts are possible using this\nparadigm. Note that this example works far better with\n``layout='constrained'``\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, axs = plt.subplots(3, 3, layout='constrained')\nfor ax in axs.flat:\n pcm = ax.pcolormesh(np.random.random((20, 20)))\n\nfig.colorbar(pcm, ax=axs[0, :2], shrink=0.6, location='bottom')\nfig.colorbar(pcm, ax=[axs[0, 2]], location='bottom')\nfig.colorbar(pcm, ax=axs[1:, :], location='right', shrink=0.6)\nfig.colorbar(pcm, ax=[axs[2, 1]], location='left')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adjusting the spacing between colorbars and parent Axes\n\nThe distance a colorbar is from the parent Axes can be adjusted with the\n*pad* keyword argument. This is in units of fraction of the parent Axes\nwidth, and the default for a vertical Axes is 0.05 (or 0.15 for a horizontal\nAxes).\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, axs = plt.subplots(3, 1, layout='constrained', figsize=(5, 5))\nfor ax, pad in zip(axs, [0.025, 0.05, 0.1]):\n pcm = ax.pcolormesh(np.random.randn(20, 20), cmap='viridis')\n fig.colorbar(pcm, ax=ax, pad=pad, label=f'pad: {pad}')\nfig.suptitle(\"layout='constrained'\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that if you do not use constrained layout, the pad command makes the\nparent Axes shrink:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, axs = plt.subplots(3, 1, figsize=(5, 5))\nfor ax, pad in zip(axs, [0.025, 0.05, 0.1]):\n pcm = ax.pcolormesh(np.random.randn(20, 20), cmap='viridis')\n fig.colorbar(pcm, ax=ax, pad=pad, label=f'pad: {pad}')\nfig.suptitle(\"No layout manager\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Manual placement of colorbars\n\nSometimes the automatic placement provided by ``colorbar`` does not\ngive the desired effect. We can manually create an Axes and tell\n``colorbar`` to use that Axes by passing the Axes to the *cax* keyword\nargument.\n\n### Using ``inset_axes``\n\nWe can manually create any type of Axes for the colorbar to use, but an\n`.Axes.inset_axes` is useful because it is a child of the parent Axes and can\nbe positioned relative to the parent. Here we add a colorbar centered near\nthe bottom of the parent Axes.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots(layout='constrained', figsize=(4, 4))\npcm = ax.pcolormesh(np.random.randn(20, 20), cmap='viridis')\nax.set_ylim([-4, 20])\ncax = ax.inset_axes([0.3, 0.07, 0.4, 0.04])\nfig.colorbar(pcm, cax=cax, orientation='horizontal')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`.Axes.inset_axes` can also specify its position in data coordinates\nusing the *transform* keyword argument if you want your Axes at a\ncertain data position on the graph:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots(layout='constrained', figsize=(4, 4))\npcm = ax.pcolormesh(np.random.randn(20, 20), cmap='viridis')\nax.set_ylim([-4, 20])\ncax = ax.inset_axes([7.5, -1.7, 5, 1.2], transform=ax.transData)\nfig.colorbar(pcm, cax=cax, orientation='horizontal')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Colorbars attached to fixed-aspect-ratio Axes\n\nAxes with a fixed aspect ratio may shrink in height to preserve the aspect\nratio of the underlying data. This can result in the colorbar becoming taller\nthan the associated Axes, as demonstrated in the following example.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots(layout='constrained', figsize=(4, 4))\npcm = ax.imshow(np.random.randn(10, 10), cmap='viridis')\nfig.colorbar(pcm, ax=ax)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To automatically adjust the colorbar size to match the parent Axes, we can\nuse ``layout='compressed'``. This ensures that as the figure is resized or\nthe fixed-aspect-ratio Axes is zoomed in or out, the colorbar dynamically\nresizes to align with the parent Axes.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots(layout='compressed', figsize=(4, 4))\npcm = ax.imshow(np.random.randn(10, 10), cmap='viridis')\nax.set_title(\"Colorbar with layout='compressed'\", fontsize='medium')\nfig.colorbar(pcm, ax=ax)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, we can manually position the colorbar using `.Axes.inset_axes`\nwith axes-relative coordinates. This approach provides precise control over\nthe colorbar's placement. However, without a layout engine, the colorbar\nmight be clipped if it extends beyond the figure boundaries.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots(layout='constrained', figsize=(4, 4))\npcm = ax.imshow(np.random.randn(10, 10), cmap='viridis')\ncax = ax.inset_axes([1.04, 0.0, 0.05, 1.0]) # Positioning the colorbar\nax.set_title('Colorbar with inset_axes', fontsize='medium')\nfig.colorbar(pcm, cax=cax)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also do this manually using an `.Axes.inset_axes` using axes-relative\ncoordinates (see `transforms_tutorial`). Note that if we do not use a\nlayout engine, the colorbar will be clipped off the right side of the figure.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots(layout='constrained', figsize=(4, 4))\npcm = ax.imshow(np.random.randn(10, 10), cmap='viridis')\ncax = ax.inset_axes([1.04, 0.0, 0.05, 1.0])\nax.set_title('Colorbar with inset_axes', fontsize='medium')\nfig.colorbar(pcm, cax=cax)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ".. seealso::\n\n `axes_grid` has methods for manually creating colorbar Axes as well:\n\n - `demo-colorbar-with-inset-locator`\n - `demo-colorbar-with-axes-divider`\n\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.15" } }, "nbformat": 4, "nbformat_minor": 0 }