将来自 %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 返回空的刻度标签