{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Multilevel (nested) ticks\n\nSometimes we want another level of tick labels on an axis, perhaps to indicate\na grouping of the ticks.\n\nMatplotlib does not provide an automated way to do this, but it is relatively\nstraightforward to annotate below the main axis.\n\nThese examples use `.Axes.secondary_xaxis`, which is one approach. It has the\nadvantage that we can use Matplotlib Locators and Formatters on the axis that\ndoes the grouping if we want.\n\nThis first example creates a secondary xaxis and manually adds the ticks and\nlabels using `.Axes.set_xticks`.  Note that the tick labels have a newline\n(e.g. ``\"\nOughts\"``) at the beginning of them to put the second-level tick\nlabels below the main tick labels.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\nimport numpy as np\n\nimport matplotlib.dates as mdates\n\nrng = np.random.default_rng(19680801)\n\nfig, ax = plt.subplots(layout='constrained', figsize=(4, 4))\n\nax.plot(np.arange(30))\n\nsec = ax.secondary_xaxis(location=0)\nsec.set_xticks([5, 15, 25], labels=['\\nOughts', '\\nTeens', '\\nTwenties'])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "This second example adds a second level of annotation to a categorical axis.\nHere we need to note that each animal (category) is assigned an integer, so\n``cats`` is at x=0, ``dogs`` at x=1 etc.  Then we place the ticks on the\nsecond level on an x that is at the middle of the animal class we are trying\nto delineate.\n\nThis example also adds tick marks between the classes by adding a second\nsecondary xaxis, and placing long, wide ticks at the boundaries between the\nanimal classes.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fig, ax = plt.subplots(layout='constrained', figsize=(7, 4))\n\nax.plot(['cats', 'dogs', 'pigs', 'snakes', 'lizards', 'chickens',\n         'eagles', 'herons', 'buzzards'],\n        rng.normal(size=9), 'o')\n\n# label the classes:\nsec = ax.secondary_xaxis(location=0)\nsec.set_xticks([1, 3.5, 6.5], labels=['\\n\\nMammals', '\\n\\nReptiles', '\\n\\nBirds'])\nsec.tick_params('x', length=0)\n\n# lines between the classes:\nsec2 = ax.secondary_xaxis(location=0)\nsec2.set_xticks([-0.5, 2.5, 4.5, 8.5], labels=[])\nsec2.tick_params('x', length=40, width=1.5)\nax.set_xlim(-0.6, 8.6)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Dates are another common place where we may want to have a second level of\ntick labels.  In this last example, we take advantage of the ability to add\nan automatic locator and formatter to the secondary xaxis, which means we do\nnot need to set the ticks manually.\n\nThis example also differs from the above, in that we placed it at a location\nbelow the main axes ``location=-0.075`` and then we hide the spine by setting\nthe line width to zero.  That means that our formatter no longer needs the\ncarriage returns of the previous two examples.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fig, ax = plt.subplots(layout='constrained', figsize=(7, 4))\n\ntime = np.arange(np.datetime64('2020-01-01'), np.datetime64('2020-03-31'),\n                 np.timedelta64(1, 'D'))\n\nax.plot(time, rng.random(size=len(time)))\n\n# just format the days:\nax.xaxis.set_major_formatter(mdates.DateFormatter('%d'))\n\n# label the months:\nsec = ax.secondary_xaxis(location=-0.075)\nsec.xaxis.set_major_locator(mdates.MonthLocator(bymonthday=1))\n\n# note the extra spaces in the label to align the month label inside the month.\n# Note that this could have been done by changing ``bymonthday`` above as well:\nsec.xaxis.set_major_formatter(mdates.DateFormatter('  %b'))\nsec.tick_params('x', length=0)\nsec.spines['bottom'].set_linewidth(0)\n\n# label the xaxis, but note for this to look good, it needs to be on the\n# secondary xaxis.\nsec.set_xlabel('Dates (2020)')\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.14.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}