【问题标题】:python saving multiple subplot figures to pdfpython将多个子图图形保存为pdf
【发布时间】:2016-12-20 16:15:23
【问题描述】:

我是 python 新手,我正在尝试使用 matplotlib 和子图的 PdfPages 将大量数据保存到带有数字的 pdf 中。问题是我发现了一个我不知道如何解决的blottleneck,代码如下:

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

with PdfPages('myfigures.pdf') as pdf:
for i in range(1000):
    f,axarr = plt.subplots(2, 3)
    plt.subplots(2, 3)
    axarr[0, 0].plot(x1, y1)
    axarr[1, 0].plot(x2, y2)

    pdf.savefig(f)
    plt.close('all')

在每个循环中创建一个图形非常耗时,但如果我将它放在循环之外,它不会清除每个图。我尝试过的其他选项(如 clear() 或 clf() 也不起作用或最终创建了多个不同的数字,任何人都知道如何以不同的方式将其放入以使其运行得更快?

【问题讨论】:

    标签: python pdf matplotlib


    【解决方案1】:

    多页 PDF 附加 w/ matplotlib

    创建 ?-rows × ?-cols 子图矩阵 axes 每个 pdf page 的矩阵并保存(追加)每个页面的子图矩阵变得完全完整→然后创建新页面,重复,???。

    要在单个 pdf 中包含大量子图作为多页输出,请立即开始用您的图填充第一页,然后您需要在检测到迭代中添加的最新子图后创建一个新页面的情节生成已最大化当前页面的 ?-rows × ?-cols 子图数组布局 [即 ? × ? 子图矩阵] 中的可用空间(如果适用)。

    这是一种方法,可以轻松更改控制每页子图数量的尺寸(? × ?):

        import sys
    
        import matplotlib
        from matplotlib.backends.backend_pdf import PdfPages
        import matplotlib.pyplot as plt
        import numpy as np
    
    
        matplotlib.rcParams.update({"font.size": 6})
    
        # Dimensions for any m-rows × n-cols array of subplots / pg.
        m, n = 4, 5
    
        # Don't forget to indent after the with statement
        with PdfPages("auto_subplotting.pdf") as pdf:
    
            """Before beginning the iteration through all the data,
            initialize the layout for the plots and create a
            representation of the subplots that can be easily
            iterated over for knowing when to create the next page
            (and also for custom settings like partial axes labels)"""
            f, axarr = plt.subplots(m, n, sharex="col", sharey="row")
            arr_ij = [(x, y) for x, y in np.ndindex(axarr.shape)]
            subplots = [axarr[index] for index in arr_ij]
    
            # To conserve needed plotting real estate,
            # only label the bottom row and leftmost subplots
            # as determined automatically using m and n
            splot_index = 0
            for s, splot in enumerate(subplots):
                splot.set_ylim(0, 0.15)
                splot.set_xlim(0, 50)
                last_row = m * n - s < n + 1
                first_in_row = s % n == 0
                if last_row:
                    splot.set_xlabel("X-axis label")
                if first_in_row:
                    splot.set_ylabel("Y-axis label")
    
            # Iterate through each sample in the data
            for sample in range(33):
    
                # As a stand-in for real data, let's just make numpy take 100 random draws
                # from a poisson distribution centered around say ~25 and then display
                # the outcome as a histogram
                scaled_y = np.random.randint(20, 30)
                random_data = np.random.poisson(scaled_y, 100)
                subplots[splot_index].hist(
                    random_data,
                    bins=12,
                    normed=True,
                    fc=(0, 0, 0, 0),
                    lw=0.75,
                    ec="b",
                )
    
                # Keep collecting subplots (into the mpl-created array; 
                # see: [1]) through the samples in the data and increment
                # a counter each time. The page will be full once the count is equal
                # to the product of the user-set dimensions (i.e. m * n)
                splot_index += 1
    
                """Once an mxn number of subplots have been collected 
                you now have a full page's worth, and it's time to 
                close and save to pdf that page and re-initialize for a
                new page possibly. We can basically repeat the same 
                exact code block used for the first layout 
                initialization, but with the addition of 3 new lines:
                 +2 for creating & saving the just-finished pdf page,
                 +1 more to reset the subplot index (back to zero)"""
                if splot_index == m * n:
                    pdf.savefig()
                    plt.close(f)
                    f, axarr = plt.subplots(m, n, sharex="col", sharey="row")
                    arr_ij = [(x, y) for x, y in np.ndindex(axarr.shape)]
                    subplots = [axarr[index] for index in arr_ij]
                    splot_index = 0
                    for s, splot in enumerate(subplots):
                        splot.set_ylim(0, 0.15)
                        splot.set_xlim(0, 50)
                        last_row = (m * n) - s < n + 1
                        first_in_row = s % n == 0
                        if last_row:
                            splot.set_xlabel("X-axis label")
                        if first_in_row:
                            splot.set_ylabel("Y-axis label")
    
            # Done!
            # But don't forget to save to pdf after the last page    
            pdf.savefig()
            plt.close(f)
    

    对于任何 m×n 布局,只需分别更改 mn 值的声明。从上面的代码(其中“m, n = 4, 5”)中,生成了一个 4x5 的子图矩阵,总共 33 个样本作为两页 pdf 输出文件:

    参考文献

    1. Link to matplotlib subplots official docs.

    注意: 在多页 PDF 的最后一页上,将有许多空白子图,这些子图的数量等于您选择的子图 ? × ? 布局尺寸数和要绘制的样本/数据总数的乘积的余数。例如,说 m=3 和 n=4,因此您得到 3 行 4 个子图,每行等于每页 12 个,如果您有 20 个样本,那么将自动创建一个两页的 pdf,总共24 个子图,第二页上的最后 4 个子图(在这个假设的例子中最底部的行是完整的)是空的。


    使用seaborn

    有关上述实现的更高级(以及更多“pythonic*)扩展,请参见下文:

    应该可以通过创建new_page 函数来简化多页处理;最好不要逐字重复代码*,特别是如果您开始自定义绘图,在这种情况下,您将不希望镜像每个更改并键入两次相同的内容。基于seaborn 并利用可用的matplotlib 参数(如下所示),更可取的定制美学也可能更可取。

    添加new_page 函数和子图样式的一些自定义:

        import matplotlib.pyplot as plt
        import numpy as np
        import random
        import seaborn as sns
    
        from matplotlib.backends.backend_pdf import PdfPages
    
        # this erases labels for any blank plots on the last page
        sns.set(font_scale=0.0)
        m, n = 4, 6
        datasize = 37 
        # 37 % (m*n) = 13, (m*n) - 13 = 24 - 13 = 11. Thus 11 blank subplots on final page
        
        # custom colors scheme / palette
        ctheme = [
            "k", "gray", "magenta", "fuchsia", "#be03fd", "#1e488f",
            (0.44313725490196076, 0.44313725490196076, 0.88627450980392153), "#75bbfd",
            "teal", "lime", "g", (0.6666674, 0.6666663, 0.29078014184397138), "y",
            "#f1da7a", "tan", "orange", "maroon", "r", ] # pick whatever colors you wish
        colors = sns.blend_palette(ctheme, datasize)
        fz = 7  # labels fontsize
    
    
        def new_page(m, n):
            global splot_index
            splot_index = 0
            fig, axarr = plt.subplots(m, n, sharey="row")
            plt.subplots_adjust(hspace=0.5, wspace=0.15)
            arr_ij = [(x, y) for x, y in np.ndindex(axarr.shape)]
            subplots = [axarr[index] for index in arr_ij]
            for s, splot in enumerate(subplots):
                splot.grid(
                    b=True,
                    which="major",
                    color="gray",
                    linestyle="-",
                    alpha=0.25,
                    zorder=1,
                    lw=0.5,
                )
                splot.set_ylim(0, 0.15)
                splot.set_xlim(0, 50)
                last_row = m * n - s < n + 1
                first_in_row = s % n == 0
                if last_row:
                    splot.set_xlabel("X-axis label", labelpad=8, fontsize=fz)
                if first_in_row:
                    splot.set_ylabel("Y-axis label", labelpad=8, fontsize=fz)
            return (fig, subplots)
    
    
        with PdfPages("auto_subplotting_colors.pdf") as pdf:
    
            fig, subplots = new_page(m, n)
    
            for sample in xrange(datasize):
                splot = subplots[splot_index]
                splot_index += 1
                scaled_y = np.random.randint(20, 30)
                random_data = np.random.poisson(scaled_y, 100)
                splot.hist(
                    random_data,
                    bins=12,
                    normed=True,
                    zorder=2,
                    alpha=0.99,
                    fc="white",
                    lw=0.75,
                    ec=colors.pop(),
                )
                splot.set_title("Sample {}".format(sample + 1), fontsize=fz)
                # tick fontsize & spacing
                splot.xaxis.set_tick_params(pad=4, labelsize=6)
                splot.yaxis.set_tick_params(pad=4, labelsize=6)
    
                # make new page:
                if splot_index == m * n:
                    pdf.savefig()
                    plt.close(fig)
                    fig, subplots = new_page(m, n)
    
            if splot_index > 0:
                pdf.savefig()
                plt.close(f)
    
    

    【讨论】:

    • 哇,有点矫枉过正,但非常感谢你,它工作得很好,请问代码是否来自某种网页?很有意思
    • 没有问题 - 只是想彻底,我实际上一直在工作中使用它,所以它对我来说也是一个很好的复习。 thx 我想出了用于分析 dna 测序结果的代码,该代码需要某种非典型的自定义格式,并且我希望以更定制/不太明显的默认 matplotlib 样式呈现可呈现的数字。
    猜你喜欢
    • 2013-07-21
    • 1970-01-01
    • 1970-01-01
    • 2020-09-26
    • 2018-09-28
    • 1970-01-01
    • 2023-03-29
    • 2022-01-01
    相关资源
    最近更新 更多