问题
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