{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n# Text in Matplotlib Plots\n\n\nIntroduction to plotting and working with text in Matplotlib.\n\nMatplotlib has extensive text support, including support for\nmathematical expressions, truetype support for raster and\nvector outputs, newline separated text with arbitrary\nrotations, and unicode support.\n\nBecause it embeds fonts directly in output documents, e.g., for postscript\nor PDF, what you see on the screen is what you get in the hardcopy.\n`FreeType `_ support\nproduces very nice, antialiased fonts, that look good even at small\nraster sizes. matplotlib includes its own\n:mod:`matplotlib.font_manager` (thanks to Paul Barrett), which\nimplements a cross platform, `W3C `\ncompliant font finding algorithm.\n\nThe user has a great deal of control over text properties (font size, font\nweight, text location and color, etc.) with sensible defaults set in\nthe `rc file `.\nAnd significantly, for those interested in mathematical\nor scientific figures, matplotlib implements a large number of TeX\nmath symbols and commands, supporting `mathematical expressions\n` anywhere in your figure.\n\n\nBasic text commands\n===================\n\nThe following commands are used to create text in the pyplot\ninterface and the object-oriented API:\n\n=================== =================== ======================================\n`.pyplot` API OO API description\n=================== =================== ======================================\n`~.pyplot.text` `~.Axes.text` Add text at an arbitrary location of\n the `~matplotlib.axes.Axes`.\n\n`~.pyplot.annotate` `~.Axes.annotate` Add an annotation, with an optional\n arrow, at an arbitrary location of the\n `~matplotlib.axes.Axes`.\n\n`~.pyplot.xlabel` `~.Axes.set_xlabel` Add a label to the\n `~matplotlib.axes.Axes`\\'s x-axis.\n\n`~.pyplot.ylabel` `~.Axes.set_ylabel` Add a label to the\n `~matplotlib.axes.Axes`\\'s y-axis.\n\n`~.pyplot.title` `~.Axes.set_title` Add a title to the\n `~matplotlib.axes.Axes`.\n\n`~.pyplot.figtext` `~.Figure.text` Add text at an arbitrary location of\n the `.Figure`.\n\n`~.pyplot.suptitle` `~.Figure.suptitle` Add a title to the `.Figure`.\n=================== =================== ======================================\n\nAll of these functions create and return a `.Text` instance, which can be\nconfigured with a variety of font and other properties. The example below\nshows all of these commands in action, and more detail is provided in the\nsections that follow.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import matplotlib\nimport matplotlib.pyplot as plt\n\nfig = plt.figure()\nfig.suptitle('bold figure suptitle', fontsize=14, fontweight='bold')\n\nax = fig.add_subplot(111)\nfig.subplots_adjust(top=0.85)\nax.set_title('axes title')\n\nax.set_xlabel('xlabel')\nax.set_ylabel('ylabel')\n\nax.text(3, 8, 'boxed italics text in data coords', style='italic',\n bbox={'facecolor': 'red', 'alpha': 0.5, 'pad': 10})\n\nax.text(2, 6, r'an equation: $E=mc^2$', fontsize=15)\n\nax.text(3, 2, u'unicode: Institut f\u00fcr Festk\u00f6rperphysik')\n\nax.text(0.95, 0.01, 'colored text in axes coords',\n verticalalignment='bottom', horizontalalignment='right',\n transform=ax.transAxes,\n color='green', fontsize=15)\n\n\nax.plot([2], [1], 'o')\nax.annotate('annotate', xy=(2, 1), xytext=(3, 4),\n arrowprops=dict(facecolor='black', shrink=0.05))\n\nax.axis([0, 10, 0, 10])\n\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Labels for x- and y-axis\n========================\n\nSpecifying the labels for the x- and y-axis is strightforward, via the\n`~matplotlib.axes.Axes.set_xlabel` and\n`~matplotlib.axes.Axes.set_ylabel` methods.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\nimport numpy as np\n\nx1 = np.linspace(0.0, 5.0, 100)\ny1 = np.cos(2 * np.pi * x1) * np.exp(-x1)\n\nfig, ax = plt.subplots(figsize=(5, 3))\nfig.subplots_adjust(bottom=0.15, left=0.2)\nax.plot(x1, y1)\nax.set_xlabel('time [s]')\nax.set_ylabel('Damped oscillation [V]')\n\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The x- and y-labels are automatically placed so that they clear the x- and\ny-ticklabels. Compare the plot below with that above, and note the y-label\nis to the left of the one above.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fig, ax = plt.subplots(figsize=(5, 3))\nfig.subplots_adjust(bottom=0.15, left=0.2)\nax.plot(x1, y1*10000)\nax.set_xlabel('time [s]')\nax.set_ylabel('Damped oscillation [V]')\n\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you want to move the labels, you can specify the *labelpad* kyeword\nargument, where the value is points (1/72\", the same unit used to specify\nfontsizes).\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fig, ax = plt.subplots(figsize=(5, 3))\nfig.subplots_adjust(bottom=0.15, left=0.2)\nax.plot(x1, y1*10000)\nax.set_xlabel('time [s]')\nax.set_ylabel('Damped oscillation [V]', labelpad=18)\n\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Or, the labels accept all the `.Text` keyword arguments, including\n*position*, via which we can manually specify the label positions. Here we\nput the xlabel to the far left of the axis. Note, that the y-coordinate of\nthis position has no effect - to adjust the y-position we need to use the\n*labelpad* kwarg.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fig, ax = plt.subplots(figsize=(5, 3))\nfig.subplots_adjust(bottom=0.15, left=0.2)\nax.plot(x1, y1)\nax.set_xlabel('time [s]', position=(0., 1e6),\n horizontalalignment='left')\nax.set_ylabel('Damped oscillation [V]')\n\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"All the labelling in this tutorial can be changed by manipulating the\n`matplotlib.font_manager.FontProperties` method, or by named kwargs to\n`~matplotlib.axes.Axes.set_xlabel`\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from matplotlib.font_manager import FontProperties\n\nfont = FontProperties()\nfont.set_family('serif')\nfont.set_name('Times New Roman')\nfont.set_style('italic')\n\nfig, ax = plt.subplots(figsize=(5, 3))\nfig.subplots_adjust(bottom=0.15, left=0.2)\nax.plot(x1, y1)\nax.set_xlabel('time [s]', fontsize='large', fontweight='bold')\nax.set_ylabel('Damped oscillation [V]', fontproperties=font)\n\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, we can use native TeX rendering in all text objects and have\nmultiple lines:\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fig, ax = plt.subplots(figsize=(5, 3))\nfig.subplots_adjust(bottom=0.2, left=0.2)\nax.plot(x1, np.cumsum(y1**2))\nax.set_xlabel('time [s] \\n This was a long experiment')\nax.set_ylabel(r'$\\int\\ Y^2\\ dt\\ \\ [V^2 s]$')\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Titles\n======\n\nSubplot titles are set in much the same way as labels, but there is\nthe *loc* keyword arguments that can change the position and justification\nfrom the default value of ``loc=center``.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fig, axs = plt.subplots(3, 1, figsize=(5, 6), tight_layout=True)\nlocs = ['center', 'left', 'right']\nfor ax, loc in zip(axs, locs):\n ax.plot(x1, y1)\n ax.set_title('Title with loc at '+loc, loc=loc)\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Vertical spacing for titles is controlled via ``rcParams[axes.titlepad]``,\nwhich defaults to 5 points. Setting to a different value moves the title.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fig, ax = plt.subplots(figsize=(5, 3))\nfig.subplots_adjust(top=0.8)\nax.plot(x1, y1)\nax.set_title('Vertically offset title', pad=30)\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ticks and ticklabels\n====================\n\nPlacing ticks and ticklabels is a very tricky aspect of making a figure.\nMatplotlib does the best it can automatically, but it also offers a very\nflexible framework for determining the choices for tick locations, and\nhow they are labelled.\n\nTerminology\n~~~~~~~~~~~\n\n*Axes* have an `matplotlib.axis` object for the ``ax.xaxis``\nand ``ax.yaxis`` that\ncontain the information about how the labels in the axis are laid out.\n\nThe axis API is explained in detail in the documentation to\n`~matplotlib.axis`.\n\nAn Axis object has major and minor ticks. The Axis has a\n`matplotlib.xaxis.set_major_locator` and\n`matplotlib.xaxis.set_minor_locator` methods that use the data being plotted\nto determine\nthe location of major and minor ticks. There are also\n`matplotlib.xaxis.set_major_formatter` and\n`matplotlib.xaxis.set_minor_formatters` methods that format the tick labels.\n\nSimple ticks\n~~~~~~~~~~~~\n\nIt often is convenient to simply define the\ntick values, and sometimes the tick labels, overriding the default\nlocators and formatters. This is discouraged because it breaks itneractive\nnavigation of the plot. It also can reset the axis limits: note that\nthe second plot has the ticks we asked for, including ones that are\nwell outside the automatic view limits.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)\naxs[0].plot(x1, y1)\naxs[1].plot(x1, y1)\naxs[1].xaxis.set_ticks(np.arange(0., 8.1, 2.))\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can of course fix this after the fact, but it does highlight a\nweakness of hard-coding the ticks. This example also changes the format\nof the ticks:\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)\naxs[0].plot(x1, y1)\naxs[1].plot(x1, y1)\nticks = np.arange(0., 8.1, 2.)\n# list comprehension to get all tick labels...\ntickla = ['%1.2f' % tick for tick in ticks]\naxs[1].xaxis.set_ticks(ticks)\naxs[1].xaxis.set_ticklabels(tickla)\naxs[1].set_xlim(axs[0].get_xlim())\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Tick Locators and Formatters\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nInstead of making a list of all the tickalbels, we could have\nused a `matplotlib.ticker.FormatStrFormatter` and passed it to the\n``ax.xaxis``\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)\naxs[0].plot(x1, y1)\naxs[1].plot(x1, y1)\nticks = np.arange(0., 8.1, 2.)\n# list comprehension to get all tick labels...\nformatter = matplotlib.ticker.StrMethodFormatter('{x:1.1f}')\naxs[1].xaxis.set_ticks(ticks)\naxs[1].xaxis.set_major_formatter(formatter)\naxs[1].set_xlim(axs[0].get_xlim())\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And of course we could have used a non-default locator to set the\ntick locations. Note we still pass in the tick values, but the\nx-limit fix used above is *not* needed.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)\naxs[0].plot(x1, y1)\naxs[1].plot(x1, y1)\nformatter = matplotlib.ticker.FormatStrFormatter('%1.1f')\nlocator = matplotlib.ticker.FixedLocator(ticks)\naxs[1].xaxis.set_major_locator(locator)\naxs[1].xaxis.set_major_formatter(formatter)\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The default formatter is the `matplotlib.ticker.MaxNLocator` called as\n``ticker.MaxNLocator(self, nbins='auto', steps=[1, 2, 2.5, 5, 10])``\nThe *steps* keyword contains a list of multiples that can be used for\ntick values. i.e. in this case, 2, 4, 6 would be acceptable ticks,\nas would 20, 40, 60 or 0.2, 0.4, 0.6. However, 3, 6, 9 would not be\nacceptable because 3 doesn't appear in the list of steps.\n\n``nbins=auto`` uses an algorithm to determine how many ticks will\nbe acceptable based on how long the axis is. The fontsize of the\nticklabel is taken into account, but the length of the tick string\nis not (because its not yet known.) In the bottom row, the\nticklabels are quite large, so we set ``nbins=4`` to make the\nlabels fit in the right-hand plot.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fig, axs = plt.subplots(2, 2, figsize=(8, 5), tight_layout=True)\naxs = axs.flatten()\nfor n, ax in enumerate(axs):\n ax.plot(x1*10., y1)\n\nformatter = matplotlib.ticker.FormatStrFormatter('%1.1f')\nlocator = matplotlib.ticker.MaxNLocator(nbins='auto', steps=[1, 4, 10])\naxs[1].xaxis.set_major_locator(locator)\naxs[1].xaxis.set_major_formatter(formatter)\n\nformatter = matplotlib.ticker.FormatStrFormatter('%1.5f')\nlocator = matplotlib.ticker.AutoLocator()\naxs[2].xaxis.set_major_formatter(formatter)\naxs[2].xaxis.set_major_locator(locator)\n\nformatter = matplotlib.ticker.FormatStrFormatter('%1.5f')\nlocator = matplotlib.ticker.MaxNLocator(nbins=4)\naxs[3].xaxis.set_major_formatter(formatter)\naxs[3].xaxis.set_major_locator(locator)\n\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, we can specify functions for the formatter using\n`matplotlib.ticker.FuncFormatter`.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def formatoddticks(x, pos):\n \"\"\"Format odd tick positions\n \"\"\"\n if x % 2:\n return '%1.2f' % x\n else:\n return ''\n\nfig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)\nax.plot(x1, y1)\nformatter = matplotlib.ticker.FuncFormatter(formatoddticks)\nlocator = matplotlib.ticker.MaxNLocator(nbins=6)\nax.xaxis.set_major_formatter(formatter)\nax.xaxis.set_major_locator(locator)\n\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dateticks\n~~~~~~~~~\n\nMatplotlib can accept `datetime.datetime` and `numpy.datetime64`\nobjects as plotting arguments. Dates and times require special\nformatting, which can often benefit from manual intervention. In\norder to help, dates have spectial Locators and Formatters,\ndefined in the `matplotlib.dates` module.\n\nA simple example is as follows. Note how we have to rotate the\ntick labels so that they don't over-run each other.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import datetime\n\nfig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)\nbase = datetime.datetime(2017, 1, 1, 0, 0, 1)\ntime = [base + datetime.timedelta(days=x) for x in range(len(y1))]\n\nax.plot(time, y1)\nax.tick_params(axis='x', rotation=70)\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Maybe the format of the labels above is acceptable, but the choices is\nrather idiosyncratic. We can make the ticks fall on the start of the month\nby modifying `matplotlib.dates.AutoDateLocator`\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import matplotlib.dates as mdates\n\nlocator = mdates.AutoDateLocator(interval_multiples=True)\n\nfig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)\nax.xaxis.set_major_locator(locator)\nax.plot(time, y1)\nax.tick_params(axis='x', rotation=70)\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"However, this changes the tick labels. The easiest fix is to pass a format\nto `matplotlib.dates.DateFormatter`. Also note that the 29th and the\nnext month are very close together. We can fix this by using the\n`dates.DayLocator` class, which allows us to specify a list of days of the\nmonth to use. Similar formatters are listed in the `matplotlib.dates` module.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"locator = mdates.DayLocator(bymonthday=[1, 15])\nformatter = mdates.DateFormatter('%b %d')\n\nfig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)\nax.xaxis.set_major_locator(locator)\nax.xaxis.set_major_formatter(formatter)\nax.plot(time, y1)\nax.tick_params(axis='x', rotation=70)\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Legends and Annotations\n=======================\n\n - Legends: :doc:`/tutorials/intermediate/legend_guide`\n - Annotations: :doc:`/tutorials/text/annotations`\n\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.7.2"
}
},
"nbformat": 4,
"nbformat_minor": 0
}