{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Legend Demo\n\nThere are many ways to create and customize legends in Matplotlib. Below\nwe'll show a few examples for how to do so.\n\nFirst we'll show off how to make a legend for specific lines.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\nimport numpy as np\n\nimport matplotlib.collections as mcol\nfrom matplotlib.legend_handler import HandlerLineCollection, HandlerTuple\nfrom matplotlib.lines import Line2D\n\nt1 = np.arange(0.0, 2.0, 0.1)\nt2 = np.arange(0.0, 2.0, 0.01)\n\nfig, ax = plt.subplots()\n\n# note that plot returns a list of lines. The \"l1, = plot\" usage\n# extracts the first element of the list into l1 using tuple\n# unpacking. So l1 is a Line2D instance, not a sequence of lines\nl1, = ax.plot(t2, np.exp(-t2))\nl2, l3 = ax.plot(t2, np.sin(2 * np.pi * t2), '--o', t1, np.log(1 + t1), '.')\nl4, = ax.plot(t2, np.exp(-t2) * np.sin(2 * np.pi * t2), 's-.')\n\nax.legend((l2, l4), ('oscillatory', 'damped'), loc='upper right', shadow=True)\nax.set_xlabel('time')\nax.set_ylabel('volts')\nax.set_title('Damped oscillation')\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next we'll demonstrate plotting more complex labels.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "x = np.linspace(0, 1)\n\nfig, (ax0, ax1) = plt.subplots(2, 1)\n\n# Plot the lines y=x**n for n=1..4.\nfor n in range(1, 5):\n ax0.plot(x, x**n, label=f\"{n=}\")\nleg = ax0.legend(loc=\"upper left\", bbox_to_anchor=[0, 1],\n ncols=2, shadow=True, title=\"Legend\", fancybox=True)\nleg.get_title().set_color(\"red\")\n\n# Demonstrate some more complex labels.\nax1.plot(x, x**2, label=\"multi\\nline\")\nhalf_pi = np.linspace(0, np.pi / 2)\nax1.plot(np.sin(half_pi), np.cos(half_pi), label=r\"$\\frac{1}{2}\\pi$\")\nax1.plot(x, 2**(x**2), label=\"$2^{x^2}$\")\nax1.legend(shadow=True, fancybox=True)\n\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we attach legends to more complex plots.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, axs = plt.subplots(3, 1, layout=\"constrained\")\ntop_ax, middle_ax, bottom_ax = axs\n\ntop_ax.bar([0, 1, 2], [0.2, 0.3, 0.1], width=0.4, label=\"Bar 1\",\n align=\"center\")\ntop_ax.bar([0.5, 1.5, 2.5], [0.3, 0.2, 0.2], color=\"red\", width=0.4,\n label=\"Bar 2\", align=\"center\")\ntop_ax.legend()\n\nmiddle_ax.errorbar([0, 1, 2], [2, 3, 1], xerr=0.4, fmt=\"s\", label=\"test 1\")\nmiddle_ax.errorbar([0, 1, 2], [3, 2, 4], yerr=0.3, fmt=\"o\", label=\"test 2\")\nmiddle_ax.errorbar([0, 1, 2], [1, 1, 3], xerr=0.4, yerr=0.3, fmt=\"^\",\n label=\"test 3\")\nmiddle_ax.legend()\n\nbottom_ax.stem([0.3, 1.5, 2.7], [1, 3.6, 2.7], label=\"stem test\")\nbottom_ax.legend()\n\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we'll showcase legend entries with more than one legend key.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, (ax1, ax2) = plt.subplots(2, 1, layout='constrained')\n\n# First plot: two legend keys for a single entry\np1 = ax1.scatter([1], [5], c='r', marker='s', s=100)\np2 = ax1.scatter([3], [2], c='b', marker='o', s=100)\n# `plot` returns a list, but we want the handle - thus the comma on the left\np3, = ax1.plot([1, 5], [4, 4], 'm-d')\n\n# Assign two of the handles to the same legend entry by putting them in a tuple\n# and using a generic handler map (which would be used for any additional\n# tuples of handles like (p1, p3)).\nl = ax1.legend([(p1, p3), p2], ['two keys', 'one key'], scatterpoints=1,\n numpoints=1, handler_map={tuple: HandlerTuple(ndivide=None)})\n\n# Second plot: plot two bar charts on top of each other and change the padding\n# between the legend keys\nx_left = [1, 2, 3]\ny_pos = [1, 3, 2]\ny_neg = [2, 1, 4]\n\nrneg = ax2.bar(x_left, y_neg, width=0.5, color='w', hatch='///', label='-1')\nrpos = ax2.bar(x_left, y_pos, width=0.5, color='k', label='+1')\n\n# Treat each legend entry differently by using specific `HandlerTuple`s\nl = ax2.legend([(rpos, rneg), (rneg, rpos)], ['pad!=0', 'pad=0'],\n handler_map={(rpos, rneg): HandlerTuple(ndivide=None),\n (rneg, rpos): HandlerTuple(ndivide=None, pad=0.)})\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, it is also possible to write custom classes that define\nhow to stylize legends.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class HandlerDashedLines(HandlerLineCollection):\n \"\"\"\n Custom Handler for LineCollection instances.\n \"\"\"\n def create_artists(self, legend, orig_handle,\n xdescent, ydescent, width, height, fontsize, trans):\n # figure out how many lines there are\n numlines = len(orig_handle.get_segments())\n xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent,\n width, height, fontsize)\n leglines = []\n # divide the vertical space where the lines will go\n # into equal parts based on the number of lines\n ydata = np.full_like(xdata, height / (numlines + 1))\n # for each line, create the line at the proper location\n # and set the dash pattern\n for i in range(numlines):\n legline = Line2D(xdata, ydata * (numlines - i) - ydescent)\n self.update_prop(legline, orig_handle, legend)\n # set color, dash pattern, and linewidth to that\n # of the lines in linecollection\n try:\n color = orig_handle.get_colors()[i]\n except IndexError:\n color = orig_handle.get_colors()[0]\n try:\n dashes = orig_handle.get_dashes()[i]\n except IndexError:\n dashes = orig_handle.get_dashes()[0]\n try:\n lw = orig_handle.get_linewidths()[i]\n except IndexError:\n lw = orig_handle.get_linewidths()[0]\n if dashes[1] is not None:\n legline.set_dashes(dashes[1])\n legline.set_color(color)\n legline.set_transform(trans)\n legline.set_linewidth(lw)\n leglines.append(legline)\n return leglines\n\nx = np.linspace(0, 5, 100)\n\nfig, ax = plt.subplots()\ncolors = plt.rcParams['axes.prop_cycle'].by_key()['color'][:5]\nstyles = ['solid', 'dashed', 'dashed', 'dashed', 'solid']\nfor i, color, style in zip(range(5), colors, styles):\n ax.plot(x, np.sin(x) - .1 * i, c=color, ls=style)\n\n# make proxy artists\n# make list of one line -- doesn't matter what the coordinates are\nline = [[(0, 0)]]\n# set up the proxy artist\nlc = mcol.LineCollection(5 * line, linestyles=styles, colors=colors)\n# create the legend\nax.legend([lc], ['multi-line'], handler_map={type(lc): HandlerDashedLines()},\n handlelength=2.5, handleheight=3)\n\nplt.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 }