Есть несколько сюжетов matplotlib. Необходимо сохранить их в единый pdf файл. Что делать?
Способ I. Сохранение одного сюжета на одной странице с помощью PdfPages.
Этот способ можно реализовать с помощью двух вариантов.
Использование магии matplotlib:
from matplotlib.backends.backend_pdf import PdfPagesimport matplotlib.pyplot as pltimport numpy as np# Создание файла.pdf = PdfPages("Figures.pdf")# Создание сюжетов и их сохранение.FUNCTIONS = [np.sin, np.cos, np.sqrt, lambda x: x**2]X = np.linspace(-5, 5, 100)for function in FUNCTIONS: plt.plot(X, function(X)) pdf.savefig() plt.close()# Сохранение файлаpdf.close()
Использование непосредственного доступа к фигурам:
from matplotlib.backends.backend_pdf import PdfPagesimport matplotlib.pyplot as pltimport numpy as np# Создание массива фигур.FUNCTIONS = [np.sin, np.cos, np.sqrt, lambda x: x**2]X = np.linspace(-5, 5, 100)figures = []for function in FUNCTIONS: figure = plt.figure() axes = figure.subplots() axes.plot(X, function(X)) figures.append(figure)# Массив фигур.# figures = []# Создание файла и сохранение каждой фигуры.pdf = PdfPages("Figures.pdf")for figure in figures: pdf.savefig(figure)# Сохранение файлаpdf.close()
Получим:
Конечный pdf файлСпособ II. Сохранение несколько сюжетов на одной странице с помощью PdfPages.
from matplotlib.backends.backend_pdf import PdfPagesimport matplotlib.pyplot as pltimport numpy as np# КонстантыFUNCTIONS = [np.sin, np.cos, np.sqrt, lambda x: x**2, np.tan]X = np.linspace(-5, 5, 100)# Количество строк и столбоц на одной строкеROWS = 2COLUMNS = 2# Создание файла.pdf = PdfPages("Figures.pdf")# Цикл по страницамindex = 0for page in range(len(FUNCTIONS)//(ROWS*COLUMNS)+1): # Создаем фигуру с несколькими осями. figure = plt.figure(figsize=(12, 12)) axes = figure.subplots(ROWS, COLUMNS) # Цикл по строкам и столбцам for row in range(ROWS): for column in range(COLUMNS): if index < len(FUNCTIONS): axes[row, column].plot(X, FUNCTIONS[index](X)) index += 1 # Сохраняем страницу pdf.savefig(figure)# Сохранение файлаpdf.close()
Получим:
Конечный pdf файлТак сделать не получится, если у нас есть массив уже конкретных построенных фигур. По той простой причине, на фигуре уже отрисовано то, что нужно, и так, как нужно. Да и к тому же, каждая фигура имеет свое значение dpi и size, которые влияют на отрисовку. Конечно, все это можно учесть, усложнив алгоритм, но намного проще перейти к 3 способу.
Способ III. Использование reportlab.
Наиболее универсальный способ.
import matplotlib.pyplot as pltimport numpy as npfrom io import BytesIOfrom reportlab.pdfgen import canvasfrom reportlab.lib.units import cmfrom reportlab.lib.utils import ImageReader# Создание массива фигур.FUNCTIONS = [np.sin, np.cos, np.sqrt, lambda x: x**2]X = np.linspace(-5, 5, 100)figures = []for function in FUNCTIONS: figure = plt.figure() axes = figure.subplots() axes.plot(X, function(X)) figures.append(figure)# Массив фигур.# figures = []# Отступindent = 1.5# Создаем canvas и устанавливаем текущее значение высотыc = canvas.Canvas("Figures.pdf")c.setTitle("Figures")height = indent# Цикл по фигурам.for figure in figures: # dpi и размер (в дюймах) графика dpi = figure.get_dpi() figureSize = figure.get_size_inches() # Создаем рамку вокруг графика. # Это не обязательно, но так удобнее вырезать распечатанный график ножницами. figure.patches.extend( [plt.Rectangle((0, 1/(dpi*figureSize[1])), width=1-2/(dpi*figureSize[0]), height=1-2/(dpi*figureSize[1]), transform=figure.transFigure, figure=figure, clip_on=False, edgecolor="black", facecolor="none", linewidth=1)]) # Рендер фигуры. image = BytesIO() figure.savefig(image, format="png") image.seek(0) image = ImageReader(image) # Размер фигуры в см. figureSize = figure.get_size_inches()*2.54 # A4 210297 мм # Если выходим за пределы листа, то добавляем новый лист if height + figureSize[1] + indent > 29.7: height = indent c.showPage() # Добавляем image в pdf c.drawImage(image, (10.5-figureSize[0]/2)*cm, height*cm, figureSize[0]*cm, figureSize[1]*cm) height += figureSize[1]# Сохраняем.c.save()
Получим:
Конечный pdf файлСпасибо за прочтение статьи. Удачи!