{
  "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()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        ".. seealso::\n\n   The `legend_guide` contains an in depth discussion on the configuration\n   options for legends.\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.14.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}