Skip to content

Commit

Permalink
More improvements to documentation (#28)
Browse files Browse the repository at this point in the history
* Documentation updates

* Add what's new page
  • Loading branch information
spencerkclark authored Mar 7, 2019
1 parent 6f9a924 commit 1d692dc
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 75 deletions.
3 changes: 2 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'
# source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'
Expand Down Expand Up @@ -314,6 +314,7 @@
intersphinx_mapping = {
'python': ('https://docs.python.org/3/', None),
'numpy': ('https://docs.scipy.org/doc/numpy/', None),
'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None),
'matplotlib': ('https://matplotlib.org/', None),
'mpl_toolkits': ('https://matplotlib.org/', None)
}
26 changes: 16 additions & 10 deletions doc/examples.rst
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
Using faceted
=============

``faceted`` is quite flexible. Here are a couple of examples illustrating the
different features. Using it in many ways resembles using ``plt.subplots``.
:py:meth:`faceted.faceted` is quite flexible. Here are a couple of examples
illustrating the different features. Using it in many ways resembles using
:py:meth:`matplotlib.pyplot.subplots`.

.. ipython:: python
:okwarning:
import matplotlib.pyplot as plt
import xarray as xr
from matplotlib import ticker
from faceted import faceted
tick_locator = ticker.MaxNLocator(nbins=3)
ds = xr.tutorial.load_dataset('rasm').isel(x=slice(30, 37), y=-1,
time=slice(0, 11))
temp = ds.Tair
Expand All @@ -21,6 +25,7 @@ different features. Using it in many ways resembles using ``plt.subplots``.
ax.set_title('{:0.2f}'.format(temp.xc.isel(x=i).item()))
ax.set_xlabel('Time')
ax.set_ylabel('Temperature [C]')
ax.tick_params(axis='x', labelrotation=45)
@savefig example_tair_base.png
fig.show()
Expand All @@ -39,13 +44,14 @@ width.
.. ipython:: python
:okwarning:
fig, axes = faceted(2, 3, width=8, left_pad=0.75, bottom_pad=0.75,
fig, axes = faceted(2, 3, width=8, left_pad=0.75, bottom_pad=0.9,
internal_pad=(0.33, 0.66))
for i, ax in enumerate(axes):
temp.isel(x=i).plot(ax=ax, marker='o', ls='none')
ax.set_title('{:0.2f}'.format(temp.xc.isel(x=i).item()))
ax.set_xlabel('Time')
ax.set_ylabel('Temperature [C]')
ax.tick_params(axis='x', labelrotation=45)
@savefig example_tair_padding.png
fig.show()
Expand All @@ -69,7 +75,7 @@ from the time mean at each location in the lower row instead.
mean = (ds.Tair * time_weights).sum('time') / time_weights.where(np.isfinite(ds.Tair)).sum('time')
anomaly = ds.Tair - mean
fig, axes = faceted(2, 3, width=8, left_pad=0.75, bottom_pad=0.75,
fig, axes = faceted(2, 3, width=8, left_pad=0.75, bottom_pad=0.9,
internal_pad=(0.33, 0.66), sharey='row')
for i, ax in enumerate(axes[:3]):
temp.isel(x=i).plot(ax=ax, marker='o', ls='none')
Expand All @@ -82,6 +88,7 @@ from the time mean at each location in the lower row instead.
ax.set_title('{:0.2f}'.format(temp.xc.isel(x=i).item()))
ax.set_xlabel('Time')
ax.set_ylabel('Anomaly [C]')
ax.tick_params(axis='x', labelrotation=45)
@savefig example_tair_share_axes.png
fig.show()
Expand All @@ -90,13 +97,13 @@ Colorbar modes and locations
----------------------------

Let's say we are plotting 2D data in the form of pcolormesh plots that require
a colorbar. ``faceted`` comes with a number of options for placing and sizing
a colorbar. :py:meth:`faceted.faceted` comes with a number of options for placing and sizing
colorbars in a paneled figure. We can add a colorbar to a figure by modifying
the ``cbar_mode`` argument; by default it is set to ``None``, meaning no
colorbar, as in the plots above. For all of the examples here, we'll just plot
a time series of maps. Since the xarray tutorial data is geographic in nature,
we'll also use this opportunity to show how to use ``cartopy`` with
``faceted``.
we'll also use this opportunity to show how to use :py:mod:`cartopy` with
:py:meth:`faceted.faceted`.

Single colorbar
###############
Expand Down Expand Up @@ -178,12 +185,11 @@ Colorbars for each panel
########################

One more common use case is a colorbar for each panel. This can be done by
specifying ``cbar_mode='each'`` as an argument in the call to ``faceted``.
specifying ``cbar_mode='each'`` as an argument in the call to :py:meth:`faceted.faceted`.

.. ipython:: python
:okwarning:
from matplotlib import ticker
tick_locator = ticker.MaxNLocator(nbins=3)
aspect = 75. / 180.
Expand Down Expand Up @@ -212,7 +218,7 @@ specifying ``cbar_mode='each'`` as an argument in the call to ``faceted``.
Parameter defintions
--------------------

A full summary of the meanings of the different arguments to ``faceted`` can be
A full summary of the meanings of the different arguments to :py:meth:`faceted.faceted` can be
found here.

Parameters controlling figure and axes dimensions
Expand Down
2 changes: 2 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Currently the only option for installing ``faceted`` is from source::
Documentation
-------------

* :doc:`whats-new`
* :doc:`why-faceted`
* :doc:`examples`
* :doc:`api`
Expand All @@ -55,6 +56,7 @@ Documentation
:maxdepth: 1
:hidden:

whats-new
why-faceted
examples
api
14 changes: 14 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.. _whats-new:

##########
What's New
##########

.. _whats-new.0.1:

v0.1 (unreleased)
=================

- Initial release. Note the name has changed since the development version from
``facets`` to ``faceted``.
- Default ``cbar_short_side_pad`` is now 0.0 instead of 0.5 inches.
108 changes: 60 additions & 48 deletions doc/why-faceted.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ Why faceted?
============

At first glance it might seem we are re-inventing the wheel here. If you just
google for "matplotlib subplots with shared colorbar" you'll find a
StackOverflow question with numerous answers with varying levels of
google for "matplotlib subplots with shared colorbar" you'll find `a
StackOverflow question
<https://stackoverflow.com/questions/13784201/matplotlib-2-subplots-1-colorbar>`_
with numerous answers with varying levels of
complexity (some in fact are quite elegant). It might be tempting to go with
one of these solutions, e.g.

Expand All @@ -27,10 +29,10 @@ one of these solutions, e.g.
This looks ok, but things become a bit more challenging when we'd like to
have a more control over the spacing and size of elements in the figure.
``matplotlib`` is super-flexible in that it is indeed *possible* to do this,
but if your starting point for creating paneled figures is ``plt.subplots``,
which it so often is for many of us, your options for exerting this type of
control are somewhat tricky to use.
:py:mod:`matplotlib` is super-flexible in that it is indeed *possible* to do this,
but if your starting point for creating paneled figures is
:py:meth:`matplotlib.pyplot.subplots`, which it so often is for many of us,
your options for exerting this type of control are somewhat tricky to use.

Let's take the example above and start to impose some contraints:

Expand All @@ -41,10 +43,10 @@ Let's take the example above and start to impose some contraints:
an inch. This thickness *should not* depend on the overall dimensions of the
figure.
- The data we are plotting is geographic in nature; we really should be using
``cartopy``, which will require that the panels have a strict aspect ratio,
:py:mod:`cartopy`, which will require that the panels have a strict aspect ratio,
related to the extent of the domain in latitude-longitude space. Currently
the aspect ratio is set dynamically based on the total figure size and
``matplotlib`` defaults for between-plot spacing and outer padding.
:py:mod:`matplotlib` defaults for between-plot spacing and outer padding.

One by one we'll go through these illustrating how much complexity this adds to
our code just to produce a simple figure.
Expand All @@ -54,16 +56,16 @@ Fixing the between-plot spacing

As soon as we try to assign a certain amount of physical space to a plot
element, we need to do some algebra. This is because to change the panel
spacing after a call to ``plt.subplots``, we need to use
``plt.subplots_adjust``, which takes parameters representing an amount of
spacing after a call to :py:meth:`matplotlib.pyplot.subplots`, we need to use
:py:meth:`matplotlib.pyplot.subplots_adjust`, which takes parameters representing an amount of
*relative* space, meaning expressed as a fraction of a plot element, be it the
whole figure or a single panel.

To help set up the problem, let's define some variables. First,
let's say that we have :math:`m` rows of :math:`n` panels each; in our example
:math:`m = 1` and :math:`n = 3`. Then let's say that we would like to
introduce an internal pad, :math:`p_{internal}`, representing the spacing
between the axes in inches. In order to use ``plt.subplots_adjust``, we need
between the axes in inches. In order to use :py:meth:`matplotlib.pyplot.subplots_adjust`, we need
to determine the amount of relative space :math:`p_{internal}` represents. In
the context of the ``wspace`` parameter, the parameter that controls the
spacing between panels, we need to determine the ratio of the width of the
Expand All @@ -75,14 +77,14 @@ a figure of width :math:`w`, with outer left and right paddings of
w_{panel} = \frac{w - p_{left} - p_{right} - (n - 1) p_{internal}}{n}.
Therefore the value we pass to ``wspace`` in ``plt.subplots_adjust`` is:
Therefore the value we pass to ``wspace`` in :py:meth:`matplotlib.pyplot.subplots_adjust` is:

.. math::
\texttt{wspace} = \frac{p_{internal}}{w_{panel}}.
Finally, since in this process we needed to fix the left and right pads of the
figure, we need to specify those in ``plt.subplots_adjust`` too; note these are
figure, we need to specify those in :py:meth:`matplotlib.pyplot.subplots_adjust` too; note these are
defined relative to the full figure width rather than the width of single panel:

.. math::
Expand Down Expand Up @@ -142,22 +144,24 @@ colorbar and the panels, :math:`p_{cbar}`, a thickness for the colorbar,
w_cbar = 0.125
h = 4.
The top and bottom pads need to be passed to ``plt.subplots_adjust`` and they
The top and bottom pads need to be passed to
:py:meth:`matplotlib.pyplot.subplots_adjust` and they
follow similar conventions to the left and right pads, i.e. they are defined in
terms of length relative to the overall height of the figure. The size of the
colorbar is controlled differently; we control its size when we construct it
using ``plt.colorbar``, using the ``fraction``, ``pad``, and ``aspect``
arguments. ``fraction`` dictates the fraction of the height of the colorbar
would take with respect to the height of a single panel in the *original*
figure; ``pad`` dictates the fraction of a single panel in the *original*
figure the padding between the colorbar and panels would take; and ``aspect``
sets the ratio of the width of the long part of the colorbar to its thickness.
Note that since we call ``plt.subplots_adjust`` before calling
``plt.colorbar``, the panel height in the original figure is determined in part
by our imposed :math:`p_{top}` and :math:`p_{bottom}`. In this case since we
are only using a single row of panels, we do not need to worry about the
between panel spacing in this dimension, but we'll include the
:math:`p_{internal}` term to keep things general:
using :py:meth:`matplotlib.pyplot.colorbar`, using the ``fraction``, ``pad``,
and ``aspect`` arguments. ``fraction`` dictates the fraction of the height of
the colorbar would take with respect to the height of a single panel in the
*original* figure; ``pad`` dictates the fraction of a single panel in the
*original* figure the padding between the colorbar and panels would take; and
``aspect`` sets the ratio of the width of the long part of the colorbar to its
thickness. Note that since we call :py:meth:`matplotlib.pyplot.subplots_adjust`
before calling :py:meth:`matplotlib.pyplot.colorbar`, the panel height in the
original figure is determined in part by our imposed :math:`p_{top}` and
:math:`p_{bottom}`. In this case since we are only using a single row of
panels, we do not need to worry about the between panel spacing in this
dimension, but we'll include the :math:`p_{internal}` term to keep things
general:

.. math::
Expand Down Expand Up @@ -202,11 +206,13 @@ Holding panels at a fixed aspect ratio
--------------------------------------

Things are starting to look much better, but there's still more work to do.
Let's introduce ``cartopy`` to the mix. Adding a ``cartopy`` projection turns
Let's introduce :py:mod:`cartopy` to the mix. Adding a :py:mod:`cartopy`
projection turns
out to fix the aspect ratio of the panels in the figure, regardless of the
figure size. We'll want to address this additional constraint by adjusting our
value for the total height of the figure, because the panel height will now by
completely determined by the panel width. In a ``PlateCarree`` projection, the
completely determined by the panel width. In a
:py:class:`cartopy.crs.PlateCarree` projection, the
aspect ratio will be determined by the ratio of the latitudinal extent of the
map divided by the longitudinal extent. In this case it will be
:math:`\texttt{aspect} = \frac{75}{360}`. :math:`h_{panel}` will now be
Expand All @@ -225,12 +231,13 @@ elements:
h = m h_{panel} + (m - 1) p_{internal} + p_{bottom} + p_{top} + p_{cbar} + w_{cbar}.
As a result of the height values changing, we'll need to update the ``bottom`` and
``top`` parameters for ``plt.subplots_adjust`` as well as the colorbar size
parameters:
``top`` parameters for :py:meth:`matplotlib.pyplot.subplots_adjust` as well as
the colorbar size parameters:

.. ipython:: python
a = 75. / 360.
p_cbar = 0.25
h_panel = a * w_panel
h = p_bottom + p_top + h_panel + p_cbar + w_cbar
h_panel_original = h - p_top - p_bottom
Expand Down Expand Up @@ -264,24 +271,26 @@ parameters:
As examples go, this one was actually fairly simple; we only had one row of
panels, rather than multiple, and we only had one colorbar. Taking the
``plt.subplots`` approach was remarkably complicated. Admittedly, it would be
*slightly* more straightforward to use the ``AxesGrid`` framework to do this,
but other problems remain with that approach; e.g. using ``AxesGrid`` with
:py:meth:`matplotlib.pyplot.subplots` approach was remarkably complicated.
Admittedly, it would be
*slightly* more straightforward to use the :py:class:`mpl_toolkits.axes_grid1.AxesGrid` framework to do this,
but other problems remain with that approach; e.g. using :py:class:`mpl_toolkits.axes_grid1.AxesGrid` with
cartopy is not ideal due to axes sharing issues (`SciTools/cartopy#939
<https://github.com/SciTools/cartopy/issues/939>`_), and colorbars drawn using
``AxesGrid`` are drawn using an outdated colorbar class in ``matplotlib``,
:py:class:`mpl_toolkits.axes_grid1.AxesGrid` are drawn using an outdated colorbar class in :py:mod:`matplotlib`,
which is different than the one used by default (`matplotlib/matplotlib#9778
<https://github.com/matplotlib/matplotlib/issues/9778>`_). In ``faceted`` we use
``AxesGrid`` to aid in the placing the axes and colorbars, but we do not use
the axes generated by it. Instead we create our own, which are modern and have
working axes-sharing capabilities. In so doing we create a
``plt.subplots``-like interface, which is slightly more intuitive to use than
``AxesGrid``.
<https://github.com/matplotlib/matplotlib/issues/9778>`_). In
:py:meth:`faceted.faceted` we use :py:class:`mpl_toolkits.axes_grid1.AxesGrid` to aid in the placing the axes
and colorbars (some math is still required to determine the figure height), but
we do not use the axes generated by it. Instead we create our own,
which are modern and have working axes-sharing capabilities. In so doing we
create a :py:meth:`matplotlib.pyplot.subplots`-like interface, which is
slightly more intuitive to use than :py:class:`mpl_toolkits.axes_grid1.AxesGrid`.

How would you do this in faceted?
---------------------------------

In ``faceted`` this becomes much simpler; there is no need to do any algebra
In :py:meth:`faceted.faceted` this becomes much simpler; there is no need to do any algebra
or post-hoc adjustment of the axes placement; everything gets handled in the
top-level function.

Expand Down Expand Up @@ -313,11 +322,14 @@ top-level function.
What can't you do in faceted?
-----------------------------

The main thing that ``faceted`` cannot do is create a constrained set of axes
The main thing that :py:meth:`faceted.faceted` cannot do is create a
constrained set of axes
that have varying size, or varying properties. For more complex figure
construction tasks we recommend using a more fundamental ``matplotlib``
approach, either using ``AxesGrid``, ``GridSpec``, or ``ConstrainedLayout``.
The main reason for creating ``faceted`` was that these other tools were *too*
flexible at the expense of simplicity. For a large percentage of the use
cases, they are not required, but for the remaining percentage they are indeed
quite useful.
construction tasks we recommend using a more fundamental :py:mod:`matplotlib`
approach, either using :py:class:`mpl_toolkits.axes_grid1.AxesGrid`,
:py:class:`matplotlib.GridSpec`, or `Constrained Layout
<https://matplotlib.org/tutorials/intermediate/constrainedlayout_guide.html#>`_. The
main reason for creating :py:meth:`faceted.faceted` was that these other tools
were *too* flexible at the expense of simplicity. For a large percentage of
the use cases, they are not required, but for the remaining percentage they are
indeed quite useful.
2 changes: 1 addition & 1 deletion faceted/faceted.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

def faceted(rows, cols, width=8., aspect=0.618, top_pad=0.25,
bottom_pad=0.25, left_pad=0.25, right_pad=0.25, internal_pad=0.33,
cbar_mode=None, cbar_short_side_pad=0.5, cbar_pad=0.5,
cbar_mode=None, cbar_short_side_pad=0.0, cbar_pad=0.5,
cbar_size=0.125, cbar_location='right', sharex='all', sharey='all',
axes_kwargs={}):
"""Create figure and tiled axes objects with precise attributes.
Expand Down
Loading

0 comments on commit 1d692dc

Please sign in to comment.