如何在sympy绘图后端库中获得svg字符串?



我使用sympy绘图后端库直接从sympy表达式创建绘图。我之所以选择这个库,是因为与标准的sympy绘图模块相比,它提供了更多的绘图微调选项。作为后端选择库,我使用matplotlib。

我的目标是得到结果图作为svg字符串,以便在网页中插入它以后。我需要用程序来做。我使用下面的代码:

from spb import plot, MB
import io
from sympy import symbols, sin, cos
x = symbols("x")
# create plot
p1 = plot(
(sin(x), "a", dict(color="k", linestyle=":")),
(cos(x), "b"),
backend=MB, show=False)
# buffer:
f = io.StringIO()
# save plot in buffer as svg string:
p1._fig.savefig(f, format = "svg")
# return result as svg string to insert it in web page later:
return f.getvalue()

问题是我得到一个异常:

'NoneType' object has no attribute 'savefig'
p1._fig.savefig(f, format = "svg")

但是如果我稍微修改一下代码:

...
# buffer:
f = io.StringIO()
# show plot:
p1.show()
# save plot in buffer as svg string:
p1._fig.savefig(f, format = "svg")
...

一切正常。但问题是,我不想显示的情节,我需要将其保存为svg字符串。有人知道怎么解决这个问题吗?

我就是这个模块的开发者。

这个错误是由于当你用MatplotlibBackendshow=False创建一个图时,没有创建这个图(太长了,无法解释为什么);这个行为是MatplotlibBackend特有的,其他后端不应该受到影响。因此,p1.fig=None

然而,绘图函数暴露了save方法,它只不过是一个特定绘图库的包装器"save"功能。如果查看源代码,您将看到MatplotlibBackend.save调用matplotlib的savefig,但它首先检查图形是否已经创建。如果没有,则强制创建。

所以,你所要做的就是:

p1.save(f, format = "svg")

最后一点。如果可能,不要使用以_(下划线)开头的属性或方法。它们表示私有属性,并且名称可能因版本而异。如果您确实需要检索matplotlib图,请使用p1.fig

编辑回答评论中关于性能的问题:

出于向后兼容性的原因,新模块默认使用自适应算法来绘制线形图,这与SymPy上使用的算法不同。一方面,它可以很容易地应用于更广泛的应用程序,另一方面,它比较慢。

您有两个选择:无论哪种方式,您都可能想要更改模块的配置文件。

选项1:自适应算法将某些损失函数(loss_fn)最小化,并在达到阈值(adaptive_goal)时停止。我们可以增加这个阈值(默认设置为0.01),从而提高性能,但牺牲线条的平滑质量。

from spb.defaults import cfg, set_defaults
# it requires a few tries to find an appropriate value
cfg["adaptive"]["goal"] = 0.02
set_defaults(cfg)
# restart the kernel to load the new configuration

选项2:不要使用自适应算法,切换到使用Numpy和矢量化的均匀网格算法(通常使用adaptive=False完成,并可能设置适当数量的离散点n=something)!与自适应算法相比,这非常非常快。

想想看:一般来说,我们的情节相对于屏幕尺寸来说是相对较小的。每行1000点(或任何你决定使用的数字)应该会创建足够光滑的线条。

因此,您可以在逐图的基础上停用自适应算法(使用adaptive=False),或者您可以将模块设置为始终使用均匀网格算法(这是我在机器上使用的设置)。

from spb.defaults import cfg, set_defaults
# disable adaptive algorithm
cfg["adaptive"]["used_by_default"] = False
set_defaults(cfg)
# restart the kernel to load the new configuration

然后,当你创建一个情节,你觉得它应该更平滑,只需通过设置n=something(默认值为1000)增加离散点的数量。

您可以在此文档页面找到更多自定义选项。

最新更新