Captions for matshow()s in multiple-page pdf

216 Views Asked by At

I have been around this problem for quite a long time but I'm not able to find an answer.

So, I have a list with matrices which I want to plot (for the sake of this question I'm just having 2 random matrices:

list = [np.random.random((500, 500)), np.random.random((500, 500))]

I then want to plot each element of the list using matshow in a separate page of a pdf file:

with PdfPages('file.pdf') as pdf:
    plt.rc('figure', figsize=(3,3), dpi=40)
    for elem in list:
        plt.matshow(elem, fignum=1)
        plt.title("title")
        plt.colorbar()
        plt.text(0,640,"Caption")
        pdf.savefig()  # saves the current figure into a pdf page
        plt.close()

The result is the following: enter image description here

My problem is with the caption. You can see I put "Caption" in the edge of the document on purpose. This is because sometimes the actual captions I want to insert are too big to fit in one single pdf page.

So, how can I make each pdf page adjustable to the caption's content (that might vary in each page)? For example, would it be possible to set each page size to A4 or A3, and then plot/write everything in each page?

I've already tried setting up plt.figure(figsize=(X, X)) with a variable X size, but it just changes the resolution of the pdf I guess.

2

There are 2 best solutions below

1
On BEST ANSWER

You may want to use the bbox_inches="tight" option when saving the file. This will adapt the figure size to its content. So it then suffices to place some text at position (0,0) in figure coordinates and align it to the top. This will then extent towards the bottom and outside the figure (so the figure when shown on screen would not contain that text), but with the bbox_inches="tight" option of savefig, the saved figure will become large enough to contain that text.
The use of the textwrap package will then also allow to limit the text in horizontal direction.

import numpy as np; np.random.seed(1)
import textwrap 
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages

p = np.ones(12); p[0] = 7
text2 = "".join(np.random.choice(list(" abcdefghijk"),p=p/p.sum(), size=1000))
text2 = textwrap.fill(text2, width=80)
texts = ["Caption: Example", "Caption 2: " + text2 ]

lis = [np.random.random((500, 500)), np.random.random((500, 500))]

with PdfPages('file.pdf') as pdf:
    for elem,text in zip(lis,texts):
        fig = plt.figure(dpi=100)

        grid_size = (3,1)

        plt.imshow(elem)
        plt.title("title")
        plt.colorbar()

        fig.text(0,0, text, va="top")

        plt.tight_layout()
        pdf.savefig(bbox_inches="tight")
        plt.close()

enter image description here

0
On

I think I have come up with an answer to this question myself, which solves the problem of having enough space for my text:

However, a perfect answer would be making each page's size dynamic, according to the amount of caption I put.

Anyway, my answer is the following (I essentially divided each page in a grid with 3 rows, making the upper 2 rows for the plots, and the last just for the caption) :

with PdfPages('file.pdf') as pdf:
    for elem in list:
        fig = plt.figure(figsize=(8.27, 11.69), dpi=100)

        grid_size = (3,1)

        plt.subplot2grid(grid_size, (0, 0), rowspan=2, colspan=1)

        plt.imshow(elem)
        plt.title("title")
        plt.colorbar()

        plt.subplot2grid(grid_size, (2, 0), rowspan=2, colspan=1)
        plt.axis('off')
        plt.text(0,1,"Caption")

        plt.tight_layout()
        pdf.savefig()
        plt.close()

Which produces the following in each page: enter image description here

Could someone find a better solution? :)