1

I am trying to create a corner plot for an upcoming paper, but I'm running into difficulty. I am creating an N x N array of subplots (currently, N = 6) and then deleting a bit over half of them. The issue is that the figure doesn't seem to resize itself after I delete the extraneous subplots, so when I later add a legend using a dummy subplot, it exists in the area where a full row and column of deleted subplots were, thus enlarging the figure. I've been working on this for several hours now and haven't found a solution. Here is the MWE:

import matplotlib.pyplot as plt
%matplotlib notebook

n_char = 8

# Set up the main figure.
fig, ax = plt.subplots(n_char, n_char, figsize=(n_char, n_char))

# Get rid of the axis labels unless it's on the left-most column or bottom-most row.
for i in range(0, n_char):

    # For each row, loop over each column.
    for j in range(0, n_char):

        # If the plot isn't in the bottom-most row, get rid of the x-axis tick labels.
        if i != n_char - 1:
            ax[i, j].set_xticklabels([])

        # If the plot isn't in the left-most column, get rid of the y-axis tick labels.
        if j != 0:
            ax[i, j].set_yticklabels([])

# Remove the plots that are repetitive or boring (plotting against the same characteristic).
for i in range(0, n_char):

    # For each row, loop over each column.
    for j in range(0, n_char):

        # Delete the offending axes.
        if j >= i:
            ax[i, j].remove()

# Set the spacing between the plots to a much smaller value.
fig.subplots_adjust(hspace=0.00, wspace=0.00)

# Create a big plot for the legend.  Have the frame hidden.
fig.add_subplot(111, frameon=False, xticks=[], yticks=[], xticklabels=[], yticklabels=[])

# Create some dummy data to serve as the source of the legend.
plt.scatter([10], [10], color="k", s=5, zorder=2, label="Targets")

# Set the x-axis limits such that the dummy data point is invisible.
fig.gca().set_xlim(-1, 1)

# Add the legend to the plot.  Have it located in the upper right.
plt.legend(scatterpoints=1, loc="upper right", fontsize=5)

# Save the final plot.
fig.savefig("./../Code Output/Other Plots/Corner_Plot_Test.png", bbox_inches="tight", dpi=500)

I have looked at many different questions here on Stack Overflow. The two most promising candidates was this one, but I found the solution wasn't quite workable due to the large number of plots (and, to be frank, I didn't fully understand the solution). I thought that the first answer in this one might also work, as I thought it was a sizing issue (i.e. the figure wasn't resizing, so creating a new subplot was creating one the size of the original figure), but all it did was resize the entire figure, so that didn't work either.

To help, I will also include an image. I took the output of the code above and edited it to show what I want:

Image showing what I want the output to be

I should add that if I don't add a subplot, the output is as I expected (i.e. it's the proper size), so the issue comes in when adding the subplot, i.e. the line fig.add_subplot(111, frameon=False, xticks=[], yticks=[], xticklabels=[], yticklabels=[]).

mknote
  • 157
  • 1
  • 3
  • 12
  • Do you need to call subplots (and all other matplotlib stuff) before you decide the ones that will the excluded or retained? – fdireito Jul 03 '20 at 15:13
  • I'm not sure if this was the direction you were going with your comment, but I have a solution that is working as I want now. I basically just didn't add the dummy row and column at all and edited how the data was plotted. With that row and column never existing, the plot is now the size I desire and the legend is in the proper spot. – mknote Jul 03 '20 at 16:55
  • Yes, I was thinking in that. Anyway, good that you had you problem solved! – fdireito Jul 03 '20 at 17:51

1 Answers1

1

The use of GridSpec may help. GridSpec is used to specify array of axes to plot. You can set widths for columns and heights for rows as ratios in the option. The unneeded row should have very small height ratio, while unneeded column very small width ratio. Here is the runnable code and output plot:-

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
#import numpy as np

fig = plt.figure(figsize=(8, 8))
nn = 6

# will create gridspec of 6 rows, 6 columns
# 1st row will occupy v small heights
# last column will occupy v small widths

sm = 0.01            # the v small width/height
wh = (1.-sm)/(nn-1.) # useful width/height

gs = gridspec.GridSpec(nn, nn, width_ratios=[*[wh]*(nn-1), sm], \
                      height_ratios= [sm, *[wh]*(nn-1)])

cols, rows = nn, nn
ax = [[0 for i in range(cols)] for j in range(rows)] 

for ea in range(nn):
    for eb in range(nn):
        ax[ea][eb] = fig.add_subplot(gs[ea, eb])
        ax[ea][eb].set_xticklabels([])
        ax[ea][eb].set_yticklabels([])
        if eb>=ea:
            ax[ea][eb].remove()

# plot data on some axes
# note that axes on the first row (index=0) are gone
ax[2][0].plot([2,5,3,7])
ax[4][2].plot([2,3,7])

# make legend in upper-right axes (GridSpec's first row, last column)
# first index: 0
# second index: nn-1
rx, cx = 0, nn-1
ax[rx][cx] = fig.add_subplot(gs[rx,cx])
hdl = ax[rx][cx].scatter([10], [10], color="k", s=5, zorder=2, label="Targets")
ax[rx][cx].set_axis_off()
#ax[rx][cx].set_visible(True)  # already True
ax[rx][cx].set_xticklabels([])
ax[rx][cx].set_yticklabels([])

# plot legend
plt.legend(bbox_to_anchor=(1.0, 1.0), loc='upper right', borderaxespad=0.)

fig.subplots_adjust(hspace=0.00, wspace=0.00)

plt.show

output plot

swatchai
  • 17,400
  • 3
  • 39
  • 58
  • The code you provided does not run. When I run it, an invalid syntax error is throw with regards to the line `gs = gridspec.GridSpec(nn, nn, width_ratios=[*[wh]*(nn-1), sm], \ height_ratios= [sm, *[wh]*(nn-1)])`, specifically with the first * in `[*[wh]*(nn-1), sm]`. Removing those two *'s and running results in this error: unsupported operand type(s) for +: 'float' and 'list' – mknote Jul 03 '20 at 16:33
  • @mknote I use Python 3.7, and matplotlib version 3.1.1. Are you still on Python 2.7? – swatchai Jul 03 '20 at 16:39
  • Try changing `*[wh]*(nn-1)` to 0.198, 0.198, 0.198, 0.198, 0.198 and `*[wh]*(nn-1)` to 0.198, 0.198, 0.198, 0.198, 0.198 – swatchai Jul 03 '20 at 16:43
  • I am in fact still on 2.7, and the correction you provided allows the code to run. The output isn't exactly the same as yours, but it's very similar and does provide a solution to the problem. As it happens, I worked out another solution in the meantime that I think I'll end up using (I outlined it in a comment to the original question), but since this does fix the problem I'll accept this as the solution. – mknote Jul 03 '20 at 17:20