{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Timeline with lines, dates, and text\n\nHow to create a simple timeline using Matplotlib release dates.\n\nTimelines can be created with a collection of dates and text. In this example,\nwe show how to create a simple timeline using the dates for recent releases\nof Matplotlib. First, we'll pull the data from GitHub.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from datetime import datetime\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nimport matplotlib.dates as mdates\n\ntry:\n    # Try to fetch a list of Matplotlib releases and their dates\n    # from https://api.github.com/repos/matplotlib/matplotlib/releases\n    import json\n    import urllib.request\n\n    url = 'https://api.github.com/repos/matplotlib/matplotlib/releases'\n    url += '?per_page=100'\n    data = json.loads(urllib.request.urlopen(url, timeout=1).read().decode())\n\n    dates = []\n    releases = []\n    for item in data:\n        if 'rc' not in item['tag_name'] and 'b' not in item['tag_name']:\n            dates.append(item['published_at'].split(\"T\")[0])\n            releases.append(item['tag_name'].lstrip(\"v\"))\n\nexcept Exception:\n    # In case the above fails, e.g. because of missing internet connection\n    # use the following lists as fallback.\n    releases = ['2.2.4', '3.0.3', '3.0.2', '3.0.1', '3.0.0', '2.2.3',\n                '2.2.2', '2.2.1', '2.2.0', '2.1.2', '2.1.1', '2.1.0',\n                '2.0.2', '2.0.1', '2.0.0', '1.5.3', '1.5.2', '1.5.1',\n                '1.5.0', '1.4.3', '1.4.2', '1.4.1', '1.4.0']\n    dates = ['2019-02-26', '2019-02-26', '2018-11-10', '2018-11-10',\n             '2018-09-18', '2018-08-10', '2018-03-17', '2018-03-16',\n             '2018-03-06', '2018-01-18', '2017-12-10', '2017-10-07',\n             '2017-05-10', '2017-05-02', '2017-01-17', '2016-09-09',\n             '2016-07-03', '2016-01-10', '2015-10-29', '2015-02-16',\n             '2014-10-26', '2014-10-18', '2014-08-26']\n\ndates = [datetime.strptime(d, \"%Y-%m-%d\") for d in dates]  # Convert strs to dates.\nreleases = [tuple(release.split('.')) for release in releases]  # Split by component.\ndates, releases = zip(*sorted(zip(dates, releases)))  # Sort by increasing date."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Next, we'll create a stem plot with some variation in levels as to\ndistinguish even close-by events. We add markers on the baseline for visual\nemphasis on the one-dimensional nature of the timeline.\n\nFor each event, we add a text label via `~.Axes.annotate`, which is offset\nin units of points from the tip of the event line.\n\nNote that Matplotlib will automatically plot datetime inputs.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Choose some nice levels: alternate meso releases between top and bottom, and\n# progressively shorten the stems for micro releases.\nlevels = []\nmacro_meso_releases = sorted({release[:2] for release in releases})\nfor release in releases:\n    macro_meso = release[:2]\n    micro = int(release[2])\n    h = 1 + 0.8 * (5 - micro)\n    level = h if macro_meso_releases.index(macro_meso) % 2 == 0 else -h\n    levels.append(level)\n\n\ndef is_feature(release):\n    \"\"\"Return whether a version (split into components) is a feature release.\"\"\"\n    return release[-1] == '0'\n\n\n# The figure and the axes.\nfig, ax = plt.subplots(figsize=(8.8, 4), layout=\"constrained\")\nax.set(title=\"Matplotlib release dates\")\n\n# The vertical stems.\nax.vlines(dates, 0, levels,\n          color=[(\"tab:red\", 1 if is_feature(release) else .5) for release in releases])\n# The baseline.\nax.axhline(0, c=\"black\")\n# The markers on the baseline.\nmeso_dates = [date for date, release in zip(dates, releases) if is_feature(release)]\nmicro_dates = [date for date, release in zip(dates, releases)\n               if not is_feature(release)]\nax.plot(micro_dates, np.zeros_like(micro_dates), \"ko\", mfc=\"white\")\nax.plot(meso_dates, np.zeros_like(meso_dates), \"ko\", mfc=\"tab:red\")\n\n# Annotate the lines.\nfor date, level, release in zip(dates, levels, releases):\n    version_str = '.'.join(release)\n    ax.annotate(version_str, xy=(date, level),\n                xytext=(-3, np.sign(level)*3), textcoords=\"offset points\",\n                verticalalignment=\"bottom\" if level > 0 else \"top\",\n                weight=\"bold\" if is_feature(release) else \"normal\",\n                bbox=dict(boxstyle='square', pad=0, lw=0, fc=(1, 1, 1, 0.7)))\n\nax.xaxis.set(major_locator=mdates.YearLocator(),\n             major_formatter=mdates.DateFormatter(\"%Y\"))\n\n# Remove the y-axis and some spines.\nax.yaxis.set_visible(False)\nax.spines[[\"left\", \"top\", \"right\"]].set_visible(False)\n\nax.margins(y=0.1)\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        ".. admonition:: References\n\n   The use of the following functions, methods, classes and modules is shown\n   in this example:\n\n   - `matplotlib.axes.Axes.annotate`\n   - `matplotlib.axes.Axes.vlines`\n   - `matplotlib.axis.Axis.set_major_locator`\n   - `matplotlib.axis.Axis.set_major_formatter`\n   - `matplotlib.dates.MonthLocator`\n   - `matplotlib.dates.DateFormatter`\n\n.. tags::\n\n   component: annotation\n   plot-type: line\n   level: intermediate\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
}