0

I'm using subplot2grid to define a grid of plots as shown below. Works great, it's a good functionality.

    plot_axes_1 = plt.subplot2grid((6, 4), (0, 0), rowspan=2, colspan=3)  ##1
    plot_axes_2 = plt.subplot2grid((6, 4), (2, 0), rowspan=2, colspan=3, sharex=scatter_axes_1)  ##2
    
    x_hist_axes_2 = plt.subplot2grid((6, 4), (4, 0), colspan=3, sharex=scatter_axes_2) ##3
    
    y_hist_axes_1 = plt.subplot2grid((6, 4), (0, 3), rowspan=2, sharey=scatter_axes_1)  ##4
    y_hist_axes_2 = plt.subplot2grid((6, 4), (2, 3), rowspan=2, sharey=scatter_axes_2, sharex= y_hist_axes_1)  ##5

grid

Now I want to consider the 5 plots from the image as a unit, and plot 6 copies of it, arranged on 3 rows and 2 columns.

    fig, ax= plt.subplots(3,2)

    for l in range(3):
        for m in range(2):

            ax[l,m].subplot2grid((6, 4), (0, 0), rowspan=2, colspan=3)  ##1
            ax[l,m].subplot2grid((6, 4), (2, 0), rowspan=2, colspan=3, sharex=scatter_axes_1)  ##2

            ax[l,m].subplot2grid((6, 4), (4, 0), colspan=3, sharex=scatter_axes_2) ##3

            ax[l,m].subplot2grid((6, 4), (0, 3), rowspan=2, sharey=scatter_axes_1)  ##4
            ax[l,m].subplot2grid((6, 4), (2, 3), rowspan=2, sharey=scatter_axes_2, sharex= y_hist_axes_1)  ##5


But I can't use subplot2grid like this, I get the error 'AxesSubplot' object has no attribute 'subplot2grid'

Is there another function I can use with AxesSubplot to do that?

3
  • Just to clarify, by plotting six copies of the above figure, you mean that you need a total of 30 subplots (effectively 4 columns and 9 rows)? Commented Apr 14, 2021 at 7:42
  • Right, in total 30 subplots, 9 rows 4 columns Commented Apr 14, 2021 at 14:22
  • Great. I added an answer assuming that. Commented Apr 14, 2021 at 14:23

2 Answers 2

2

I'm a little confused by what you are trying to do. However, a perhaps an alternate way to deal with different widths and heights is to use width ratios?

EDIT: use subfigure to keep logical groups of axes.


import matplotlib.pyplot as plt

fig = plt.figure(constrained_layout=True, figsize=(8, 12))
sfigs = fig.subfigures(3, 2)
for nn, sf in enumerate(sfigs.flat):
    sf.suptitle(nn)
    axs = sf.subplots(3, 2, gridspec_kw={'width_ratios': [2, 1],
                                         'height_ratios': [2, 2, 1]})
    sf.delaxes(axs[2, 1])
plt.show()

enter image description here

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks. I clarified better what I needed, which was 6 times this. But thank you for this approach. It is simpler than my way for plotting the main 5 axes at least.
I would use subfigure(3,2) and then populate as above.
Wow, didn't know about subfigures. Seems amazing! This is matplotlib >= 3.4, right?
yes subfigures is brand new, as is subplot_mosaic.
1

I think this is a job for matplotlib's sematic figure composition function, i.e., the subplot_mosaic function. This is available in matplotlib > 3.3. You will need to define a basic layout for your 5 panels, and then generate a full layout depending on how many rows/columns you want. As far as I can see, this will be quite convoluted and hard (although not impossible!) to create by subplot2grid or Gridspec or any of the other approaches.

import matplotlib.pyplot as plt
import numpy as np

def layout(panel, rows=3, cols=2, empty_sentinal=999):
    """Takes in a single layout and arranges it in multiple
       rows and columns"""
    
    npanels = rows * cols
    panel[panel >= empty_sentinal] = empty_sentinal
    minipanels = len(np.unique(panel))

    panels = np.array([i * (minipanels) + panel for i in range(npanels)])
        
    panel_rows = [np.hstack(panels[i : i + cols]) for i in range(0, npanels, cols)]
    panel_cols = np.vstack(panel_rows)
    
    panel_cols[panel_cols > empty_sentinal] = empty_sentinal
    
    return panel_cols

A) Generating a single panel:

single_panel = np.array([
    [1, 1, 1, 1, 1, 1, 2, 2, 999], 
    [1, 1, 1, 1, 1, 1, 2, 2, 999], 
    [1, 1, 1, 1, 1, 1, 2, 2, 999], 
    [1, 1, 1, 1, 1, 1, 2, 2, 999], 
    [3, 3, 3, 3, 3, 3, 4, 4, 999], 
    [3, 3, 3, 3, 3, 3, 4, 4, 999], 
    [3, 3, 3, 3, 3, 3, 4, 4, 999], 
    [3, 3, 3, 3, 3, 3, 4, 4, 999], 
    [5, 5, 5, 5, 5, 5, 999, 999, 999],
    [5, 5, 5, 5, 5, 5, 999, 999, 999],
    [5, 5, 5, 5, 5, 5, 999, 999, 999],
    [999] * 9,
    [999] * 9,
])

fig, ax = plt.subplot_mosaic(single_panel, figsize=(10, 10), empty_sentinel=999)
for k, v in ax.items():
    v.set_xticklabels([])
    v.set_yticklabels([])
    v.text(0.5, 0.5, k, ha="center", va="center", fontsize=25)

plt.show()

Single Layout

(B) "Tiling" the above single panel

my_layout = layout(panel=single_panel, rows=3, cols=2)
    
fig, ax = plt.subplot_mosaic(my_layout, figsize=(10, 10), empty_sentinel=999)
for k, v in ax.items():
    v.set_xticklabels([])
    v.set_yticklabels([])
    v.text(0.5, 0.5, k, ha="center", va="center", fontsize=25)

plt.show()

Tiled Single Panel

Some Notes:

  1. The empty_sentinal is set to 999. If you have more than 999 subplots, increase that to a higher number.
  2. Each "mini-panel" can be individually acessed. You might need to write other functions to access "panel-group"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.