Sympy:生成具有多个子图的图形

4

我正在使用sympy和matplotlib,并希望生成一个带有多个图的图形,类似于在numpy中使用pylab.subplot的方式。这应该很简单,或者我以为是这样...

让我惊讶的是,我没有找到一种简单的方法来做到这一点。要么(a)在多个点上评估SymPy表达式并获得一个可以与matplotlib一起使用的numpy数组,要么(b)在sympy.plotting中使用类似于pylab.subplot的机制。

示例代码:

import sympy.plotting.plot as symplot
import sympy as sym
x = sym.Symbol('x')
# This opens two different figures...
symplot(x*x, (x, -10, 10))
symplot(x, (x, -10, 10))

有什么想法吗?

1
这个问题有点太抽象了。加上一些具体的例子可能会有所帮助... - mgilson
你想要子图还是两条线叠加在同一张图上?我假设你想要第一个。对于第二个,只需将表达式分组即可。 - Krastanov
我想要的是你理解的matplotlib风格。 - Uri Cohen
3个回答

6
这取决于您使用的SymPy版本。
在最新版本(0.7.2)中,您已经拥有了一个绘图模块,可以保证能够绘制任何内容,并且可以使用matplotlib作为后端。
在旧版本中,您可以使用“lambdify”选项,它是一个hackish、大多数情况下不可用的辅助函数,返回一个快速的数值函数,可与numpy一起使用。但是,对于非平凡表达式,它会出现错误。
下面我将解释如何在0.7.2中使用绘图模块:
  1. 只需像这样调用plotp = plot(expression, (var, start, stop))。如果您有matplotlib,它将直接使用它。
  2. 如果您想要一些花哨的东西,请提取matplotlib图形:f = p._backend.fig
  3. 不再关心SymPy,您的其余工作都在matplotlib中。您可以做任何您想做的事情。
SymPy绘图模块的理念是能够评估任何可能的表达式,而不是重新实现类似于matplotlib的绘图库。因此,只需使用sympy.plotting.plot进行评估,并在matplotlib中进行花式子图变换。
使用sympy绘图模块还有其他优点:检测间断点和自适应采样,根据函数着色,评估病态复杂的符号表达式(尽管速度较慢)。
显然,检查文档。虽然它们不是很好,但许多问题都在那里得到了回答:http://docs.sympy.org/0.7.2/modules/plotting.html 还要检查sympy示例文件夹中的笔记本。
编辑以回答一些额外的问题:
  1. There is no notion of subplots in the SymPy's plotting module, and hopefully there will never be one. As I mentioned above, SymPy is not trying to reimplement modules like matplotlib, rather it is trying to give the tools necessary for good easy use within another module (interfaces between modules are better than big project with many small submodules).

  2. In order to create one figure with two subplots in matplotlib from two different sympy plots do (this is an ugly hack, as matplotlib does not support merges of figures):

    sympy_p1 = sympy.plot(foo)
    sympy_p2 = sympy.plot(bar)
    matplotlib_fig = plt.figure()
    sp1 = matplotlib_fig.add_subplot(121)
    sp2 = matplotlib_fig.add_subplot(122)
    sp1.add_collection(sympy_p1._backend.ax.get_children()[appropriate_index])
    sp2.add_collection(sympy_p2._backend.ax.get_children()[appropriate_index])
    matplotlib_fig.show()
    
  3. In order to update sympy plot (not creation of a subplot, just adding a new expression) use sympy_p1.append(sympy_p2). This will result in sympy_p1 containing the plots of both foo and bar (NOT two subplots, rather one plot with both expressions).

  4. You might want to use sympy.plot(..., show=False) in some cases.


我想知道的是,文档中没有提到如何使 sympy 重复使用一个图形而不是打开一个新的。在您的建议中,我该如何在 matplotlib 的两个不同子图中使用 sympy 生成两个图形?谢谢! - Uri Cohen
@UriCohen,我认为我已经在上面的编辑中回答了你的问题。我还没有测试过代码,但它应该可以工作-如果有问题,请警告我,我会修复它。 - Krastanov
我认为最新版本的sympy在Plot对象中没有_backend,只有backend,它是一个DefaultBackend实例,没有ax成员... - Uri Cohen
_backend еұһжҖ§жҳҜеңЁ show жҲ– save ж—¶еҲӣе»әзҡ„гҖӮе®ғеӯҳеңЁдё”дёҺ backend дёҚеҗҢгҖӮ - Krastanov
我已经修复了代码。似乎matplotlib不允许合并图形。我怀疑在sympy的一侧实现子图可能不会被执行,因为这只会重复很多matplotlib功能。在一个完美的世界中,将会向matplotlib添加图形合并功能。 - Krastanov

2
第二部分的答案可能是使用PlotGrid类:
>>> p1 = plot(x*x, (x, -10, 10), show=False)
>>> p2 = plot(x, (x, -10, 10), show=False)   
>>> PlotGrid(2, 1 , p1, p2)     # grid size or subplot size: 2x1
PlotGrid object containing:
Plot[0]:Plot object containing:
[0]: cartesian line: x*x for x over (x, -10, 10)
Plot[1]:Plot object containing:
[0]: cartesian line: x for x over (x, -10, 10)

这个功能不是最新版本的一部分,但可能会在下一个SymPy版本中推出。您可以在当前的SymPy开发版本中找到它。

1

对于您的第一个问题,您可以使用lambdify()将表达式转换为函数:

import numpy as np
from sympy import *

x, y = symbols("x, y")
eq = sqrt(x**2 + y**2)

xa = np.random.rand(10)
ya = np.random.rand(10)
f = lambdify((x, y),eq,'numpy')

print f(xa, ya)
print np.sqrt(xa**2 + ya**2)

1
lambdify 在任何非平凡输入上都会失败。对于复杂的内容,您可以使用 expression.evalf(subs=arg) 循环处理值数组,但这是在Python中进行的,并且精度高于机器,因此速度较慢。 - Krastanov
这有点傻,因为我不需要lambda;只需要在某些图中评估函数...虽然不是最好的选择,但我会尝试一下。 - Uri Cohen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接