4

I am trying to do the following:

I have created a figure, using matplotlib, with several subplots. More specifically, 2x4 subplots

The output is great for showing it on the screen, but not for saving it to pdf.

If I just use save_fig, it prints a single page pdf document, with the 2x4 grid.

What I would like to do, is re-arrange my subplots, to let's say a 2x4 grid (choosing which subplot goes where, would be good, but not necessary) and printing it to a 2-page pdf with 4 subplots each. (in order to be able to fit it to A4 page size)

Is this possible?

Thank you in advanced!

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
stavrop
  • 465
  • 2
  • 8
  • 20
  • 1
    I think you will have to split the figure 'manually' into two and save them into the same pdf file. – Thomas Kühn Jun 21 '17 at 09:17
  • 2
    you can save multiple-page pdf's with `PdfPages`: https://matplotlib.org/examples/pylab_examples/multipage_pdf.html but you will probably need to create two figure objects to do this – tmdavison Jun 21 '17 at 09:19
  • I have seen the multiple-page sample code, but i would like to keep the current layout for viewing, and create a new layout for saving. I can't seem to find a way to get the subplots from the original figure, and re-arrange them to a new one.... – stavrop Jun 21 '17 at 09:23
  • 2
    Rearanging the subplots will of course destroy the original layout. So it's really questionable why going through all this rearanging would be better than just create different plots (1 for showing, 2 for saving) with the same data. – ImportanceOfBeingErnest Jun 21 '17 at 09:50

2 Answers2

3

As I needed something similar for my work, I put some effort into automating the process of grouping plots into figures depending on the display medium. At first I had the idea to do each plot only once and just add the subplots to the figures to be saved in the pdf, but sadly, according to a comment in this answer, this is not possible, so everything needs to be re-plotted. The code shows the general idea of how this can be automated using PdfPages:

from matplotlib import pyplot as plt
import numpy as np
from matplotlib.backends.backend_pdf import PdfPages


def niter(iterable, n):
    """
    Function that returns an n-element iterator, i.e.
    sub-lists of a list that are max. n elements long.
    """
    pos = 0
    while pos < len(iterable):
        yield iterable[pos:pos+n]
        pos += n


def plot_funcs(x, functions, funcnames, max_col, max_row):
    """
    Function that plots all given functions over the given x-range,
    max_col*max_row at a time, creating all needed figures while doing
    so.
    """

    ##amount of functions  to put in one plot    
    N = max_col*max_row

    ##created figures go here
    figs = []

    ##plotted-on axes go here
    used_axes = []

    ##looping through functions N at a time:
    for funcs, names in zip(niter(functions, N), niter(funcnames,N)):

        ##figure and subplots
        fig, axes = plt.subplots(max_col, max_row)

        ##plotting functions
        for name,func,ax in zip(names, funcs, axes.reshape(-1)):
            ax.plot(x, func(x))
            ax.set_title(name)
            used_axes.append(ax)

        ##removing empty axes:
        for ax in axes.reshape(-1):
            if ax not in used_axes:
                ax.remove()

        fig.tight_layout()
        figs.append(fig)

    return figs

##some functions to display
functions = [
    lambda x: x, lambda x: 1-x, lambda x: x*x, lambda x: 1/x, #4
    np.exp, np.sqrt, np.log, np.sin, np.cos,                  #5
    ]
funcnames = ['x','1-x', 'x$^2$', '1/x', 'exp', 'sqrt', 'log', 'sin','cos']

##layout for display on the screen
disp_max_col = 3
disp_max_row = 2

##layout for pdf
pdf_max_col = 2
pdf_max_row = 4

##displaying on the screen:
x = np.linspace(0,1,100)
figs = plot_funcs(x, functions, funcnames, disp_max_row, disp_max_col)
plt.show()


##saving to pdf if user wants to:
answer = input('Do you want to save the figures to pdf?')
if answer in ('y', 'Y', 'yes', ''):

    ##change number of subplots
    N = disp_max_col*disp_max_row
    figs = plot_funcs(x, functions, funcnames, pdf_max_row, pdf_max_col)

    ##from https://matplotlib.org/examples/pylab_examples/multipage_pdf.html
    with PdfPages('multipage_pdf.pdf') as pdf:
        for fig in figs:
            plt.figure(fig.number)
            pdf.savefig()

The core function, plot_funcs takes max_col and max_row keywords and then creates figures with the according amount of subplots. It then loops through a given list of functions to be plotted, each on its own subplot. Unused subplots are removed. Finally a list of all figures is returned.

In my example, I have 9 different functions, which I first show on the screen in a 2x3 layout (making a total of two figures, one with 6 subplots and one with 3 subplots). If the user is happy, the plots are redone in a 2x4 layout (again two figures, but this time one with 8 subplots and 1 with 1 subplot) and then saved to a file called multipage_pdf.pdf, following the example in the documentation.

Tested on python 3.5

Thomas Kühn
  • 9,412
  • 3
  • 47
  • 63
1

I would suggest to create 3 figures. One for showing and 2 for saving and plot the same data to them.

import matplotlib.pyplot as plt
import numpy as np


data = np.sort(np.cumsum(np.random.rand(24,16), axis=0), axis=0)

def plot(ax, x, y, **kwargs):
    ax.plot(x,y, **kwargs)

colors = ["crimson", "indigo", "limegreen", "gold"]
markers = ["o", "", "s", ""]
lines = ["", "-", "", ":"]

# figure 0 for showing
fig0, axes = plt.subplots(nrows=2,ncols=4)

for i, ax in enumerate(axes.flatten()):
    plot(ax, data[:,2*i], data[:,2*i+1], marker=markers[i%4], ls=lines[i%4],color=colors[i%4])


# figure 1 for saving
fig1, axes = plt.subplots(nrows=1,ncols=4)
for i, ax in enumerate(axes.flatten()):
    plot(ax, data[:,2*i], data[:,2*i+1], marker=markers[i], ls=lines[i],color=colors[i])

#figure 2 for saving
fig2, axes = plt.subplots(nrows=1,ncols=4)
for i, ax in enumerate(axes.flatten()):
    plot(ax, data[:,2*i+4], data[:,2*i+1+4], marker=markers[i], ls=lines[i],color=colors[i])

#save figures 1 and 2
fig1.savefig(__file__+"1.pdf")
fig2.savefig(__file__+"2.pdf")

#close figures 1 and 2
plt.close(fig1)
plt.close(fig2)
#only show figure 0
plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712