diff --git a/doc/_static/anatomy.png b/doc/_static/anatomy.png index d3054395b06e..22b4d893a6c9 100644 Binary files a/doc/_static/anatomy.png and b/doc/_static/anatomy.png differ diff --git a/tutorials/introductory/usage.py b/tutorials/introductory/usage.py index fc265883c66a..cb2d1d5b9519 100644 --- a/tutorials/introductory/usage.py +++ b/tutorials/introductory/usage.py @@ -1,6 +1,6 @@ """ *********** -Usage Guide +Basic Usage *********** This tutorial covers some basic usage patterns and best practices to @@ -8,6 +8,7 @@ """ # sphinx_gallery_thumbnail_number = 3 +import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np @@ -18,39 +19,21 @@ # # Matplotlib graphs your data on `~.figure.Figure`\s (e.g., windows, Jupyter # widgets, etc.), each of which can contain one or more `~.axes.Axes`, an -# area where points can be specified in terms of x-y coordinates, or theta-r -# in a polar plot, x-y-z in a 3D plot, etc. The simplest way of +# area where points can be specified in terms of x-y coordinates (or theta-r +# in a polar plot, x-y-z in a 3D plot, etc). The simplest way of # creating a figure with an axes is using `.pyplot.subplots`. We can then use # `.Axes.plot` to draw some data on the axes: fig, ax = plt.subplots() # Create a figure containing a single axes. ax.plot([1, 2, 3, 4], [1, 4, 2, 3]) # Plot some data on the axes. -############################################################################### -# Many other plotting libraries or languages do not require you to explicitly -# create an axes. For example, in MATLAB, one can just do -# -# .. code-block:: matlab -# -# plot([1, 2, 3, 4], [1, 4, 2, 3]) % MATLAB plot. -# -# and get the desired graph. -# -# In fact, you can do the same in Matplotlib: for each `~.axes.Axes` graphing -# method, there is a corresponding function in the :mod:`matplotlib.pyplot` -# module that performs that plot on the "current" axes, creating that axes (and -# its parent figure) if they don't exist yet. So, the previous example can be -# written more shortly as - -plt.plot([1, 2, 3, 4], [1, 4, 2, 3]) # Matplotlib plot. - ############################################################################### # .. _figure_parts: # # Parts of a Figure # ================= # -# Here is a more detailed layout of the components of a Matplotlib figure. +# Here are the components of a Matplotlib figure. # # .. image:: ../../_static/anatomy.png # @@ -59,12 +42,8 @@ # # The **whole** figure. The figure keeps # track of all the child :class:`~matplotlib.axes.Axes`, a group of -# 'special' artists (titles, figure legends, etc), and the **canvas**. -# (The canvas is not the primary focus. It is crucial as it is the -# object that actually does the drawing to get you your plot, but as -# the user, it is mostly invisible to you). A figure can contain any -# number of :class:`~matplotlib.axes.Axes`, but will typically have -# at least one. +# 'special' artists (titles, figure legends, colorbars, etc), and +# even nested subfigures. # # The easiest way to create a new figure is with pyplot:: # @@ -72,35 +51,35 @@ # fig, ax = plt.subplots() # a figure with a single Axes # fig, axs = plt.subplots(2, 2) # a figure with a 2x2 grid of Axes # -# It's convenient to create the axes together with the figure, but you can -# also add axes later on, allowing for more complex axes layouts. +# It is often convenient to create the axes together with the figure, but you +# can also manually add axes later on. Note that many +# :doc:`Matplotlib backends ` support zooming and +# panning on figure windows. # # :class:`~matplotlib.axes.Axes` # ------------------------------ # -# This is what you think of as 'a plot'. It is the region of the image -# with the data space. A given figure -# can contain many Axes, but a given :class:`~matplotlib.axes.Axes` -# object can only be in one :class:`~matplotlib.figure.Figure`. The -# Axes contains two (or three in the case of 3D) +# An Axes is an artist attached to a figure that contains a region for +# plotting data, and usually includes two (or three in the case of 3D) # :class:`~matplotlib.axis.Axis` objects (be aware of the difference -# between **Axes** and **Axis**) which take care of the data limits (the -# data limits can also be controlled via the :meth:`.axes.Axes.set_xlim` and -# :meth:`.axes.Axes.set_ylim` methods). Each :class:`~.axes.Axes` has a title +# between **Axes** and **Axis**) that provide ticks and tick labels to +# provide scales for the data in the Axes. Each :class:`~.axes.Axes` also +# has a title # (set via :meth:`~matplotlib.axes.Axes.set_title`), an x-label (set via # :meth:`~matplotlib.axes.Axes.set_xlabel`), and a y-label set via # :meth:`~matplotlib.axes.Axes.set_ylabel`). # -# The :class:`~.axes.Axes` class and its member functions are the primary entry -# point to working with the OO interface. +# The :class:`~.axes.Axes` class and its member functions are the primary +# entry point to working with the OO interface, and have most of the +# plotting methods defined on them (e.g. ``ax.plot()``, shown above, uses +# the `~.Axes.plot` method) # # :class:`~matplotlib.axis.Axis` # ------------------------------ # -# These are the objects most similar to a number line. -# They set graph limits and generate ticks (the marks -# on the axis) and ticklabels (strings labeling the ticks). The location of -# the ticks is determined by a `~matplotlib.ticker.Locator` object and the +# These objects set the scale and limits and generate ticks (the marks +# on the axis) and ticklabels (strings labeling the ticks). The location +# of the ticks is determined by a `~matplotlib.ticker.Locator` object and the # ticklabel strings are formatted by a `~matplotlib.ticker.Formatter`. The # combination of the correct `.Locator` and `.Formatter` gives very fine # control over the tick locations and labels. @@ -120,25 +99,40 @@ # Types of inputs to plotting functions # ===================================== # -# All of plotting functions expect `numpy.array` or `numpy.ma.masked_array` as -# input. Classes that are similar to arrays ('array-like') such as `pandas` +# Plotting functions expect `numpy.array` or `numpy.ma.masked_array` as +# input, or objects that can be passed to `numpy.asarray`. +# Classes that are similar to arrays ('array-like') such as `pandas` # data objects and `numpy.matrix` may not work as intended. Common convention # is to convert these to `numpy.array` objects prior to plotting. -# -# For example, to convert a `pandas.DataFrame` :: -# -# a = pandas.DataFrame(np.random.rand(4, 5), columns = list('abcde')) -# a_asarray = a.values -# -# and to convert a `numpy.matrix` :: +# For example, to convert a `numpy.matrix` :: # # b = np.matrix([[1, 2], [3, 4]]) # b_asarray = np.asarray(b) # +# Most methods will also parse an addressible object like a *dict*, a +# `numpy.recarray`, or a `pandas.DataFrame`. Matplotlib allows you provide +# the ``data`` keyword argument and generate plots passing the strings +# corresponding to the *x* and *y* variables. +np.random.seed(19680801) # seed the random number generator. +data = {'a': np.arange(50), + 'c': np.random.randint(0, 50, 50), + 'd': np.random.randn(50)} +data['b'] = data['a'] + 10 * np.random.randn(50) +data['d'] = np.abs(data['d']) * 100 + +fig, ax = plt.subplots(figsize=(5, 2.7), constrained_layout=True) +ax.scatter('a', 'b', c='c', s='d', data=data) +ax.set_xlabel('entry a') +ax.set_ylabel('entry b') + +############################################################################## # .. _coding_styles: # -# The object-oriented interface and the pyplot interface -# ====================================================== +# Coding styles +# ============= + +# The object-oriented and the pyplot interfaces +# --------------------------------------------- # # As noted above, there are essentially two ways to use Matplotlib: # @@ -147,12 +141,12 @@ # - Rely on pyplot to automatically create and manage the figures and axes, and # use pyplot functions for plotting. # -# So one can do (OO-style) +# So one can use the OO-style x = np.linspace(0, 2, 100) # Sample data. # Note that even in the OO-style, we use `.pyplot.figure` to create the figure. -fig, ax = plt.subplots() # Create a figure and an axes. +fig, ax = plt.subplots(figsize=(5, 2.7), constrained_layout=True) ax.plot(x, x, label='linear') # Plot some data on the axes. ax.plot(x, x**2, label='quadratic') # Plot more data on the axes... ax.plot(x, x**3, label='cubic') # ... and some more. @@ -162,10 +156,11 @@ ax.legend() # Add a legend. ############################################################################### -# or (pyplot-style) +# or the pyplot-style: x = np.linspace(0, 2, 100) # Sample data. +plt.figure(figsize=(5, 2.7), constrained_layout=True) plt.plot(x, x, label='linear') # Plot some data on the (implicit) axes. plt.plot(x, x**2, label='quadratic') # etc. plt.plot(x, x**3, label='cubic') @@ -175,77 +170,373 @@ plt.legend() ############################################################################### -# In addition, there is a third approach, for the case when embedding +# (In addition, there is a third approach, for the case when embedding # Matplotlib in a GUI application, which completely drops pyplot, even for -# figure creation. We won't discuss it here; see the corresponding section in -# the gallery for more info (:ref:`user_interfaces`). +# figure creation. See the corresponding section in the gallery for more info +# (:ref:`user_interfaces`).) # # Matplotlib's documentation and examples use both the OO and the pyplot -# approaches (which are equally powerful), and you should feel free to use -# either (however, it is preferable to pick one of them and stick to it, instead -# of mixing them). In general, we suggest to restrict pyplot to interactive -# plotting (e.g., in a Jupyter notebook), and to prefer the OO-style for -# non-interactive plotting (in functions and scripts that are intended to be -# reused as part of a larger project). +# styles. In general, we suggest using the OO style, particularly for +# complicated plots, and functions and scripts that are intended to be reused +# as part of a larger project. However, the pyplot style can be very conveneient +# for quick interactive work. # # .. note:: # -# In older examples, you may find examples that instead used the so-called -# ``pylab`` interface, via ``from pylab import *``. This star-import -# imports everything both from pyplot and from :mod:`numpy`, so that one -# could do :: -# -# x = linspace(0, 2, 100) -# plot(x, x, label='linear') -# ... +# You may find older examples that use the ``pylab`` interface, +# via ``from pylab import *``. This approach is strongly deprecated. # -# for an even more MATLAB-like style. This approach is strongly discouraged -# nowadays and deprecated. It is only mentioned here because you may still -# encounter it in the wild. +# Making a helper functions +# ------------------------- # -# If you need to make the same plots over and over -# again with different data sets, use the recommended signature function below. +# If you need to make the same plots over and over again with different data sets, +# or want to easily wrap Matplotlib methods, use the recommended signature function +# below. def my_plotter(ax, data1, data2, param_dict): """ - A helper function to make a graph + A helper function to make a graph. + """ + out = ax.plot(data1, data2, **param_dict) + return out - Parameters - ---------- - ax : Axes - The axes to draw to +############################################################################### +# which you would then use twice to populate two subplots: - data1 : array - The x data +data1, data2, data3, data4 = np.random.randn(4, 100) # make 4 random data sets +xdata = np.arange(len(data1)) # make an ordinal for this +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(5, 2.7)) +my_plotter(ax1, data1, data2, {'marker': 'x'}) +my_plotter(ax2, data3, data4, {'marker': 'o'}) + +############################################################################### +# These examples provide convenience for more complex graphs. +# +# Note that if you want to install these as a python package, or any other +# customizations you could use use one of the many templates on the web; +# Matplotlib has one at `mpl-cookiecutter +# `_ +# +# +# Styling Artists +# =============== +# +# Most plotting methods have styling options for the Artists, accessible either +# when a plotting method is called, or from a "setter" on the artist. In the +# plot below we manaully set the *color*, *linewidth*, and *linestyle* of the +# artists created by `~.Axes.plot`, and we set the linestyle of the second line +# after the fact with `~.Line2D.set_linestyle`. - data2 : array - The y data +fig, ax = plt.subplots(figsize=(5, 2.7)) +x = np.arange(len(data1)) +ax.plot(x, np.cumsum(data1), color='blue', linewidth=3, linestyle='--') +l, = ax.plot(x, np.cumsum(data2), color='orange', linewidth=2) +l.set_linestyle(':') - param_dict : dict - Dictionary of keyword arguments to pass to ax.plot +############################################################################### +# Colors +# ------ +# +# Matplotlib has a very flexible array of colors that are accepted for most +# artists; see the :doc:`colors tutorial ` for a +# list of specifications. Some Artists will take multiple colors. i.e. for +# a `~.Axes.scatter` plot, the edge of the markers can be different colors +# from the interior: - Returns - ------- - out : list - list of artists added - """ - out = ax.plot(data1, data2, **param_dict) - return out +fig, ax = plt.subplots(figsize=(5, 2.7)) +x = np.arange(len(data1)) +ax.scatter(data1, data2, s=50, facecolor='C0', edgecolor='k') ############################################################################### -# which you would then use as: +# Linewidths, linestyles, and markersizes +# --------------------------------------- +# +# Line widths are typically in typographic points (1 pt = 1/72 inch) and +# available for artists that have stroked lines. Similarly, stroked lines +# can have a linestyle. See the :doc:`linestyles example +# `. +# +# Marker size depends on the method being used. `~.Axes.plot` specifies +# markersize in points, and is generally the "diameter" or width of the +# marker. `~.Axes.scatter` specifies markersize as approximately +# proportional to the visual area of the marker. There are also an array of +# markerstyles available as string codes (see :mod:`~.matplotlib.markers`) or +# users can define their own `~.MarkerStyle` (see +# :doc:`/gallery/lines_bars_and_markers/marker_reference`): -data1, data2, data3, data4 = np.random.randn(4, 100) -fig, ax = plt.subplots(1, 1) -my_plotter(ax, data1, data2, {'marker': 'x'}) +fig, ax = plt.subplots(figsize=(5, 2.7)) +ax.plot(data1, 'o', label='data1') +ax.plot(data2, 'd', label='data2') +ax.plot(data3, 'v', label='data3') +ax.plot(data4, 's', label='data4') +ax.legend() + +################################################################################ +# +# Labelling plots +# =============== +# +# Axes labels and text +# -------------------- +# +# `~.Axes.set_xlabel`, `~.Axes.set_ylabel` and `~.Axes.set_title` are used to +# add text in the indicated locations (see :doc:`/tutorials/text/text_intro` for +# more discussion). Text can also be directly added to plots using +# `~.Axes.text`: + +mu, sigma = 115, 15 +x = mu + sigma * np.random.randn(10000) +fig, ax = plt.subplots(figsize=(5, 2.7), constrained_layout=True) +# the histogram of the data +n, bins, patches = ax.hist(x, 50, density=1, facecolor='C0', alpha=0.75) + +ax.set_xlabel('Length [cm]') +ax.set_ylabel('Probability') +ax.set_title('Aardvark lengths\n (not really)') +ax.text(75, .025, r'$\mu=115,\ \sigma=15$') +ax.axis([55, 175, 0, 0.03]) +ax.grid(True) +plt.show() ############################################################################### -# or if you wanted to have two sub-plots: +# All of the `~.Axes.text` functions return a `matplotlib.text.Text` +# instance. Just as with lines above, you can customize the properties by +# passing keyword arguments into the text functions:: +# +# t = ax.set_xlabel('my data', fontsize=14, color='red') +# +# These properties are covered in more detail in +# :doc:`/tutorials/text/text_props`. +# +# Using mathematical expressions in text +# -------------------------------------- +# +# Matplotlib accepts TeX equation expressions in any text expression. +# For example to write the expression :math:`\sigma_i=15` in the title, +# you can write a TeX expression surrounded by dollar signs:: +# +# ax.set_title(r'$\sigma_i=15$') +# +# where the ``r`` preceding the title string signifies that the string is a +# *raw* string and not to treat backslashes as python escapes. +# Matplotlib has a built-in TeX expression parser and +# layout engine, and ships its own math fonts -- for details see +# :doc:`/tutorials/text/mathtext`. You can also use LaTeX directly to format +# your text and incorporate the output directly into your display figures or +# saved postscript -- see :doc:`/tutorials/text/usetex`. +# +# Annotations +# ----------- +# +# We can also annotate points on a plot, odten by connecting an arrow pointing +# to *xy*, to a piece of text at *xytext*: -fig, (ax1, ax2) = plt.subplots(1, 2) -my_plotter(ax1, data1, data2, {'marker': 'x'}) -my_plotter(ax2, data3, data4, {'marker': 'o'}) +fig, ax = plt.subplots(figsize=(5, 2.7)) + +t = np.arange(0.0, 5.0, 0.01) +s = np.cos(2*np.pi*t) +line, = ax.plot(t, s, lw=2) + +ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5), + arrowprops=dict(facecolor='black', shrink=0.05)) + +ax.set_ylim(-2, 2) ############################################################################### -# These examples provide convenience for more complex graphs. +# In this basic example, both *xy* and *xytext* are in data coordinates. +# There are a variety of other coordinate systems one can choose -- see +# :ref:`annotations-tutorial` and :ref:`plotting-guide-annotation` for +# details. More examples also can be found in +# :doc:`/gallery/text_labels_and_annotations/annotation_demo`. +# +# Legends +# ------- +# +# Often we want to identify lines or markers with a `.Axes.legend`: + +fig, ax = plt.subplots(figsize=(5, 2.7)) +ax.plot(np.arange(len(data1)), data1, label='data1') +ax.plot(np.arange(len(data2)), data2, label='data2') +ax.plot(np.arange(len(data3)), data3, 'd', label='data3') +ax.legend() + +############################################################################## +# Legends in Matplotlib are quite flexible in layout, placement, and what +# Artists they can represent. They are discussed in detail in +# :doc:`/tutorials/intermediate/legend_guide`. +# +# Axis scales and ticks +# ===================== +# +# Each Axes has two (or three) `~.axis.Axis` objects represnting the x- and y-axis. +# These control the *scale* of the axis, the tick *Locators* and the tick +# *Formatters*. +# +# Scales +# ------ +# +# In addition to the linear scale, Matplotlib supplies non-linear scales, +# such as a log-scale. Since log-scales are used so much there are also +# direct methods like `~.Axes.loglog`, `~.Axes.semilogx`, and +# `~.Axes.semilogy`. There are a number of scales (see +# :doc:`/gallery/scales/scales` for other examples). Here we set the scale +# manually: + +fig, axs = plt.subplots(1, 2, figsize=(5, 2.7), constrained_layout=True) +data = 10**data1 +axs[0].plot(xdata, data) + +axs[1].set_yscale('log') +axs[1].plot(xdata, data) + +############################################################################## +# The scale sets the mapping from data values to spacing along the Axis. This +# happens in both directions, and gets combined into a *transform*, which +# is the way that Matplotlib maps from data co-ordinates to Axes, Figure, or +# screen co-ordinates. See :doc:`/tutorials/advanced/transforms_tutorial`. +# +# Tick locators and formatters +# ---------------------------- +# +# Each Axis has a tick *locator* and *formatter* that choose where along the +# axes to put tick marks. A simple interface to this is `~.Axes.set_xticks`: + +fig, axs = plt.subplots(2, 1, constrained_layout=True) +axs[0].plot(xdata, data1) +axs[0].set_title('Automatic ticks') + +axs[1].plot(xdata, data1) +axs[1].set_xticks(np.arange(0, 100, 30), ['zero', '30', 'sixty', '90']) +axs[1].set_yticks([-1.5, 0, 1.5]) # note that we don't need to specify labels +axs[1].set_title('Manual ticks') + +############################################################################## +# Different scales can have different locators and formatters; for instance +# the log-scale above uses `~.LogLocator` and `~.LogFormatter`. See +# :doc:`/gallery/ticks/tick-locators` and +# :doc:`/gallery/ticks/tick-formatters` for other formatters and +# locators and information for writing your own. +# +# Plotting dates and strings +# -------------------------- +# +# Matplotlib can handle plotting arrays of dates and arrays of strings as +# well as floating point numbers. These get special locators and formatters +# as appropriate. For dates: + +fig, ax = plt.subplots(figsize=(5, 3.7), constrained_layout=True) +dates = np.arange(np.datetime64('2021-11-15'), np.datetime64('2021-12-25'), + np.timedelta64(1, 'h')) +data = np.cumsum(np.random.randn(len(dates))) +ax.plot(dates, data) + +############################################################################## +# For more information see the date examples +# (e.g. :doc:`/gallery/text_labels_and_annotations/date`) +# +# For strings, we get categorical plotting (see: +# :doc:`/gallery/lines_bars_and_markers/categorical_variables`). + +fig, ax = plt.subplots(figsize=(5, 2.7), constrained_layout=True) +categories = ['turnips', 'rutabega', 'cucumber', 'pumpkins'] + +ax.bar(categories, np.random.rand(len(categories))) + +############################################################################## +# One caveat about categorical plotting is that some methods of parsing +# text files return a list of strings, even if the strings all represent +# numbers or dates. If you pass 1000 strings Matplotlib will think you +# meant 1000 categories and will add 1000 ticks to your plot. +# +# Color mapped data +# ================= +# +# Often we want to have a third dimension in a plot represented by a colors in +# a colormap. Matplotlib has a number of plot types that do this: + +X, Y = np.meshgrid(np.linspace(-3, 3, 128), np.linspace(-3, 3, 128)) +Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2) + +fig, axs = plt.subplots(2, 2, constrained_layout=True) +pc = axs[0, 0].pcolormesh(X, Y, Z, vmin=-1, vmax=1, cmap='RdBu_r') +fig.colorbar(pc, ax=axs[0, 0]) +axs[0, 0].set_title('pcolormesh()') + +co = axs[0, 1].contourf(X, Y, Z, levels=np.linspace(-1.25, 1.25, 11)) +fig.colorbar(co, ax=axs[0, 1]) +axs[0, 1].set_title('contourf()') + +pc = axs[1, 0].imshow(Z**2 * 100, cmap='plasma', + norm=mpl.colors.LogNorm(vmin=0.01, vmax=100)) +fig.colorbar(pc, ax=axs[1, 0], extend='both') +axs[1, 0].set_title('imshow() with LogNorm()') + +pc = axs[1, 1].scatter(data1, data2, c=data3, cmap='RdBu_r') +fig.colorbar(pc, ax=axs[1, 1], extend='both') +axs[1, 1].set_title('scatter()') + +############################################################################## +# Colormaps +# --------- +# +# These are all examples of Artists that derive from `~.ScalarMappable` +# objects. They all can set a linear mapping between *vmin* and *vmax* into +# the colormap specified by *cmap*. Matplotlib has many colormaps to choose +# from (:doc:`/tutorials/colors/colormaps`) you can make your +# own (:doc:`/tutorials/colors/colormap-manipulation`) or download as +# `third-party packages +# `_. +# +# Normalizations +# -------------- +# +# Sometimes we want a non-linear mapping of the data to the colormap, as +# in the ``LogNorm`` example above. We do this by supplying the +# ScalarMappable with the *norm* argument instead of *vmin* and *vmax*. +# More normalizations are shown at :doc:`/tutorials/colors/colormapnorms`. +# +# Colorbars +# --------- +# +# Adding a `~.Figure.colorbar` gives a key to relate the color back to the +# underlying data. Colorbars are figure-level Artists, and are attached to +# a ScalarMappable (where they get their information about the norm and +# colormap) and usually steal space from a parent axes. Placement of +# colorbars can be complex: see +# :doc:`/gallery/subplots_axes_and_figures/colorbar_placement` for +# details. You can also change the appearance of colorbars with the +# *extend* keyword to add arrows to the ends, and *shrink* and *aspect* to +# control the size. +# +# +# Working with multiple figures and axes +# ====================================== +# +# You can open multiple figures with multiple calls to +# ``fig = plt.figure()`` or ``fig2, ax = plt.subplots()``. By keeping the +# object references you can add artists to either figure. +# +# Multiple axes can be added a number of ways, but the most basic is +# ``plt.subplots()`` as used above. One can achieve more complex layouts, +# with axes spanning columns or rows, using `~.pyplot.subplot_mosaic`. + +fig, axd = plt.subplot_mosaic([['upleft', 'right'], + ['lowleft', 'right']], constrained_layout=True) +axd['upleft'].set_title('upleft') +axd['lowleft'].set_title('lowleft') +axd['right'].set_title('right') +plt.show() + +############################################################################### +# Matplotlib has quite sophisticated tools for arranging axes: See +# :doc:`/tutorials/intermediate/arranging_axes` and +# :doc:`/tutorials/provisional/mosaic`. +# +# +# More reading +# ============ +# +# - For more plot types see :doc:`Plot types ` and the +# :doc:`API reference `, in particlar the +# :doc:`Axes API `.