5

I produced two matplotlib Figures, at size of 1000x1000. Each of the figures is 4x4 subplots based figure. I want one figure at size of 1000x2000 (width is 2000).

fig1
<Figure size 1000x1000 with 4 Axes>

fig2
<Figure size 1000x1000 with 4 Axes>

Now I want to combine them together.

I've searched many references: How to make two plots side-by-side using Python?

Plotting two figures side by side

Adding figures to subplots in Matplotlib

They are not relevant because mostly they suggest to change the way the initial plots were created. I don't want to change it - I want to use the Figure as is.

I just need to place Fig1 to the left of Fig2. Not changing the way Fig1 or Fig2 were created.

I also tried using PIL method: https://note.nkmk.me/en/python-pillow-concat-images/ However it was lower quality

jonb
  • 845
  • 1
  • 13
  • 36

2 Answers2

6

You can render your figures to arrays using the agg backend. Then concat the arrays side by side and switch back to your normal backend to show the result:

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

backend = mpl.get_backend()
mpl.use('agg')

dpi = 100
fig1,_ = plt.subplots(2,2, figsize=(1000/dpi, 1000/dpi), dpi=dpi)
fig1.suptitle('Figure 1')
fig2,_ = plt.subplots(2,2, figsize=(1000/dpi, 1000/dpi), dpi=dpi)
fig2.suptitle('Figure 2')

c1 = fig1.canvas
c2 = fig2.canvas

c1.draw()
c2.draw()

a1 = np.array(c1.buffer_rgba())
a2 = np.array(c2.buffer_rgba())
a = np.hstack((a1,a2))

mpl.use(backend)
fig,ax = plt.subplots(figsize=(2000/dpi, 1000/dpi), dpi=dpi)
fig.subplots_adjust(0, 0, 1, 1)
ax.set_axis_off()
ax.matshow(a)

enter image description here

Stef
  • 28,728
  • 2
  • 24
  • 52
  • It reduces the quality of the images. Thanks anyway. – jonb Nov 26 '21 at 22:55
  • It doesn't reduce the quality of the images itself (as you take both rendered images and just concat their data), but showing the combined image in a matshow or imshow may reduce the quality of the *displayed* result if you need to shrink the combined image to make if fit to your screen (depending on figsize and interpolation settings). I attached the result to the answer and there's no quality reduction (of course you need to view the image at 100 % zoom level) – Stef Nov 27 '21 at 12:04
  • Can plots added this way be resized relative to one another? What if I want them to share the available width at a 2:1 ratio, for example? – Mandias Jul 15 '22 at 05:45
  • @Mandias you could make `fig1` `1333/dpi` wide and `fig2` `667/dpi`, so that the resulting figure keeps the same size but the ratio would be 2:1 – Stef Jul 15 '22 at 06:06
2

Not directly merging two seperate figures, but I succeeded achieving the final goal by using this reference: https://matplotlib.org/devdocs/gallery/subplots_axes_and_figures/subfigures.html

That's the code I needed:

fig = plt.figure(constrained_layout=True, figsize=(20, 11))
titles_size = 25
labels_size = 18
subfigs = fig.subfigures(1, 2, wspace=0.02)
subfigs[0].suptitle('Title 1', fontsize=titles_size)
subfigs[1].suptitle('Title 2', fontsize=titles_size)

axsLeft = subfigs[0].subplots(2, 2)
axsRight = subfigs[1].subplots(2, 2)

for ax_idx, ax in enumerate(axsLeft.reshape(-1)):
    ax.grid(False)
    ax.set_xticklabels([])
    ax.set_yticklabels([])
    ax.axes.xaxis.set_visible(False)
    ax.axes.yaxis.set_visible(False)
for ax_idx, ax in enumerate(axsRight.reshape(-1)):
    ax.grid(False)
    ax.set_xticklabels([])
    ax.set_yticklabels([])
    ax.axes.xaxis.set_visible(False)
    ax.axes.yaxis.set_visible(False)
plt.show()

result

jonb
  • 845
  • 1
  • 13
  • 36