{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n.. redirect-from:: /tutorials/intermediate/autoscale\n\n\n# Autoscaling Axis\n\nThe limits on an axis can be set manually (e.g. ``ax.set_xlim(xmin, xmax)``)\nor Matplotlib can set them automatically based on the data already on the Axes.\nThere are a number of options to this autoscaling behaviour, discussed below.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will start with a simple line plot showing that autoscaling\nextends the axis limits 5% beyond the data limits (-2\u03c0, 2\u03c0).\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\nimport numpy as np\n\nimport matplotlib as mpl\n\nx = np.linspace(-2 * np.pi, 2 * np.pi, 100)\ny = np.sinc(x)\n\nfig, ax = plt.subplots()\nax.plot(x, y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Margins\nThe default margin around the data limits is 5%, which is based on the\ndefault configuration setting of :rc:`axes.xmargin`, :rc:`axes.ymargin`,\nand :rc:`axes.zmargin`:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(ax.margins())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The margin size can be overridden to make them smaller or larger using\n`~matplotlib.axes.Axes.margins`:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\nax.plot(x, y)\nax.margins(0.2, 0.2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In general, margins can be in the range (-0.5, \u221e), where negative margins set\nthe axes limits to a subrange of the data range, i.e. they clip data.\nUsing a single number for margins affects both axes, a single margin can be\ncustomized using keyword arguments ``x`` or ``y``, but positional and keyword\ninterface cannot be combined.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\nax.plot(x, y)\nax.margins(y=-0.2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sticky edges\nThere are plot elements (`.Artist`\\s) that are usually used without margins.\nFor example false-color images (e.g. created with `.Axes.imshow`) are not\nconsidered in the margins calculation.\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "xx, yy = np.meshgrid(x, x)\nzz = np.sinc(np.sqrt((xx - 1)**2 + (yy - 1)**2))\n\nfig, ax = plt.subplots(ncols=2, figsize=(12, 8))\nax[0].imshow(zz)\nax[0].set_title(\"default margins\")\nax[1].imshow(zz)\nax[1].margins(0.2)\nax[1].set_title(\"margins(0.2)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This override of margins is determined by \"sticky edges\", a\nproperty of `.Artist` class that can suppress adding margins to axis\nlimits. The effect of sticky edges can be disabled on an Axes by changing\n`~matplotlib.axes.Axes.use_sticky_edges`.\nArtists have a property `.Artist.sticky_edges`, and the values of\nsticky edges can be changed by writing to ``Artist.sticky_edges.x`` or\n``Artist.sticky_edges.y``.\n\nThe following example shows how overriding works and when it is needed.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots(ncols=3, figsize=(16, 10))\nax[0].imshow(zz)\nax[0].margins(0.2)\nax[0].set_title(\"default use_sticky_edges\\nmargins(0.2)\")\nax[1].imshow(zz)\nax[1].margins(0.2)\nax[1].use_sticky_edges = False\nax[1].set_title(\"use_sticky_edges=False\\nmargins(0.2)\")\nax[2].imshow(zz)\nax[2].margins(-0.2)\nax[2].set_title(\"default use_sticky_edges\\nmargins(-0.2)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see that setting ``use_sticky_edges`` to *False* renders the image\nwith requested margins.\n\nWhile sticky edges don't increase the axis limits through extra margins,\nnegative margins are still taken into account. This can be seen in\nthe reduced limits of the third image.\n\n## Controlling autoscale\n\nBy default, the limits are\nrecalculated every time you add a new curve to the plot:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots(ncols=2, figsize=(12, 8))\nax[0].plot(x, y)\nax[0].set_title(\"Single curve\")\nax[1].plot(x, y)\nax[1].plot(x * 2.0, y)\nax[1].set_title(\"Two curves\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, there are cases when you don't want to automatically adjust the\nviewport to new data.\n\nOne way to disable autoscaling is to manually set the\naxis limit. Let's say that we want to see only a part of the data in\ngreater detail. Setting the ``xlim`` persists even if we add more curves to\nthe data. To recalculate the new limits calling `.Axes.autoscale` will\ntoggle the functionality manually.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots(ncols=2, figsize=(12, 8))\nax[0].plot(x, y)\nax[0].set_xlim(left=-1, right=1)\nax[0].plot(x + np.pi * 0.5, y)\nax[0].set_title(\"set_xlim(left=-1, right=1)\\n\")\nax[1].plot(x, y)\nax[1].set_xlim(left=-1, right=1)\nax[1].plot(x + np.pi * 0.5, y)\nax[1].autoscale()\nax[1].set_title(\"set_xlim(left=-1, right=1)\\nautoscale()\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can check that the first plot has autoscale disabled and that the second\nplot has it enabled again by using `.Axes.get_autoscale_on()`:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(ax[0].get_autoscale_on()) # False means disabled\nprint(ax[1].get_autoscale_on()) # True means enabled -> recalculated" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Arguments of the autoscale function give us precise control over the process\nof autoscaling. A combination of arguments ``enable``, and ``axis`` sets the\nautoscaling feature for the selected axis (or both). The argument ``tight``\nsets the margin of the selected axis to zero. To preserve settings of either\n``enable`` or ``tight`` you can set the opposite one to *None*, that way\nit should not be modified. However, setting ``enable`` to *None* and tight\nto *True* affects both axes regardless of the ``axis`` argument.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\nax.plot(x, y)\nax.margins(0.2, 0.2)\nax.autoscale(enable=None, axis=\"x\", tight=True)\n\nprint(ax.margins())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working with collections\n\nAutoscale works out of the box for all lines, patches, and images added to\nthe Axes. One of the artists that it won't work with is a `.Collection`.\nAfter adding a collection to the Axes, one has to manually trigger the\n`~matplotlib.axes.Axes.autoscale_view()` to recalculate\naxes limits.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\ncollection = mpl.collections.StarPolygonCollection(\n 5, rotation=0, sizes=(250,), # five point star, zero angle, size 250px\n offsets=np.column_stack([x, y]), # Set the positions\n offset_transform=ax.transData, # Propagate transformations of the Axes\n)\nax.add_collection(collection)\nax.autoscale_view()" ] } ], "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.13.2" } }, "nbformat": 4, "nbformat_minor": 0 }