How do I use the `_backend` attribute of a Sympy `plot`

℡╲_俬逩灬. 提交于 2021-01-28 05:12:21

问题


In the following example I use Sympy to make a plot:

from sympy import symbols, plot
x = symbols('x')
p = plot(x*x, x**3, (x, -2, 2), show=False)
for n, line in enumerate(p, 2): 
    line.label='$x^{%d}$'%n
    line.line_color = ['k', 'r'][n-2]
p.legend = True

As you can see, the legend is placed over the lines and Sympy doesn't offer a direct way to change the position of the legend.

After some research I found, directly in the source code of */sympy/plotting/plot.py, this comment:

Especially if you need publication ready graphs and this module is not enough for you - just get the _backend attribute and add whatever you want directly to it. In the case of matplotlib (the common way to graph data in python) just copy _backend.fig which is the figure and _backend.ax which is the axis and work on them as you would on any other matplotlib object.

Hence I tried

be = p._backend 

but what I've got back is an:

AttributeError: 'Plot' object has no attribute '_backend'

What should I do to move the legend or to otherwise tweak the plot using this ._backend attribute?


▶      U  P  D  A  T  E      ◀

After another trip to the source code I realized that the ._backend attribute is instantiated only after the plot is committed to the screen, as in p.show().

With this new knowledge, always in the interactive interpreter, I tried

...
p.show()
p._backend.ax.legend(loc='4') # bottom right

and the plot was updated with the legend location in the "correct" place.

Have I solved my problem? I'm afraid I've not, because this works in an IPython session, when you have issued the IPython's magic %matplotlib (that enables to interact with a live plot) and, afaict, only under these conditions.

In particular. the following code, executed as a script,

from sympy import symbols, plot 
x = symbols('x')
p = plot(x*x, x**3, (x, -2, 2), show=False)
for n, line in enumerate(p, 2): 
    line.label='$x^{%d}$'%n
    line.line_color = ['k', 'r'][n-2]
p.legend = True
p.show()
p._backend.ax.legend(loc=4) # bottom-right
p.save('plot_with_legend_OK_maybe.png')

saves the plot with the legend in the top-right corner, over the plotted lines.

So here it is the updated version of my

Q U E S T I O N

Is it possible to change the plot, using its .backend attribute, and have the changes persisted in a saved image file?


回答1:


Quick answer:

Use p._backend.fig.savefig('plot_with_legend_OK.png') instead of p.save('..').

This uses the savefig command from matplotlib as far as I know (if you want to know other options you can use).


The long(er) story of why it doesn't work. We can look at the code for self.save(path) which is called when you do p.save('plot_with_legend_OK_maybe.png').

    def save(self, path):
    if hasattr(self, '_backend'):
        self._backend.close()
    self._backend = self.backend(self)
    self._backend.save(path)

As you can see, if a _backend is established it will be closed, a new backend will be called and then it will save the figure using this backend. This will effectively undo all changes you did to the backend before then. That is why you cannot use the sympy show() and save() commands on plots that you have altered using the matplotlib backend.

The reason for this is simplicity, as given by the developers of sympy here:

Simplicity of code takes much greater importance than performance. Don't use it if you care at all about performance. A new backend instance is initialized every time you call show() and the old one is left to the garbage collector.

which also goes for save().



来源:https://stackoverflow.com/questions/43869393/how-do-i-use-the-backend-attribute-of-a-sympy-plot

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!