Python and MatPlotLib Axes and SubPlots

This page is obsolete, I wrote it while I was just starting to learn python. I have wrote a much better set of notes on pandas available here:

Prequisites

Python

Data

Let's recreate the dataframe which we looked at then we looked at bar graphs.

Python

We can view the rainfalldf dataframe in variable explorer:

As we can see, this gives the rainfall per month up until May 2019.

Creating a Plot (Simple Bar Graph)

Python

Tight Layout

As we can see, the bottom of the xlabel is cut from the plot. This can be solved by using the tight layout.

Python

Assigning a Figure, Axes and Plot to Variables

Let's have a look at recreating the plot above but in this case we will go through it step by step and assign the figure, axes and plot to variable names.

Figure

Python

We close all plots (line 24) and then create figure(1) using the command figure (line 25) which creates a blank figure canvas. This blank canvas can be assigned to a variable, in this case fig1 which we can later access.

We can now have a look at the properties of the figure using getp in the command window

Python
agg_filter = None
    alpha = None
    animated = False
    axes = []
    children = [<matplotlib.patches.Rectangle object at 0x0000022...
    clip_box = None
    clip_on = True
    clip_path = None
    constrained_layout = False
    constrained_layout_pads = (0.04167, 0.04167, 0.02, 0.02)
    contains = None
    default_bbox_extra_artists = []
    dpi = 100.0
    edgecolor = (1.0, 1.0, 1.0, 1.0)
    facecolor = (1.0, 1.0, 1.0, 1.0)
    figheight = 4.8
    figure = None
    figwidth = 6.4
    frameon = True
    gid = None
    in_layout = True
    label = 
    path_effects = []
    picker = None
    rasterized = None
    size_inches = [6.4 4.8]
    sketch_params = None
    snap = None
    tight_layout = False
    transform = IdentityTransform()
    transformed_clip_path_and_affine = (None, None)
    url = None
    visible = True
    window_extent = TransformedBbox(     Bbox(x0=0.0, y0=0.0, x1=6.4, ...
    zorder = 0

Axes

As we can see the axes are empty. We can then select this plot and add a set of axes to it (line 27) and save these axes to a variable ax1. For example by adding a single subplot which occupies the figure.

Python
Python

Note the first digit m=1 corresponds to the number of rows of subplots, the second digit n=1 corresponds to the number of columns of subplots and the third digit o=1 selects the subplot to plot from. The number of subplots ranges from 1 to m×n in this case 1×1. Note zero order indexing does not apply here.

This gives a default single plot which has an axes which covers the 1st and only subplot occupying the figure canvas. As no data is present in the axes, the default axes limits in x and y will be [0,1]. These will rescale to suit data, when data is plotted. We can get the properties of the axes from the command line

Python
adjustable = box
    agg_filter = None
    alpha = None
    anchor = C
    animated = False
    aspect = auto
    autoscale_on = True
    autoscalex_on = True
    autoscaley_on = True
    axes_locator = None
    axisbelow = line
    children = [<matplotlib.spines.Spine object at 0x0000022DA29D...
    clip_box = None
    clip_on = True
    clip_path = None
    contains = None
    data_ratio = 1.0
    default_bbox_extra_artists = [<matplotlib.spines.Spine object at 0x0000022DA29D...
    facecolor = (1.0, 1.0, 1.0, 1.0)
    fc = (1.0, 1.0, 1.0, 1.0)
    figure = Figure(640x480)
    frame_on = True
    geometry = (1, 1, 1)
    gid = None
    gridspec = GridSpec(1, 1)
    images = <a list of 0 AxesImage objects>
    in_layout = True
    label = 
    legend = None
    legend_handles_labels = ([], [])
    lines = <a list of 0 Line2D objects>
    navigate = True
    navigate_mode = None
    path_effects = []
    picker = None
    position = Bbox(x0=0.125, y0=0.10999999999999999, x1=0.9, y1=...
    rasterization_zorder = None
    rasterized = None
    renderer_cache = None
    shared_x_axes = <matplotlib.cbook.Grouper object at 0x0000022D9E98...
    shared_y_axes = <matplotlib.cbook.Grouper object at 0x0000022D9E9C...
    sketch_params = None
    snap = None
    subplotspec = <matplotlib.gridspec.SubplotSpec object at 0x00000...
    title = 
    transform = IdentityTransform()
    transformed_clip_path_and_affine = (None, None)
    url = None
    visible = True
    window_extent = Bbox(x0=76.5, y0=49.3, x1=579.5, y1=425.9)
    xaxis = XAxis(80.000000,52.800000)
    xaxis_transform = BlendedGenericTransform(     CompositeGenericTrans...
    xbound = (0.0, 1.0)
    xgridlines = <a list of 6 Line2D xgridline objects>
    xlabel = 
    xlim = (0.0, 1.0)
    xmajorticklabels = <a list of 6 Text xticklabel objects>
    xminorticklabels = <a list of 0 Text xticklabel objects>
    xscale = linear
    xticklabels = <a list of 6 Text xticklabel objects>
    xticklines = <a list of 12 Line2D xtickline objects>
    xticks = [0.  0.2 0.4 0.6 0.8 1. ]
    yaxis = YAxis(80.000000,52.800000)
    yaxis_transform = BlendedGenericTransform(     BboxTransformTo(     ...
    ybound = (0.0, 1.0)
    ygridlines = <a list of 6 Line2D ygridline objects>
    ylabel = 
    ylim = (0.0, 1.0)
    ymajorticklabels = <a list of 6 Text yticklabel objects>
    yminorticklabels = <a list of 0 Text yticklabel objects>
    yscale = linear
    yticklabels = <a list of 6 Text yticklabel objects>
    yticklines = <a list of 12 Line2D ytickline objects>
    yticks = [0.  0.2 0.4 0.6 0.8 1. ]
    zorder = 0

Since we have just created a single subplot, using the command plt.axes will give the same result.

Python

Once again we can look at the axes properties using the command line:

Python
 adjustable = box
    agg_filter = None
    alpha = None
    anchor = C
    animated = False
    aspect = auto
    autoscale_on = True
    autoscalex_on = True
    autoscaley_on = True
    axes_locator = None
    axisbelow = line
    children = [<matplotlib.spines.Spine object at 0x000001EEB5F0...
    clip_box = None
    clip_on = True
    clip_path = None
    contains = None
    data_ratio = 1.0
    default_bbox_extra_artists = [<matplotlib.spines.Spine object at 0x000001EEB5F0...
    facecolor = (1.0, 1.0, 1.0, 1.0)
    fc = (1.0, 1.0, 1.0, 1.0)
    figure = Figure(640x480)
    frame_on = True
    geometry = (1, 1, 1)
    gid = None
    gridspec = GridSpec(1, 1)
    images = <a list of 0 AxesImage objects>
    in_layout = True
    label = 
    legend = None
    legend_handles_labels = ([], [])
    lines = <a list of 0 Line2D objects>
    navigate = True
    navigate_mode = None
    path_effects = []
    picker = None
    position = Bbox(x0=0.125, y0=0.10999999999999999, x1=0.9, y1=...
    rasterization_zorder = None
    rasterized = None
    renderer_cache = None
    shared_x_axes = <matplotlib.cbook.Grouper object at 0x000001EEB38D...
    shared_y_axes = <matplotlib.cbook.Grouper object at 0x000001EEB391...
    sketch_params = None
    snap = None
    subplotspec = <matplotlib.gridspec.SubplotSpec object at 0x00000...
    title = 
    transform = IdentityTransform()
    transformed_clip_path_and_affine = (None, None)
    url = None
    visible = True
    window_extent = Bbox(x0=76.5, y0=49.3, x1=579.5, y1=425.9)
    xaxis = XAxis(80.000000,52.800000)
    xaxis_transform = BlendedGenericTransform(     CompositeGenericTrans...
    xbound = (0.0, 1.0)
    xgridlines = <a list of 6 Line2D xgridline objects>
    xlabel = 
    xlim = (0.0, 1.0)
    xmajorticklabels = <a list of 6 Text xticklabel objects>
    xminorticklabels = <a list of 0 Text xticklabel objects>
    xscale = linear
    xticklabels = <a list of 6 Text xticklabel objects>
    xticklines = <a list of 12 Line2D xtickline objects>
    xticks = [0.  0.2 0.4 0.6 0.8 1. ]
    yaxis = YAxis(80.000000,52.800000)
    yaxis_transform = BlendedGenericTransform(     BboxTransformTo(     ...
    ybound = (0.0, 1.0)
    ygridlines = <a list of 6 Line2D ygridline objects>
    ylabel = 
    ylim = (0.0, 1.0)
    ymajorticklabels = <a list of 6 Text yticklabel objects>
    yminorticklabels = <a list of 0 Text yticklabel objects>
    yscale = linear
    yticklabels = <a list of 6 Text yticklabel objects>
    yticklines = <a list of 12 Line2D ytickline objects>
    yticks = [0.  0.2 0.4 0.6 0.8 1. ]
    zorder = 0

Figure and Axes Combined

Line 25 and 27 can be combined to a single line using the command subplots. Here two output arguments are defined fig1 and ax1

Python
Python
fig1=
    agg_filter = None
    alpha = None
    animated = False
    axes = [<matplotlib.axes._subplots.AxesSubplot object at ...
    children = [<matplotlib.patches.Rectangle object at 0x0000020...
    clip_box = None
    clip_on = True
    clip_path = None
    constrained_layout = False
    constrained_layout_pads = (0.04167, 0.04167, 0.02, 0.02)
    contains = None
    default_bbox_extra_artists = [<matplotlib.axes._subplots.AxesSubplot object at ...
    dpi = 100.0
    edgecolor = (1.0, 1.0, 1.0, 1.0)
    facecolor = (1.0, 1.0, 1.0, 1.0)
    figheight = 4.8
    figure = None
    figwidth = 6.4
    frameon = True
    gid = None
    in_layout = True
    label = 
    path_effects = []
    picker = None
    rasterized = None
    size_inches = [6.4 4.8]
    sketch_params = None
    snap = None
    tight_layout = False
    transform = IdentityTransform()
    transformed_clip_path_and_affine = (None, None)
    url = None
    visible = True
    window_extent = TransformedBbox(     Bbox(x0=0.0, y0=0.0, x1=6.4, ...
    zorder = 0
ax1=
    adjustable = box
    agg_filter = None
    alpha = None
    anchor = C
    animated = False
    aspect = auto
    autoscale_on = True
    autoscalex_on = True
    autoscaley_on = True
    axes_locator = None
    axisbelow = line
    children = [<matplotlib.spines.Spine object at 0x000002090B35...
    clip_box = None
    clip_on = True
    clip_path = None
    contains = None
    data_ratio = 1.0
    default_bbox_extra_artists = [<matplotlib.spines.Spine object at 0x000002090B35...
    facecolor = (1.0, 1.0, 1.0, 1.0)
    fc = (1.0, 1.0, 1.0, 1.0)
    figure = Figure(640x480)
    frame_on = True
    geometry = (1, 1, 1)
    gid = None
    gridspec = GridSpec(1, 1)
    images = <a list of 0 AxesImage objects>
    in_layout = True
    label = 
    legend = None
    legend_handles_labels = ([], [])
    lines = <a list of 0 Line2D objects>
    navigate = True
    navigate_mode = None
    path_effects = []
    picker = None
    position = Bbox(x0=0.125, y0=0.10999999999999999, x1=0.9, y1=...
    rasterization_zorder = None
    rasterized = None
    renderer_cache = None
    shared_x_axes = <matplotlib.cbook.Grouper object at 0x0000020904D7...
    shared_y_axes = <matplotlib.cbook.Grouper object at 0x0000020904DC...
    sketch_params = None
    snap = None
    subplotspec = <matplotlib.gridspec.SubplotSpec object at 0x00000...
    title = 
    transform = IdentityTransform()
    transformed_clip_path_and_affine = (None, None)
    url = None
    visible = True
    window_extent = TransformedBbox(     Bbox(x0=0.125, y0=0.109999999...
    xaxis = XAxis(80.000000,52.800000)
    xaxis_transform = BlendedGenericTransform(     CompositeGenericTrans...
    xbound = (0.0, 1.0)
    xgridlines = <a list of 6 Line2D xgridline objects>
    xlabel = 
    xlim = (0.0, 1.0)
    xmajorticklabels = <a list of 6 Text xticklabel objects>
    xminorticklabels = <a list of 0 Text xticklabel objects>
    xscale = linear
    xticklabels = <a list of 6 Text xticklabel objects>
    xticklines = <a list of 12 Line2D xtickline objects>
    xticks = [0.  0.2 0.4 0.6 0.8 1. ]
    yaxis = YAxis(80.000000,52.800000)
    yaxis_transform = BlendedGenericTransform(     BboxTransformTo(     ...
    ybound = (0.0, 1.0)
    ygridlines = <a list of 6 Line2D ygridline objects>
    ylabel = 
    ylim = (0.0, 1.0)
    ymajorticklabels = <a list of 6 Text yticklabel objects>
    yminorticklabels = <a list of 0 Text yticklabel objects>
    yscale = linear
    yticklabels = <a list of 6 Text yticklabel objects>
    yticklines = <a list of 12 Line2D ytickline objects>
    yticks = [0.  0.2 0.4 0.6 0.8 1. ]
    zorder = 0

Finally leaving the input arguments blank in line 26 will default to nrow=1, ncol=1 and num=1.

Python

Plot

Now the plot can be assigned to the axes.

Python

We can also look up the properties of plot1

Python
    agg_filter = None
    alpha = None
    animated = False
    antialiased or aa = True
    bbox = Bbox(x0=-0.4, y0=0.0, x1=0.4, y1=215.6)
    capstyle = butt
    children = []
    clip_box = TransformedBbox(     Bbox(x0=0.0, y0=0.0, x1=1.0, ...
    clip_on = True
    clip_path = None
    contains = None
    data_transform = CompositeGenericTransform(     TransformWrapper(  ...
    edgecolor or ec = (0.0, 0.0, 0.0, 1.0)
    extents = Bbox(x0=95.969696969697, y0=74.28416860022952, x1=...
    facecolor or fc = (0.0, 0.6901960784313725, 0.3137254901960784, 0.8)
    figure = Figure(640x478)
    fill = True
    gid = None
    hatch = O
    height = 215.6
    in_layout = True
    joinstyle = miter
    label = _nolegend_
    linestyle or ls = solid
    linewidth or lw = 1.2
    patch_transform = CompositeGenericTransform(     BboxTransformTo(   ...
    path = Path(array([[0., 0.],        [1., 0.],        [1.,...
    path_effects = []
    picker = None
    rasterized = None
    sketch_params = None
    snap = None
    transform = CompositeGenericTransform(     CompositeGenericTra...
    transformed_clip_path_and_affine = (None, None)
    url = None
    verts = [[ 95.96969697  74.2841686 ]  [130.12823147  74.28...
    visible = True
    width = 0.8
    window_extent = Bbox(x0=95.969696969697, y0=74.28416860022952, x1=...
    x = -0.4
    xy = (-0.4, 0)
    y = 0
    zorder = 1

Setting XTicks and XLabels

In the past we used the command which set the xticks for the currently selected axes.

Python

However it is also possible to explicitly select the axes and alter the xticks of the axes. For example if in the console we type in ax1. then tab we will get a list of commands we can index into ax1. Many of the commands will begin with get which can be used to view current properties and set which can be used to set new properties.

Note the input arguments for plt.xticks and ax1.set_xticks are slightly different with ax1.set_xticks being more restrictive and requiring the additional use of ax1.set_xticklabels to get all the options presented in plt.xticks.

Before ax1 xticks are set, the xticks are equally spaced from 0 to 12 in steps of 2.

We can set them to be equally spaced from 0 to 12 in steps of 1 (line 31).

Python
Python

Setting Axes Titles

Likewise the xlabel, ylabel and title can be set using the same convention.

Python

Legend

The legend can also be added by indexing into ax1.

Python

Grid

The grid can also be added by indexing into ax1.

Python

Tight Layout

The tight layout is assigned to the figure and not the axes. It can be assigned to the figure by indexing into fig1

Python

The steps above hae reproduced the figure above but have assigned the figure, axes and plot to fig1, ax1 and plot1 respectively and the properties of the zxes have been altered mainly by use of indexing into ax1 opposed to using plt which indexes into the currently selected axes. In this case, since we only have one set of axes it appears to yield the same result however some of these commands are required for plots with more complicated axes types which we will look at next and some of these commands are required when looking at 3D plots.

Moving Y-Axes to the Right

To move the y-axes to the right we have to index into ax1 and select the yaxis and then set the location to the right (line 35)

Python

In this case, the label still remains on the left, we may also wish to move this to the right by setting the label position (line 36):

Python

Moving X Axes to the Top

We can likewise move the x-axes to the top (line 38 and 39)

Python

Two Y-Axes

In some case, one may want two different y-axes for a dataset for example if one wants to display the same set of data using two different measurement systems. This can be done by creating a second set of axes from ax1 using the function twinx (line 35).

Python

Because no data is added to these second axes, they will range between the default values of 0 and 1. Data can be added to these axes by creating another plot and the axes will automatically adjust. However in this case, we want the right axes to relate to the original data, displaying it in inches instead of mm.

To do this we can index into ax1 and get the ylimits (line 39) saving the lower and upper bound to variables. We can then index into ax2 and set the ylimits to match these lower and upper bounds divided by the scaling factor 2.54 to get the limits in inches (line 41) and create a new ylabel for these second axes (line 42).

Python

Two X-Axes and Two Y-Axes

An additional top x-axis can be made likewise by using the function twiny (lines 47-55). In this case we can show the value of the numeric months at the top and show the 3 letter abbreviations at the bottom x-axis.

Python

Creating a Plot with SubPlots

Now let's have a look at the use of subplots.

2 by 1 Subplots

Say we want instead of a single subplot, a figure with 2 subplots (2 rows and 1 column). We can use the code above and modify line 27 for 2 subplots. On the left side we must designate the output arguments, the 0th output argument will be the figure as before. The 1st order argument will be an array of axes. In our case we can call these ax1 and ax2. Our naming convention will use 1st order indexing opposed to 0th order indexing as subplots are named in accordance to 1st order indexing. Here we set the nrows=2, ncols=1 and for the third input argument we can set num=1 (subplot1 corresponding to ax1). We could also set num=2 (subplot2 corresponding to ax2) for the third input argument. The third argument will just designate the current axes selected (for instance if one uses the plt command).

Python

We can now carry out a plot. Let's have a look at:

Python

We could also select ax2

Python

Or alternatively create a plot with two bar graphs for example, 2016 data and 2017 data.

Python

Once again we can change the properties of ax1.

Python

And we can do the same for ax2.

Python

2 by 2 Subplots

We can create more complicated subplots such as a 2 rows by 2 columns subplot.

Python

We can select any of the four axes to plot to:

Python
Python
Python
Python
Python

We can create a plot of 2016, 2017, 2018 and 2019 rainfall using these four subplots:

Python

Height and Width ratios

It is also possible to use gridspec and then to use height_ratios and/or width_ratios to make some subplots larger than others.

Python

Grid Specification

Sometimes we may wish to create subplots of different sizes. For example a figure with 3 plots and we may wish for the third subplot to occupy the space of the 3rd and 4th subplot i.e. be twice as wide as subplot1 and subplot2. This is done by using gridspec (line 27). In this case the gridspec is set to a 2 by 2 matrix. Each subplot is created by using the command add_subplot with an input argument indexing into the created gridspec and then assigned to a axes variable. Note indexing into the gridspec, gs1 uses 0 order indexing. Since the third subplot is plotted across all columns, we use the semicolon.

Python