{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Arrow guide\n\nAdding arrow patches to plots.\n\nArrows are often used to annotate plots. This tutorial shows how to plot arrows\nthat behave differently when the data limits on a plot are changed. In general,\npoints on a plot can either be fixed in \"data space\" or \"display space\".\nSomething plotted in data space moves when the data limits are altered - an\nexample would be the points in a scatter plot. Something plotted in display\nspace stays static when data limits are altered - an example would be a\nfigure title or the axis labels.\n\nArrows consist of a head (and possibly a tail) and a stem drawn between a\nstart point and end point, called 'anchor points' from now on.\nHere we show three use cases for plotting arrows, depending on whether the\nhead or anchor points need to be fixed in data or display space:\n\n1. Head shape fixed in display space, anchor points fixed in data space\n2. Head shape and anchor points fixed in display space\n3. Entire patch fixed in data space\n\nBelow each use case is presented in turn.\n\n.. redirect-from:: /gallery/text_labels_and_annotations/arrow_simple_demo\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n\nimport matplotlib.patches as mpatches\n\nx_tail = 0.1\ny_tail = 0.5\nx_head = 0.9\ny_head = 0.8\ndx = x_head - x_tail\ndy = y_head - y_tail" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Head shape fixed in display space and anchor points fixed in data space\n\nThis is useful if you are annotating a plot, and don't want the arrow\nto change shape or position if you pan or scale the plot.\n\nIn this case we use `.patches.FancyArrowPatch`.\n\nNote that when the axis limits are changed, the arrow shape stays the same,\nbut the anchor points move.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, axs = plt.subplots(nrows=2)\narrow = mpatches.FancyArrowPatch((x_tail, y_tail), (x_head, y_head),\n mutation_scale=100)\naxs[0].add_patch(arrow)\n\narrow = mpatches.FancyArrowPatch((x_tail, y_tail), (x_head, y_head),\n mutation_scale=100)\naxs[1].add_patch(arrow)\naxs[1].set(xlim=(0, 2), ylim=(0, 2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Head shape and anchor points fixed in display space\n\nThis is useful if you are annotating a plot, and don't want the arrow to\nchange shape or position if you pan or scale the plot.\n\nIn this case we use `.patches.FancyArrowPatch`, and pass the keyword argument\n``transform=ax.transAxes`` where ``ax`` is the Axes we are adding the patch\nto.\n\nNote that when the axis limits are changed, the arrow shape and location\nstay the same.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, axs = plt.subplots(nrows=2)\narrow = mpatches.FancyArrowPatch((x_tail, y_tail), (x_head, y_head),\n mutation_scale=100,\n transform=axs[0].transAxes)\naxs[0].add_patch(arrow)\n\narrow = mpatches.FancyArrowPatch((x_tail, y_tail), (x_head, y_head),\n mutation_scale=100,\n transform=axs[1].transAxes)\naxs[1].add_patch(arrow)\naxs[1].set(xlim=(0, 2), ylim=(0, 2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Head shape and anchor points fixed in data space\n\nIn this case we use `.patches.Arrow`, or `.patches.FancyArrow` (the latter is\nin orange).\n\nNote that when the axis limits are changed, the arrow shape and location\nchange.\n\n`.FancyArrow`'s API is relatively awkward, and requires in particular passing\n``length_includes_head=True`` so that the arrow *tip* is ``(dx, dy)`` away\nfrom the arrow start. It is only included in this reference because it is\nthe arrow class returned by `.Axes.arrow` (in green).\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, axs = plt.subplots(nrows=2)\n\narrow = mpatches.Arrow(x_tail, y_tail, dx, dy)\naxs[0].add_patch(arrow)\narrow = mpatches.FancyArrow(x_tail, y_tail - .4, dx, dy,\n width=.1, length_includes_head=True, color=\"C1\")\naxs[0].add_patch(arrow)\naxs[0].arrow(x_tail + 1, y_tail - .4, dx, dy,\n width=.1, length_includes_head=True, color=\"C2\")\n\narrow = mpatches.Arrow(x_tail, y_tail, dx, dy)\naxs[1].add_patch(arrow)\narrow = mpatches.FancyArrow(x_tail, y_tail - .4, dx, dy,\n width=.1, length_includes_head=True, color=\"C1\")\naxs[1].add_patch(arrow)\naxs[1].arrow(x_tail + 1, y_tail - .4, dx, dy,\n width=.1, length_includes_head=True, color=\"C2\")\naxs[1].set(xlim=(0, 2), ylim=(0, 2))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "plt.show()" ] } ], "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 }