I want to take 2 figures objects and save them together on one pdf page. This is my code that currently saves them on 2 separate pages:
if (output_pdf_name is not None):
pdf = matplotlib.backends.backend_pdf.PdfPages(output_pdf_name)
explained_seq_fetures_letters_list1 = explained_seq_fetures_letters1 if multiple_samples else [
explained_seq_fetures_letters1]
explained_seq_fetures_letters_list2 = explained_seq_fetures_letters2 if multiple_samples else [
explained_seq_fetures_letters2]
for explained_seq_fetures_letters_item1, explained_seq_fetures_letters_item2 in zip(explained_seq_fetures_letters_list1,explained_seq_fetures_letters_list2):
# create Logo object
explained_seq_fetures_letters_item = pd.DataFrame(explained_seq_fetures_letters_item1, columns=Logo_letters)
IG_logo1 = create_DNA_logo(PWM_df=explained_seq_fetures_letters_item,
secondary_color=secondary_color)
pdf.savefig(IG_logo1.ax.figure)
explained_seq_fetures_letters_item2 = explained_seq_fetures_letters_item2[explained_seq_fetures_letters_item2 != 0].reshape(-1, 1)
explained_seq_fetures_letters_item = pd.DataFrame(explained_seq_fetures_letters_item2, columns=Logo_symbol)
IG_logo2 = create_DNA_logo(PWM_df=explained_seq_fetures_letters_item,
secondary_color=True)
pdf.savefig(IG_logo2.ax.figure)
plt.close('all')
pdf.close()
IG_logo.ax.figure is figure object as you can see in the image:
It is created by logomaker library.
This is an example of my figure:
I tried to find a solution but it seems that there is no simple solution. Any ideas?
Thanks!
CodePudding user response:
disclaimer: I am the author of the library borb
(used in this answer)
borb
is a pure python library that makes it easy to work with a PDF document. It also has convenient wrappers to interact with other famous libraries (PIL
, matplotlib
, etc)
There is a huge examples repo where one of the sections deals with exactly your problem; inserting matplotlib
charts in a PDF. You can find that section here.
For completeness I'll copy/paste the code below. Keep in mind this example first builds the chart object, you obviously won't be needing that part:
from borb.pdf.document.document import Document
from borb.pdf.page.page import Page
from borb.pdf.canvas.layout.page_layout.multi_column_layout import SingleColumnLayout
from borb.pdf.canvas.layout.page_layout.page_layout import PageLayout
from borb.pdf.canvas.layout.text.paragraph import Paragraph
from borb.pdf.pdf import PDF
from borb.pdf.canvas.layout.image.chart import Chart
from decimal import Decimal
import matplotlib.pyplot as MatPlotLibPlot
import numpy as np
import pandas as pd
def create_plot() -> None:
# build DataFrame
df = pd.DataFrame(
{
"X": range(1, 101),
"Y": np.random.randn(100) * 15 range(1, 101),
"Z": (np.random.randn(100) * 15 range(1, 101)) * 2,
}
)
# plot
fig = MatPlotLibPlot.figure()
ax = fig.add_subplot(111, projection="3d")
ax.scatter(df["X"], df["Y"], df["Z"], c="skyblue", s=60)
ax.view_init(30, 185)
# return
return MatPlotLibPlot.gcf()
def main():
# create Document
doc: Document = Document()
# create Page
page: Page = Page()
# add Page to Document
doc.append_page(page)
# set a PageLayout
layout: PageLayout = SingleColumnLayout(page)
# add a Paragraph
layout.add(Chart(create_plot(), width=Decimal(256), height=Decimal(256)))
# store
with open("output.pdf", "wb") as pdf_file_handle:
PDF.dumps(pdf_file_handle, doc)
if __name__ == "__main__":
main()
Other things worth mentioning:
PageLayout
decides how yourPage
will be laid out. In this example I've opted forSingleColumnLayout
which does what you'd expect, content takes up the entire page (minus margins) and flows from top to bottom.- You can try other
PageLayout
implementations (such asMultiColumnLayout
) if you prefer more of a scientific journal kind of aesthetic. - You can nest the charts inside a
Table
object (another way to display them side by side). - You can add content as an attachment to a PDF. You could include your raw data (as csv) to the PDF, thus ensuring other people can easily build their own visualizations of the data in their preferred style.