将来自 %matplotlib 笔记本后端的输出作为 SVG 包含在 ipynb 中

Posted

技术标签:

【中文标题】将来自 %matplotlib 笔记本后端的输出作为 SVG 包含在 ipynb 中【英文标题】:Include output from %matplotlib notebook backend as SVG in ipynb 【发布时间】:2018-01-10 00:57:18 【问题描述】:

This 几年前的回答展示了如何让 jupyter notebook 将图形创建为 svg。解决方案是告诉 InlineBackend 使用svg 作为输出。

import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format = 'svg'
plt.plot(...)

这将导致笔记本内以及生成的 ipynb 文件中的所有图像都是 svg 格式;该文件将有一行像

"data":   "image/svg+xml": [  "<?xml  .....

在里面。

现在的问题是,如果使用%matplotlib notebook 后端,这将不起作用。 %config InlineBackend 不会更改笔记本后端的任何内容,因此输出文件包含 PNG 图像

"data":  "text/html": [  "<img src=\"data:image/png;base64,iVBORw0....

所以问题是:如何让 ipynb 文件包含使用 %matplotlib notebook 后端创建的静态版本的绘图作为 SVG 图像?

一个月前@mark jay 有一条小评论,他想做我现在想做的事,但没有回答或暗示该评论。

在我的代码中,我直接从数据框中绘制:

%matplotlib notebook
import pandas as pd
df = pd.read_sql(sql1, connection)
...
...
df.plot(subplots=True, kind='bar')

无需导入matplotlib.pyplot 即可完美运行,但也不能强制将图形创建为 svg。我想如果基本情况可行,我可以修改绘图代码,使其不涉及熊猫或数据框。

【问题讨论】:

如果你进入 jupyter notebook,直接从 pandas 数据框中绘制一些东西,保存并关闭它,然后在文本编辑器中打开 .ipynb 文件,你会发现你的图形已存储作为 .png,像素级数据。您可以将其存储为 svg,以便某些类型的图形占用更少的空间。 我花了一段时间才明白这个问题。因此,我编辑了这个问题,现在让它更清楚。请看一下并确定这是否是您要问的(请注意,问题与熊猫的使用无关)。如果您对我的编辑不满意,请单击我的编辑并使用回滚按钮。 是的,谢谢,添加了更多关于熊猫图的信息,但是从你的编辑可以看出你比我更了解这些图形格式,我只知道当它只是条形图时更紧凑几行。 我一直在尝试其他绘图库,并且 plotly 似乎为交互式绘图提供了最佳结果。散景也不错。 plot.ly/python 感谢您的提示,我读到 plotly 是开源的,没想到它是,也可以看到它在仪表板中。 【参考方案1】:

据我了解 matplotlib 后端,nbagg(使用%matplotlib notebook 调用)使用无法渲染矢量图形的 Agg(反粒度几何)渲染。不幸的是,这是为 Jupyter 使用 interactive 内联后端的唯一开箱即用方式。

文档链接https://matplotlib.org/faq/usage_faq.html#what-is-interactive-mode 类似答案How to make matplotlibs nbagg backend generate SVGs?

如果您不需要交互性,请继续使用

import pandas as pd
from IPython.display import SVG, display
from numpy import ndarray

def svg_add(chart, size=(4,4), dpi=100):
    """Takes a chart, optional tuple of ints for size, int for dpi
    default is 4 by 4 inches with 100 dpi"""

    if type(chart) == ndarray:
        fig = chart[0].get_figure()
        fig.set_size_inches(size)
        fig.savefig("mybar.svg", dpi=dpi)
        display(SVG(filename='mybar.svg'))
    else:
        fig = chart.get_figure()
        fig.set_size_inches(size)
        fig.savefig("mybar.svg", dpi=dpi)
        display(SVG(filename='mybar.svg'))

然后

df = pd.DataFrame([[2,5]],columns=['a','b'])
bar_chart = df.plot(subplots=False, kind='bar')
svg_add(chart=bar_chart,size=(3,3),dpi=100)
#or
#svg_add(bar_chart,(3,3),100)

【讨论】:

问题是关于如何让ipynb文件包含svg。 %matplotlib notebook 后端在使用 savefig 时完全能够生成 svg;但是需要一种将 svg 包含到 ipynb 文件中的方法。 %matplotlib notebook 使用 Nbagg 后端。仅在笔记本中呈现时生成 PNG 文件。所以当它在 Notebook 中渲染为 PNG 然后保存 notebook 时,知道它只能渲染 PNG,你为什么期望它可以在 notebook 中保存为 SVG 渲染? Matplotlib 确实接受自定义后端,也许您可​​以编写自己的后端。现在的问题是可以做什么与您希望它做什么 我认为没有人希望笔记本后端将 svg 渲染到笔记本中。有人这么说吗?但是,既然可以生成 svg,那么肯定有办法将 svg 包含在 ipynb 文件中。这就是问题所要求的。我会考虑$ ipython nbconvert 和一些模板左右的方向。无论如何,我看不出这个答案如何有助于接近那个目标。 我明白你的意思,我很确定使用 html magic 或分隔笔记本的每个单元格的配置有一种 hacky 方式。就像在一个单元格上使用 %matplotlib notebook 和在另一个单元格上使用 %matplotlib inline 一样,或者只使用 html 并包括 svg 本身。但是这个问题的框架是你希望它发生在%matplotlib notebook 上。如果您只想显示 svg,那么我更新了我的答案以显示保存 svg 然后渲染到笔记本中 感谢@fcsr 是的,是的,我注意到保存的 .svg 为 132kB,并不像我想象的那么小。这与您的subplots=False 完美配合,尽管在我的用例subplots=True 中导致错误AttributeError: 'numpy.ndarray' object has no attribute 'get_figure' 我怀疑我的问题的真正答案是Pandas、Matplotlib 或Jupyter 开发人员尚未真正创建此功能(还)它只能通过黑客来真正实现。【参考方案2】:

因为显然即使在赏金期之后也没有人能够提供解决方案,所以可能有以下解决方法。

    使用%matplotlib notebook 创建您的笔记本。对结果感到满意后,保存它。

    使用它的副本并将%matplotlib notebook替换为

    %matplotlib inline
    %config InlineBackend.figure_format = 'svg'
    

    重新运行完整的笔记本。保存结果。

    在文本编辑器中打开生成的 ipynb 文件,并将前两行再次替换为 %matplotlib notebook

最终结果将是一个带有 svg 图像的 ipynb。但是一旦打开并运行,它将使用笔记本后端进行图形创建。

【讨论】:

感谢您设置赏金,还有 20 小时。已经系统地测试了这个为每个步骤创建一个新文件。在步骤 1、2 和 3 之后,我的 .ipynb 文件大小为 174kB、411kB 和 184kB。当我对文件内容进行文本搜索时,image/svg+xml 仅在第 2 步之后的文件中。按照this answer 添加了figsize=(10, 30) btw 以在第2 步之后设置大小。第3 步,文本编辑器编辑并重新运行把它变回png。该 figsize 命令确实取代了我对 matplotlib notebook 的鼠标调整大小功能的需求并存储 svg。

以上是关于将来自 %matplotlib 笔记本后端的输出作为 SVG 包含在 ipynb 中的主要内容,如果未能解决你的问题,请参考以下文章

Matplotlib笔记 · 禁止Matplotlib在Jupyter中输出文本信息

PySide 代替 PyQt4 作为 matplotlib Qt4Agg 后端的先决条件

带有 Qt5Agg 后端的 matplotlib 返回空的刻度标签

如何在 ipython 笔记本中将 matplotlib 图作为 html 抓取?

来自python27的cx-freeze exe

结合 Jupyter 丰富的显示和 matplotlib 图表