Какой лучший способ сделать отчет в PDF с более чем 100 графиками с помощью Python? - PullRequest
0 голосов
/ 08 сентября 2018

Мне нужно иметь отчет в PDF с большим количеством графиков. Большинство из них будут созданы с помощью matplotlib в цикле, но мне также нужно будет включать графики панд и фреймы данных (весь вид) и графики морского побережья. Прямо сейчас я исследовал следующие решения:

  • PythonTex. Я уже использовал его для других проектов, но это заняло бы много времени, потому что вы должны писать \ pythontexprint для каждого графика, который вы хотите отобразить.
  • Используйте команду savefig на каждой итерации цикла и сохраняйте все графики как изображения для последующей вставки всех в латекс. Это был бы очень трудоемкий выбор тоже. Другой вариант - с помощью этой команды сохранить графики в формате PDF, а затем объединить все файлы PDF. Это создало бы уродливый отчет, поскольку графики не умещаются на всю страницу.
  • Используйте RStudio с сеткой для создания отчета по уценке. Проблема здесь в том, что мне нужно было бы изучить функциональность сети и тратить время.
  • Насколько я знаю, PyPDF не соответствует моим потребностям.
  • Создайте блокнот Jupyter и попробуйте экспортировать его в PDF. Еще раз, я не знаю, как использовать ноутбук Jupyter, и я прочитал, что мне придется сначала конвертировать в HTML, а затем в PDF.
  • Решения здесь: Создание отчетов с помощью Python: PDF или HTML в PDF Однако этот вопрос задан три года назад, и в настоящее время он может быть лучше.

Таким образом, мой вопрос заключается в следующем: есть ли простой и быстрый способ получить все эти графики (если они соответствуют коду, который генерирует их еще лучше) в PDF с приличным аспектом?

1 Ответ

0 голосов
/ 12 сентября 2018

Я бы порекомендовал использовать matplotlibs savefig в BytesIO буфере (или сохранять буферы в список или подобную структуру данных для 100). Затем вы можете использовать эти буферы изображений, чтобы вставить изображение в PDF-файл, используя библиотеку, такую ​​как reportlab (веб-сайт здесь и документы здесь ). Я регулярно использую этот подход для создания документов PowerPoint с использованием библиотеки python-pptx, но также проверяю это в PDF с reportlab. reportlab библиотека очень мощная и немного «низкого уровня», так что может быть небольшая кривая обучения, но она, безусловно, отвечает вашим потребностям. Здесь есть простое руководство по началу работы здесь . reportlab - это лицензия BSD, доступная на pip и conda.

В любом случае мой фрагмент кода выглядит следующим образом.
Извините, это немного долго, но мой код имеет некоторые вспомогательные функции для печати текста и фиктивных изображений. Вы должны быть в состоянии скопировать / вставить его напрямую.

Код даст PDF, который будет выглядеть следующим образом enter image description here

import io

from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import inch

import numpy as np
import matplotlib.pyplot as plt


def plot_hist():
    """ Create a sample histogram plot and return a bytesio buffer with plot

    Returns
    -------
    BytesIO : in memory buffer with plot image, can be passed to reportlab or elsewhere
    """    
    # from https://matplotlib.org/gallery/lines_bars_and_markers/scatter_masked.html#sphx-glr-gallery-lines-bars-and-markers-scatter-masked-py
    plt.figure(figsize=(7, 2.25))

    N = 100
    r0 = 0.6
    x = 0.9 * np.random.rand(N)
    y = 0.9 * np.random.rand(N)
    area = (20 * np.random.rand(N))**2  # 0 to 10 point radii
    c = np.sqrt(area)
    r = np.sqrt(x * x + y * y)
    area1 = np.ma.masked_where(r < r0, area)
    area2 = np.ma.masked_where(r >= r0, area)
    plt.scatter(x, y, s=area1, marker='^', c=c)
    plt.scatter(x, y, s=area2, marker='o', c=c)
    # Show the boundary between the regions:
    theta = np.arange(0, np.pi / 2, 0.01)
    plt.plot(r0 * np.cos(theta), r0 * np.sin(theta))

    # create buffer and save image to buffer
    # dpi should match the dpi of your PDF, I think 300 is typical otherwise it won't pretty well
    buf = io.BytesIO()
    plt.savefig(buf, format='png', dpi=300)
    buf.seek(0)
    # you'll want to close the figure once its saved to buffer
    plt.close()

    return buf

def add_text(text, style="Normal", fontsize=12):
    """ Adds text with some spacing around it to  PDF report 

    Parameters
    ----------
    text : str
        The string to print to PDF

    style : str
        The reportlab style

    fontsize : int
        The fontsize for the text
    """
    Story.append(Spacer(1, 12))
    ptext = "<font size={}>{}</font>".format(fontsize, text)
    Story.append(Paragraph(ptext, styles[style]))
    Story.append(Spacer(1, 12))

# Use basic styles and the SimpleDocTemplate to get started with reportlab
styles=getSampleStyleSheet()
doc = SimpleDocTemplate("form_letter.pdf",pagesize=letter,
                        rightMargin=inch/2,leftMargin=inch/2,
                        topMargin=72,bottomMargin=18)

# The "story" just holds "instructions" on how to build the PDF
Story=[]

add_text("My Report", style="Heading1", fontsize=24)

# See plot_hist for information on how to get BytesIO object of matplotlib plot
# This code uses reportlab Image function to add and valid PIL input to the report
image_buffer1 = plot_hist()
im = Image(image_buffer1, 7*inch, 2.25*inch)
Story.append(im)

add_text("This text explains something about the chart.")

image_buffer2 = plot_hist()
im = Image(image_buffer2, 7*inch, 2.25*inch)
Story.append(im)

add_text("This text explains something else about another chart chart.")

# This command will actually build the PDF
doc.build(Story)

# should close open buffers, can use a "with" statement in python to do this for you
# if that works better
image_buffer1.close()
image_buffer2.close()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...